DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Streamlining Event Data in Event-Driven Ansible
  • Dynamic Web Forms In React For Enterprise Platforms
  • Unlocking Oracle 23 AI's JSON Relational Duality
  • Loading XML into MongoDB

Trending

  • Cookies Revisited: A Networking Solution for Third-Party Cookies
  • Blue Skies Ahead: An AI Case Study on LLM Use for a Graph Theory Related Application
  • Artificial Intelligence, Real Consequences: Balancing Good vs Evil AI [Infographic]
  • Immutable Secrets Management: A Zero-Trust Approach to Sensitive Data in Containers
  1. DZone
  2. Coding
  3. Languages
  4. How To Validate JSON Request Body in Spring Boot

How To Validate JSON Request Body in Spring Boot

We will be looking at the request-validator library, which is able to compare the user input against a pre-defined set of rules like required, max, min, email, etc.

By 
Seun Matt user avatar
Seun Matt
DZone Core CORE ·
Oct. 30, 22 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
21.5K Views

Join the DZone community and get the full member experience.

Join For Free

We sometimes encounter server errors caused by a user providing input that's longer than the database column size or even a non-existent ENUM value. Do not trust user input is a popular cliche that, if implemented, will save a lot of time and resources down the line.

That is why, in this article, we will be looking at the request-validator library, which is able to compare the user input against a pre-defined set of rules and return errors if any.

Dependency Installation

In order for us to use request-validator, we need to add it to our project's pom.xml:

Listing 2.1 pom.xml

XML
 
<dependency>
	<groupId>com.smattme</groupId>
	<artifactId>request-validator</artifactId>
	<version>0.0.2</version>
</dependency>


The latest version of the dependency is available on Maven central. 

Validating JSON Request Body

Given that we have a simple login endpoint that requires a valid email and password and as a good Engineer we want to ensure that the user sends both fields and the email is a valid one.

We can easily achieve this with the request-validator library. For the email input field, we want the user to provide a non-null field and a valid email address while for the password field, we just want the user to provide a non-null value:

Listing 3.1 LoginController.java

Java
 
@RestController
public class LoginController {


    @PostMapping("/auth/login")
    public ResponseEntity<GenericResponse> login(@RequestBody LoginRequest request) {

        Map<String, String> rules = new HashMap<>();
        rules.put("email", "required|email");
        rules.put("password", "required");

        List<String> errors = RequestValidator.validate(request, rules);
        if (!errors.isEmpty()) {
            GenericResponse genericResponse = new GenericResponse();
            genericResponse.setStatus(false);
            genericResponse.setCode(HttpStatus.BAD_REQUEST.value());
            genericResponse.setErrors(errors);
            genericResponse.setMessage("Missing required parameter(s)");
            return ResponseEntity.badRequest().body(genericResponse);
        }

        //otherwise all is well, process the request
        //loginService.login()

        return ResponseEntity.ok(GenericResponse.generic200ResponseObj("Login successful"));

    }
}


From Listing 3.1 above, we used a Map<String, String> to store the rules for each expected request field. The key of the map is the name of the field as the API user should supply it, while the value contains the validation rules.

We then call the RequestValidator.validate() method to check the incoming request object against the defined rules. The method returns a List<String> that'll contain all the error messages if there are violations.

One big advantage of the library is that it returns a separate descriptive error message for each rule and checks ALL the rules in a single invocation.

Because RequestValidator.validate() expects Object data type, the request object can be a Map, a POJO or even a JSON String. 

If an API user provides an invalid request body, they'll get a 400 Bad Request with a detailed list of all the data infractions:

Listing 3.2 Curl Request/Response

JSON
 
Request:

curl --location --request POST 'http://localhost:8080/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "john"
}'

Response:
{
    "status": false,
    "message": "Missing required parameter(s)",
    "errors": [
        "password is required",
        "email supplied is invalid"
    ],
    "code": 400
}


However, as expected, a valid request body will return success:

Listing 3.3 Curl Request/Response

JSON
 
Request:

curl --location --request POST 'http://localhost:8080/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "john@example.com",
    "password": "changeit"
}'


Response:

{
    "status": true,
    "message": "Login successful",
    "code": 200
}


Note that the returned List<String> of errors can be used as part of your response in any way you and your team have chosen. It's not limited to the response format demonstrated in this article.

The complete source code is available at the end of this article and will help you better understand how the response format in this tutorial is formatted.

