错误响应
REST 服务的一个常见需求是在错误响应的主体中包含详细信息。Spring 框架支持“HTTP API 的问题详情”规范,RFC 9457。
以下是对此支持的主要抽象:
-
ProblemDetail
— 表示 RFC 9457 问题详情的表示形式;一个简单的容器,用于存放规范中定义的标准字段和非标准字段。 -
ErrorResponse
— 用于公开 HTTP 错误响应详细信息的契约,包括 HTTP 状态、响应头和 RFC 9457 格式的主体;这允许异常封装并公开它们如何映射到 HTTP 响应的详细信息。所有 Spring MVC 异常都实现了这一点。 -
ErrorResponseException
— 基本的ErrorResponse
实现,其他人可以将其用作方便的基类。 -
ResponseEntityExceptionHandler
— 一个方便的基类,用于@ControllerAdvice,处理所有 Spring MVC 异常和任何ErrorResponseException
,并渲染带有主体的错误响应。
渲染
您可以从任何 @ExceptionHandler
或任何 @RequestMapping
方法返回 ProblemDetail
或 ErrorResponse
以呈现 RFC 9457 响应。处理过程如下:
-
ProblemDetail
的status
属性决定了 HTTP 状态。 -
如果
instance
属性尚未设置,ProblemDetail
的instance
属性将从当前 URL 路径中设置。 -
对于内容协商,Jackson 的
HttpMessageConverter
在渲染ProblemDetail
时优先选择 "application/problem+json" 而不是 "application/json",如果没有找到兼容的媒体类型,也会回退到它。
要为 Spring WebFlux 异常和任何 ErrorResponseException
启用 RFC 9457 响应,可以扩展 ResponseEntityExceptionHandler
并在 Spring 配置中将其声明为一个 @ControllerAdvice。该处理程序具有一个 @ExceptionHandler
方法,用于处理任何 ErrorResponse
异常,其中包括所有内置的 web 异常。你可以添加更多的异常处理方法,并使用一个受保护的方法将任何异常映射到一个 ProblemDetail
。
您可以通过 MVC 配置 使用 WebMvcConfigurer
注册 ErrorResponse
拦截器。使用它来拦截任何 RFC 9457 响应并采取一些措施。
非标准字段
您可以通过以下两种方式之一扩展 RFC 9457 响应中的非标准字段。
一,将未知属性插入到 ProblemDetail
的 "properties" Map
中。当使用 Jackson 库时,Spring Framework 会注册 ProblemDetailJacksonMixin
,确保这个 "properties" Map
被解包并在响应中作为顶级 JSON 属性进行渲染,同样在反序列化过程中任何未知属性都会被插入到这个 Map
中。
你还可以扩展 ProblemDetail
来添加专用的非标准属性。ProblemDetail
中的复制构造函数允许子类轻松地从现有的 ProblemDetail
创建。这可以集中完成,例如,从 @ControllerAdvice
中的 ResponseEntityExceptionHandler
重新创建异常的 ProblemDetail
为具有附加非标准字段的子类。
自定义和国际化
自定义和国际化错误响应详情是一个常见的需求。自定义 Spring MVC 异常的问题详情以避免暴露实现细节也是一种良好的实践。本节描述了对此的支持。
ErrorResponse
会公开 "type"、"title" 和 "detail" 的消息代码,以及 "detail" 字段的消息代码参数。ResponseEntityExceptionHandler
通过 MessageSource 解析这些代码,并相应地更新对应的 ProblemDetail
字段。
消息代码的默认策略如下:
-
"type":
problemDetail.type.[完全限定异常类名]
-
"title":
problemDetail.title.[完全限定异常类名]
-
"detail":
problemDetail.[完全限定异常类名][后缀]
一个 ErrorResponse
可能会暴露多个消息代码,通常是在默认消息代码后添加一个后缀。下表列出了 Spring MVC 异常的消息代码和参数:
异常 | 消息代码 | 消息代码参数 |
---|---|---|
AsyncRequestTimeoutException | (默认) | |
ConversionNotSupportedException | (默认) | {0} 属性名称, {1} 属性值 |
HandlerMethodValidationException | (默认) | {0} 列出所有验证错误。每个错误的消息代码和参数也通过 MessageSource 解析。 |
HttpMediaTypeNotAcceptableException | (默认) | {0} 支持的媒体类型列表 |
HttpMediaTypeNotAcceptableException | (默认) + ".parseError" | |
HttpMediaTypeNotSupportedException | (默认) | {0} 不支持的媒体类型, {1} 支持的媒体类型列表 |
HttpMediaTypeNotSupportedException | (默认) + ".parseError" | |
HttpMessageNotReadableException | (默认) | |
HttpMessageNotWritableException | (默认) | |
HttpRequestMethodNotSupportedException | (默认) | {0} 当前的 HTTP 方法, {1} 支持的 HTTP 方法列表 |
MethodArgumentNotValidException | (默认) | {0} 全局错误列表, {1} 字段错误列表。每个错误的消息代码和参数也通过 MessageSource 解析。 |
MissingRequestHeaderException | (默认) | {0} 头名称 |
MissingServletRequestParameterException | (默认) | {0} 请求参数名称 |
MissingMatrixVariableException | (默认) | {0} 矩阵变量名称 |
MissingPathVariableException | (默认) | {0} 路径变量名称 |
MissingRequestCookieException | (默认) | {0} Cookie 名称 |
MissingServletRequestPartException | (默认) | {0} 部分名称 |
NoHandlerFoundException | (默认) | |
NoResourceFoundException | (默认) | |
TypeMismatchException | (默认) | {0} 属性名称, {1} 属性值 |
UnsatisfiedServletRequestParameterException | (默认) | {0} 参数条件列表 |
与其他异常不同,MethodArgumentValidException
和 HandlerMethodValidationException
的消息参数基于 MessageSourceResolvable
错误列表,这些错误也可以通过 MessageSource 资源包进行自定义。有关更多详细信息,请参见 自定义验证错误。
客户端处理
客户端应用程序在使用 WebClient
时可以捕获 WebClientResponseException
,或者在使用 RestTemplate
时捕获 RestClientResponseException
,并使用它们的 getResponseBodyAs
方法将错误响应体解码为任何目标类型,例如 ProblemDetail
或 ProblemDetail
的子类。