推荐使用@ControllerAdvice+@ExceptionHandler配合自定义ErrorResponse实体,封装timestamp(Instant.now())、path(从HttpServletRequest获取)、error(HttpStatus.getReasonPhrase())、status(HTTP状态码),并支持按异常类型精细化返回400/404/405等状态及对应错误信息。

<p>在Spring Boot中实现统一异常处理并返回包含<code>timestamp</code>、<code>path</code>、<code>error</code>、<code>status</code>字段的响应结构,推荐使用<code>@ControllerAdvice</code> + <code>@ExceptionHandler</code>配合自定义响应体。</p>
<H3>定义标准化响应实体</H3>
<p>创建一个通用响应类(如<code>ErrorResponse</code>),封装异常时需返回的核心字段:</p>
<ul>
<li><code>timestamp</code>:使用<code>Instant.now()</code>或<code>LocalDateTime.now()</code>,建议用<code>Instant</code>避免时区歧义</li>
<li><code>path</code>:从请求上下文获取当前请求路径(通过<code>WebRequest</code>或<code>HttpServletRequest</code>)</li>
<li><code>error</code>:异常的简明描述(如<code>"Bad Request"</code>或<code>"Internal Server Error"</code>),可从<code>HttpStatus</code>获取<code>getReasonPhrase()</code></li>
<li><code>status</code>:HTTP状态码(<code>int</code>类型,如<code>400</code>、<code>500</code>)</li>
</ul>
<H3>编写全局异常处理器</H3>
<p>使用<code>@ControllerAdvice</code>捕获所有控制器抛出的异常,并构造<code>ErrorResponse</code>返回:</p>
<pre><code>@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(
Exception ex, WebRequest request) {
HttpStatus status = (ex instanceof HttpClientErrorException)
? ((HttpClientErrorException) ex).getStatusCode()
: HttpStatus.INTERNAL_SERVER_ERROR;
ErrorResponse errorResponse = new ErrorResponse(
Instant.now(),
request.getDescription(false), // 默认含path,也可用 request.getContextPath() + request.getServletPath()
status.getReasonPhrase(),
status.value()
);
return ResponseEntity.status(status).body(errorResponse);
}
}</code></pre>
<p>注意:<code>request.getDescription(false)</code>默认返回<code>uri=xxx</code>,若需纯净<code>path</code>,建议注入<code>HttpServletRequest</code>并调用<code>getServletPath()</code>或<code>getRequestURI()</code>。</p>
<H3>补充常见异常的精细化处理</H3>
<p>为不同异常类型提供更准确的状态码和错误信息:</p>
<ul>
<li><code>@ResponseStatus(HttpStatus.NOT_FOUND)</code>修饰的自定义异常(如<code>UserNotFoundException</code>)可单独捕获,设<code>error="Not Found"</code>、<code>status=404</code></li>
<li><code>MethodArgumentNotValidException</code>对应参数校验失败,<code>error</code>可取第一个校验错误的<code>defaultMessage</code>,<code>status=400</code></li>
<li><code>HttpRequestMethodNotSupportedException</code>返回<code>error="Method Not Supported"</code>,<code>status=405</code></li>
</ul>
<H3>确保响应体序列化友好</H3>
<p>在<code>ErrorResponse</code>类中添加Lombok注解或手动写getter/setter,并用<code>@JsonFormat</code>规范时间格式:</p>
<pre><code>public class ErrorResponse {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
private Instant timestamp;
private String path;
private String error;
private int status;
// 构造方法、getter、setter...
}</code></pre>
<p>这样返回的JSON会是标准格式,例如:</p>
<font color="gray">{
"timestamp": "2024-06-15 14:22:33.123",
"path": "/api/users/999",
"error": "Not Found",
"status": 404
}</font>