Spring MV 3.2 异常响应映射

sme*_*eeb 5 exception spring-mvc 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
}
Run Code Online (Sandbox Code Playgroud)

看起来也许ResponseEntityExceptionHandler为我做到这一点,但我并没有透过树木看到森林,因为它是如何传递参数的。我希望我能做这样的事情:

// 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

那么,我只需要对类进行注释@ControllerAdvice来配置 Spring 使用它吗?

Gre*_*y.K 4

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

  1. 用于@ResponseStatus每种handleSomeException方法(例如@ResponseStatus(HttpStatus.UNAUTHORIZED)
  2. MyAppResponse从这些方法返回自定义

但如果每种异常都以相同的方式处理(仅根据 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

优点:

  1. 很短。
  2. 没有代码重复。
  3. 效果稍微好一点。
  4. 易于扩展。

此外,您可以将映射配置移到外部并注入它。

如何配置 MVC 调度程序 Servlet

首先,检查mvc-dispatcher-servlet.xml(或contextConfigLocation来自 的另一个web.xml)是否包含:

<context:component-scan base-package="base.package"/>
<mvc:annotation-driven/>
Run Code Online (Sandbox Code Playgroud)

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

有关更多详细信息,请参阅Spring MVC 中的异常处理Spring MVC @ExceptionHandler 示例中的完整示例。