Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return 400 HTTP error code when some property of a RequestBody parameter is null?

I have the following example:

This is the request body:

public class UserLoginData
    implements Serializable {

    private static final long serialVersionUID = 1L;

    private String username;
    private String password;
    //... getter and setters
}

This is the Controller:

@RequestMapping(value = {"/login"}, method = RequestMethod.POST)
@ResponseBody
public LoginResponse login(@RequestBody(required = true) UserLoginData loginData){
    //... some code
 }

This is how I invoke the service:

POST /login
{"username":"neuquino"}

I expect that Spring returns a HTTP 400 BAD REQUEST error, because password is missing. But instead of that, it returns a HTTP 500 INTERNAL SERVER error with the following stacktrace:

java.lang.NullPointerException
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:948) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
    ...

How can I specify to Spring that username and password are required fields in request body?

like image 830
Neuquino Avatar asked Jan 16 '14 20:01

Neuquino


People also ask

Can a request body be null?

body. The read-only body property of the Request interface contains a ReadableStream with the body contents that have been added to the request. Note that a request using the GET or HEAD method cannot have a body and null is return in these cases.

When can I return a Bad Request?

The HyperText Transfer Protocol (HTTP) 400 Bad Request response status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (for example, malformed request syntax, invalid request message framing, or deceptive request routing).

What is the best HTTP response code for a post request with incorrect parameters?

400 Bad Request A 400 is the most commonly expected error response and indicates that a request failed due to providing bad input. Bad input can be a malformed request body, missing required parameters, wrongly typed or malformed parameters or a parameter that references another resource that does not exist.


2 Answers

@Bart's answer was very useful to find my final solution:

public class UserLoginData
    implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull
    @NotBlank
    private String username;

    @NotNull
    @NotBlank
    private String password;
    //... getter and setters
}

On my Controller I have:

public LoginResponse login(
        @RequestBody(required = true) @Valid UserLoginData loginData){
    //... login code
}

Until here is very similar, but it is clearer because the controller's method does not have the error validation. Instead of that, I used another class with the ControllerAdvice annotation

@ControllerAdvice
public class RestErrorHandler {

    private MessageSource messageSource;

    @Autowired
    public RestErrorHandler(@Qualifier("messageSource") MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ValidationError processValidationError(MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();

        return this.processFieldErrors(fieldErrors);
    }

    private ValidationError processFieldErrors(List<FieldError> fieldErrors) {
        ValidationError dto = new ValidationError();

        for (FieldError fieldError : fieldErrors) {
            String localizedErrorMessage = this.resolveLocalizedErrorMessage(fieldError);
            dto.getErrors().put(fieldError.getField(), localizedErrorMessage);
        }

        return dto;
    }

    private String resolveLocalizedErrorMessage(FieldError fieldError) {
        Locale currentLocale = LocaleContextHolder.getLocale();
        String localizedErrorMessage = this.messageSource.getMessage(fieldError, currentLocale);

        return localizedErrorMessage;
    }
}

Now my service is returning this:

{
  "errors":{
    "country":"country cannot be null"
  }
}

I hope it helps someone else.

To get this solution I also used what is written in this post.

like image 198
Neuquino Avatar answered Sep 24 '22 05:09

Neuquino


If the password is missing it will not be set when the UserLoginData object is created. It will not check if the value is valid or anything. If you need to validate your login data use proper validation.

You could use the annotations in the hibernate validator package for declarative validation e.g.

public class UserLoginData
    implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull
    @NotBlank
    private String username;

    @NotNull
    @NotBlank
    private String password;
    //... getter and setters
}

Your method could then be written as (note the @Valid annotation):

public LoginResponse login(
        @RequestBody(required = true) @Valid UserLoginData loginData, 
        BindingResult result, 
        HttpServletResponse response){

    if (result.hasErrors()) {
        // Validation problems!
        response.sendError(400, "Bad login data");
    }
}
like image 38
Bart Avatar answered Sep 24 '22 05:09

Bart