首页 > 解决方案 > 为什么我在使用 spring 5 的 tomcat 9 中得到代码 400 响应而不是错误 json 对象?

问题描述

控制器:

@RestController
@RequestMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
@Validated
public class ApiController {


    @PostMapping(value = "/in",
            consumes = MediaType.APPLICATION_JSON_VALUE)
    ResponseEntity<InitResponse> inPost(
            @ApiParam(required = true) @Valid @RequestBody InRequest inRequest) {
        LOG.info("inPost request was received = {}", inRequest);
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }

异常处理程序:

@ControllerAdvice(assignableTypes = ApiController .class, annotations = RestController.class)
public class InExceptionHandler {
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<INErrors> handleConstraintViolation(ConstraintViolationException ex) {

        LOG.info("handleConstraintViolation was trigerred");
        INError INError = new INError(HttpStatus.BAD_REQUEST.toString(), ex.getLocalizedMessage());
        return new ResponseEntity<>(new INErrors(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<INErrors> handleMethodArgumentConstraintViolation(MethodArgumentNotValidException ex) {

        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();

        return new ResponseEntity<>(processFieldErrors(fieldErrors), HttpStatus.BAD_REQUEST);
    }
}

如果 InRequest 具有 javax 验证约束内的所有字段,那么我会得到正确的代码,但是当字段与验证不匹配时,我只会得到 400 响应代码。定义了其他异常处理程序,但我已将断点放置在任何地方,并且没有触发任何内容。

我还添加了 log4j 属性:

log4j.logger.org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod=DEBUG,stdout

但这在调试时没有产生任何额外的输出。我还希望将 INErrors 对象发回,但它甚至没有输入两种处理方法中的任何一种。

标签: javatomcat9spring5

解决方案


这是因为 Spring 的默认异常处理程序自己处理所有 WebMvc 的标准异常,然后将未处理的异常委托给用户定义的@ExceptionHandler方法。

在您的情况下,@Valid违反约束会引发MethodArgumentNotValidExceptionResponseEntityExceptionHandler#handleMethodArgumentNotValid. 因此,要更改此异常的默认行为,您需要在@ControllerAdivce.

@ControllerAdvice(assignableTypes = ApiController .class, annotations = RestController.class)
public class InExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();

        return ResponseEntity.badRequest().body(processFieldErrors(fieldErrors));
    }
}

编辑:我看到您同时使用assignableTypesannotations用于@ControllerAdvice异常处理程序。这使得 Spring 为所有@RestControllers 注册一个异常处理程序。尝试使用assignableTypesannotations

作为一个选项,您可以为不同的异常处理程序创建自定义注释。

以下代码在向“/two”提供无效数据时打印“one”,在/one向“/two”发送数据时打印“two”。

@RestController
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface One {}

@RestController
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Two {}

@One
class ControllerOne {

    @PostMapping("one")
    String a(@RequestBody @Valid Data data) {
        return data.value;
    }
}

@Two
class ControllerTwo {

    @PostMapping("two")
    String a(@RequestBody @Valid Data data) {
        return data.value;
    }
}

@ControllerAdvice(annotations = One.class)
class HandlerOne extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return ResponseEntity.badRequest().body("one");
    }
}

@ControllerAdvice(annotations = Two.class)
class HandlerTwo extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        return ResponseEntity.badRequest().body("two");
    }
}

推荐阅读