The request validator library allows us to combine one or more rules together using the pipe (|) character as a separator. From Listing 3.1 above, we combined the required and email rules together using the |. 

The only exception to this is when using the regex rule with other rules. It should be the last rule and should be separated by double pipe characters as in ||. This is to accommodate the potential presence of the pipe character in the regex pattern:

Listing 3.3 Regex Pattern:

Java
 
Map<String, String> rules = new HashMap<>();
rules.put("dob", "required||regex:[0-9]{2}-[0-9]{2}-[0-9]{4}");


The complete list of rules is available here.

Defining Custom Rules

Let's say we want to add a custom validation rule that's not in the library by default, we can easily achieve it by subclassing the RequestValidator class and implementing the RuleValidator interface. 

Given that we need to add a rule to ensure the user-provided value starts with custom_, first, we will need to create a PrefixRuleValidator class that'll implement the RuleValidator interface and perform the custom logic: 

Listing 4.1 PrefixRuleValidator.java 

Java
 
public class PrefixRuleValidator implements RuleValidator {
    private static final String CUSTOM_PREFIX = "custom_";
    @Override
    public ValidationResult isValid(Object value, Rule rule) {
        return value != null && String.class.isAssignableFrom(value.getClass()) &&
                value.toString().startsWith(CUSTOM_PREFIX)
                ? ValidationResult.success()
                : ValidationResult.failed(rule.getKey() + " should start with " + CUSTOM_PREFIX);
    }
}


The next component we need is a class that will extend RequestValidator. We will be calling this CustomRequestValidator, instead of the library's RequestValidator, to do our checks:

Listing 4.2 CustomRequestValidator.java 

Java
 
public class CustomRequestValidator extends RequestValidator {

    static {
        ruleValidatorMap.put("customprefix", PrefixRuleValidator.class);
    }

    public static List<String> validate(Object target, Map<String, String> rules) {
        String jsonRequest = convertObjectRequestToJsonString(target);
        return validate(jsonRequest, rules, ruleValidatorMap);
    }


}


The structure of CustomRequestValidator is very simple, we statically added the PrefixRuleValidator class to the parent's ruleValidatorMap. We then proceed to create a copy of the parent's validate() method, this will effectively make our rules to be available alongside other default rules.

Finally, let's use our custom rule in a controller:

Listing 4.3 CustomPrefixController.java 

Java
 
@RestController
public class CustomPrefixController {

    @PostMapping("/custom")
    public ResponseEntity<GenericResponse> formCustomPrefix(@RequestBody Map<String, Object> request) {

        Map<String, String> rules = Collections.singletonMap("objectType", "customprefix");

        List<String> errors = CustomRequestValidator.validate(request, rules);
        if(!errors.isEmpty()) {
            GenericResponse genericResponse = new GenericResponse();
            genericResponse.setStatus(false);
            genericResponse.setCode(HttpStatus.BAD_REQUEST.value());
            genericResponse.setErrors(errors);
            genericResponse.setMessage("Missing required parameter(s)");
            return ResponseEntity.badRequest().body(genericResponse);
        }

        return ResponseEntity.ok(GenericResponse.generic200ResponseObj("Operation successful"));
    }
}


Posting a valid request will return 200 OK:

Listing 4.4 Curl Request/Response

JSON
 
Request:

curl --location --request POST 'http://localhost:8080/custom' \
--header 'Content-Type: application/json' \
--data-raw '{
    "objectType": "custom_john"
}'

Response:

{
    "status": true,
    "message": "Operation successful",
    "code": 200
}


On the other hand, posting an invalid request will return the error message as coded in Listing 4.1 PrefixRuleValidator.java:

Listing 4.5 Curl Request/Response

JSON
 
Request:

curl --location --request POST 'http://localhost:8080/custom' \
--header 'Content-Type: application/json' \
--data-raw '{
    "objectType": "john"
}'

Response:

{
    "status": false,
    "message": "Missing required parameter(s)",
    "errors": [
        "objectType should start with custom_"
    ],
    "code": 400
}


Conclusion

In this article, we've seen how we can easily validate the JSON request body and ensure the API consumers are sending the data we expect alongside practical examples. The complete source code is available on GitHub. Happy coding.

JSON

Published at DZone with permission of Seun Matt. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Streamlining Event Data in Event-Driven Ansible
  • Dynamic Web Forms In React For Enterprise Platforms
  • Unlocking Oracle 23 AI's JSON Relational Duality
  • Loading XML into MongoDB

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!