Spring MV 3.2异常响应映射 [英] Spring MV 3.2 Exception Response Mapping
问题描述
Spring 3.2.15,基于MVC的REST API这里( not 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
- Spring MVC框架错误(例如
其中 MyAppResponse
对象是:
/ Groovy伪代码
@Canonical
class MyAppResponse {
String detail
String randomNumber
}
它出现像 ResponseEntityExceptionHandler
可能可以为我做这个,但我没有看到森林通过树木wrt它如何被传递参数。我希望我可以做一些像:
// Groovy-伪代码
@ControllerAdvice
MyAppExceptionMapper类扩展了ResponseEntityExceptionHandler {
ResponseEntity< Object> handleFizzBuzzException(FizzBuzzException fbEx,HttpHeaders头,HttpStatus状态){
// TODO:如何将状态重置为401?
status = ???
new ResponseEntity(fbEx.message,headers,status)
}
ResponseEntity< Object> handleFooBarException(FooBarException fbEx,HttpHeaders头,HttpStatus状态){
// TODO:如何将状态重置为403?
status = ???
new ResponseEntity(fbEx.message,headers,status)
}
ResponseEntity< Object> handleOmgException(OmgException omgEx,HttpHeaders头,HttpStatus状态){
// TODO:如何将状态重置为404?
status = ???
new ResponseEntity(omgEx.message,headers,status)
}
//现在将所有Spring生成的异常映射到422
ResponseEntity< Object> ; handleAllSpringExceptions(SpringException springEx,HttpHeaders头,HttpStatus状态){
// TODO:如何将状态重置为422?
status = ???
new ResponseEntity(springEx.message,headers,status)
}
//其他都是一个500 ...
ResponseEntity< Object> handleAllOtherExceptions(Exception ex,HttpHeaders headers,HttpStatus status){
// TODO:如何将状态重置为500?
status = ???
new ResponseEntity(糟糕,发生了什么事,Lol。,标题,状态)
}
}
任何想法我如何可以完全实现这个映射逻辑,以及实体要求是一个 MyAppResponse
实例,而不是只是一个字符串?
然后,使用 @ControllerAdvice
注释类,我唯一需要做的配置Spring使用它?
为了减少@ bond-java-bond答案,您不需要构建 ResponseEntity
自己:
- 使用
@ResponseStatus
对于每个handleSomeException
方法(例如@ResponseStatus(HttpStatus.UNAUTHORIZED)
) - 从这些方法返回自定义
MyAppResponse
但是,如果每种异常将以相同的方式处理(仅通过HTTP状态的差异)我建议减少 MyAppEx ceptionMapper
像这样:
@ControllerAdvice
public class MyAppExceptionMapper {
private最终地图< Class<?>,HttpStatus>地图;
{
map = new HashMap<>();
map.put(FizzBuzzException.class,HttpStatus.UNAUTHORIZED);
map.put(FooBarException.class,HttpStatus.FORBIDDEN);
map.put(NoSuchRequestHandlingMethodException.class,HttpStatus.UNPROCESSABLE_ENTITY);
/ *列出Spring特定的异常,如@ bond-java-bond建议* /
map.put(Exception.class,HttpStatus.INTERNAL_SERVER_ERROR);
}
//处理所有异常
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseEntity< MyAppResponse> handleException(异常异常){
MyAppResponse response = new MyAppResponse();
//填写响应细节
HttpStatus status = map.get(exception.getClass());
if(status == null){
status = map.get(Exception.class); //默认
}
返回新的ResponseEntity<> (回应,状态);
}
}
优点:
- 相当短暂。
- 没有代码重复。
- 稍微更有效。
- 易于扩展。
此外,您可以将映射配置移动到外部并注入。
如何配置MVC Dispatcher Servlet
首先,检查 mvc-dispatcher-servlet.xml
(或另一个 contextConfigLocation
从 web.xml
)包含:
< context:component-scan base-package =base.package/>
< mvc:注释驱动/>
其次,检查@ControllerAdvice注释类和@Controller注释类是否都属于<$ c的子包$ c> base.package 。
请参阅 Spring MVC中的异常处理或 Spring MVC @ExceptionHandler示例了解更多详细信息。
Spring 3.2.15, MVC-based REST API here (not Spring Boot, sadly!). I am trying to implement an exception mapper/handler that meets the following criteria:
- No matter what happens (success or error), the Spring app always returns a response entity of
MyAppResponse
(see below); and - In the event of processing a request successfully, return an HTTP status of 200 (typical); and
- In the event of processing a request and an exception occurs, I need to control the mapping of the specific exception to a particular HTTP status code
- Spring MVC framework errors (such as
BlahException
) must map to HTTP 422 - Custom app exceptions, such as my
FizzBuzzException
have their own status mapping schemes:FizzBuzzException
-> HTTP 401FooBarException
-> HTTP 403OmgException
-> HTTP 404
- All other exceptions, that is, non-Spring exceptions, and non-custom app exceptions (the 3 listed above), should produce an HTTP 500
- Spring MVC framework errors (such as
Where the MyAppResponse
object is:
// Groovy pseudo-code
@Canonical
class MyAppResponse {
String detail
String randomNumber
}
It appears like ResponseEntityExceptionHandler
might be able to do this for me, but I'm not seeing the forest through the trees w.r.t. how it gets passed arguments. I'm hoping I can do something like:
// 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)
}
}
Any idea how I can fully implement this mapping logic and the requirement for the entity to be a MyAppResponse
instance and not just a string?
Then, is annotating the class with @ControllerAdvice
the only thing that I need to do to configure Spring to use it?
To reduce @bond-java-bond answer you do not need to build ResponseEntity
by yourself:
- Use
@ResponseStatus
for eachhandleSomeException
method (e.g.@ResponseStatus(HttpStatus.UNAUTHORIZED)
) - Return custom
MyAppResponse
from those methods
But if each kind of exceptions will be processed by the same way (diffs by HTTP status only) I suggest to reduce MyAppExceptionMapper
like this:
@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);
}
}
Pros:
- Pretty short.
- No code duplication.
- Slightly more effective.
- Easy to extend.
Also, you can move mapping configuration outside and inject it.
How to configure MVC Dispatcher Servlet
First of all, check if mvc-dispatcher-servlet.xml
(or another contextConfigLocation
from web.xml
) contains:
<context:component-scan base-package="base.package"/>
<mvc:annotation-driven/>
Secondly, check if @ControllerAdvice annotated class and @Controller annotated class both belong to subpackage of base.package
.
See complete examples at Exception Handling in Spring MVC or Spring MVC @ExceptionHandler Example for more details.
这篇关于Spring MV 3.2异常响应映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!