当RequestBody参数的某些属性为null时,如何返回400 HTTP错误代码?


问题内容

我有以下示例:

这是请求正文:

public class UserLoginData
    implements Serializable {

    private static final long serialVersionUID = 1L;

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

这是控制器:

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

这就是我调用服务的方式:

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

我希望Spring会返回HTTP 400 BAD REQUEST错误,因为密码丢失了。但是,相反,它返回带有以下堆栈跟踪的HTTP 500内部服务器错误:

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)
    ...

如何向Spring指定用户名和密码是请求正文中的必填字段?


问题答案:

@Bart的答案对于找到我的最终解决方案非常有用:

public class UserLoginData
    implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull
    @NotBlank
    private String username;

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

在我的控制器上,我有:

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

到这里为止非常相似,但是更清楚了,因为控制器的方法没有错误验证。取而代之的是,我使用了另一个带有ControllerAdvice注释的类

@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;
    }
}

现在我的服务返回此:

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

我希望它可以帮助其他人。

为了获得此解决方案,我还使用了本文中的内容