Spring MV 3.2异常响应映射

时间:2016-12-16 19:10:15

标签: spring-mvc exception-handling httpresponse

Spring 3.2.15,这里基于MVC的REST API(遗憾的是,不是 Spring Boot!)。我正在尝试实现符合以下条件的异常映射器/处理程序:

  • 无论发生什么(成功或错误),Spring应用程序始终返回MyAppResponse的响应实体(见下文);和
  • 如果成功处理请求,则返回HTTP状态200(典型值);和
  • 如果处理请求并发生异常,我需要控制特定异常到特定HTTP状态代码的映射
    • Spring MVC框架错误(例如BlahException)必须映射到HTTP 422
    • 自定义应用例外,例如我的FizzBuzzException有自己的状态映射方案:
      • FizzBuzzException - > HTTP 401
      • FooBarException - > HTTP 403
      • OmgException - > HTTP 404
    • 所有其他异常,即非Spring异常和非自定义应用程序异常(上面列出的3)应生成HTTP 500

MyAppResponse对象的位置:

// Groovy pseudo-code
@Canonical
class MyAppResponse {
    String detail
    String randomNumber
}

出现就像ResponseEntityExceptionHandler也许可以为我做这个,但我没有看到森林穿过树木w.r.t.如何传递参数。我希望我可以这样做:

// Groovy-pseudo code
@ControllerAdvice
class MyAppExceptionMapper extends ResponseEntityExceptionHandler {
    ResponseEntity<Object> handleFizzBuzzException(FizzBuzzException fbEx, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 401?
        status = ???

        new ResponseEntity(fbEx.message, headers, status)
    }

    ResponseEntity<Object> handleFooBarException(FooBarException fbEx, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 403?
        status = ???

        new ResponseEntity(fbEx.message, headers, status)
    }

    ResponseEntity<Object> handleOmgException(OmgException omgEx, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 404?
        status = ???

        new ResponseEntity(omgEx.message, headers, status)
    }

    // Now map all Spring-generated exceptions to 422
    ResponseEntity<Object> handleAllSpringExceptions(SpringException springEx, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 422?
        status = ???

        new ResponseEntity(springEx.message, headers, status)
    }

    // Everything else is a 500...
    ResponseEntity<Object> handleAllOtherExceptions(Exception ex, HttpHeaders headers, HttpStatus status) {
        // TODO: How to reset status to 500?
        status = ???

        new ResponseEntity("Whoops, something happened. Lol.", headers, status)
    }
}

知道如何完全实现这个映射逻辑以及实体要求是MyAppResponse实例而不仅仅是字符串吗?

然后,使用@ControllerAdvice注释类是我需要做的唯一的事情来配置Spring来使用它吗?

2 个答案:

答案 0 :(得分:2)

要减少@ bond-java-bond答案,您不需要自己构建ResponseEntity

  1. 对每个@ResponseStatus方法使用handleSomeException(例如@ResponseStatus(HttpStatus.UNAUTHORIZED)
  2. 从这些方法中返回自定义MyAppResponse
  3. 但是如果每种异常都以相同的方式处理(仅通过HTTP状态进行差异),我建议像这样减少MyAppExceptionMapper

    @ControllerAdvice
    public class MyAppExceptionMapper {
        private final Map<Class<?>, HttpStatus> map;
        {
            map = new HashMap<>();
            map.put(FizzBuzzException.class, HttpStatus.UNAUTHORIZED);
            map.put(FooBarException.class, HttpStatus.FORBIDDEN);
            map.put(NoSuchRequestHandlingMethodException.class, HttpStatus.UNPROCESSABLE_ENTITY);
            /* List Spring specific exceptions here as @bond-java-bond suggested */
            map.put(Exception.class, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    
        // Handle all exceptions
        @ExceptionHandler(Exception.class)
        @ResponseBody
        public ResponseEntity<MyAppResponse> handleException(Exception exception) {
            MyAppResponse response = new MyAppResponse();
            // Fill response with details
    
            HttpStatus status = map.get(exception.getClass());
            if (status == null) {
                status = map.get(Exception.class);// By default
            }
    
            return new ResponseEntity<>(response, status);
        }
    }
    

    优点:

    1. 很短。
    2. 没有代码重复。
    3. 稍微有效一点。
    4. 易于扩展。
    5. 此外,您可以在外部移动映射配置并将其注入。

      如何配置MVC Dispatcher Servlet

      首先,检查mvc-dispatcher-servlet.xml(或contextConfigLocation中的其他web.xml)是否包含:

      <context:component-scan base-package="base.package"/>
      <mvc:annotation-driven/>
      

      其次,检查@ControllerAdvice注释类和@Controller注释类是否都属于base.package的子包。

      有关详细信息,请参阅Exception Handling in Spring MVCSpring MVC @ExceptionHandler Example处的完整示例。

答案 1 :(得分:1)

首先,错误/异常处理程序担心成功响应。

因此,成功响应的责任应该在于使用@RequestMapping注释的控制器( plain或REST控制器)方法,如下所示

@RequestMapping(value = "/demo", method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.OK)
public MyAppResponse doSomething() { .... }

为了映射具有异常的特定HTTP响应代码,只需编写如下@ControllerAdvice无需其他配置

@ControllerAdvice
public class CustomExceptionHandler {

    // Handle FizzBuzzException with status code as 401
    @ExceptionHandler(value = FizzBuzzException.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(FizzBuzzException ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.UNAUTHORIZED);
    }

    // Handle FooBarException with status code as 403
    @ExceptionHandler(value = FooBarException.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(FooBarException ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.FORBIDDEN);
    }

    // Handle OmgException with status code as 404
    @ExceptionHandler(value = OmgException.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(OmgException ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.NOT_FOUND);
    }

    // handle Spring MVC specific exceptions with status code 422
    @ExceptionHandler(value = {NoSuchRequestHandlingMethodException.class, HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class,
        HttpMediaTypeNotAcceptableException.class, MissingPathVariableException.class, MissingServletRequestParameterException.class, ServletRequestBindingException.class,
        ConversionNotSupportedException.class, TypeMismatchException.class, HttpMessageNotReadableException.class, HttpMessageNotWritableException.class, MethodArgumentNotValidException.class,
        MissingServletRequestPartException.class, BindException.class, NoHandlerFoundException.class, AsyncRequestTimeoutException.class})
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(Exception ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.UNPROCESSABLE_ENTITY);
    }

    // Handle rest of the exception(s) with status code as 500
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseEntity<MyAppResponse> handleException(Exception ex) {
        return new ResponseEntity<MyAppResponse>(buildResponse(ex), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    private MyAppResponse buildResponse(Throwable t) {
        MyAppResponse response = new MyAppResponse();
        // supply value to response object
        return response;
    }
}

如果需要任何进一步的信息,请在评论中说明。

P.S。:Spring MVC异常列表reference