Tomcat异常如何转化为JSON格式
在Web应用开发中,将Tomcat抛出的异常转换为JSON格式响应是一种常见的实践,特别是在前后端分离的架构中,这种处理方式能够使前端应用更方便地捕获和处理错误信息,提供更好的用户体验,本文将详细介绍如何在Tomcat中实现异常到JSON格式的转换。
为什么需要将异常转换为JSON格式
- 前后端分离需求:现代Web应用多采用前后端分离架构,前端通过API与后端交互,JSON是前后端数据交换的标准格式。
- 统一错误响应:将异常信息封装为JSON格式,可以统一错误响应的结构,便于前端统一处理。
- 友好提示:可以自定义错误信息,向用户返回更友好的错误提示,而不是直接暴露技术细节。
- 国际化支持:JSON格式的错误响应可以方便地支持多语言错误提示。
实现异常转JSON的几种方式
使用Servlet Filter过滤器
通过过滤器拦截所有请求,捕获异常并转换为JSON响应:
@WebFilter("/*")
public class ExceptionToJsonFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
convertToJsonResponse(response, e);
}
}
private void convertToJsonResponse(ServletResponse response, Exception e) throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("status", "error");
errorResponse.put("message", e.getMessage());
errorResponse.put("timestamp", System.currentTimeMillis());
if (e instanceof ServletException) {
errorResponse.put("exception", ((ServletException) e).getRootCause().getClass().getSimpleName());
} else {
errorResponse.put("exception", e.getClass().getSimpleName());
}
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(errorResponse));
out.flush();
}
}
使用Spring Boot的@ControllerAdvice
如果项目是基于Spring Boot的,可以使用@ControllerAdvice和@ExceptionHandler注解来实现:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleException(Exception e) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
body.put("error", "Internal Server Error");
body.put("message", e.getMessage());
body.put("path", ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getRequestURI());
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(ServletException.class)
public ResponseEntity<Map<String, Object>> handleServletException(ServletException e) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
body.put("error", "Servlet Error");
body.put("message", e.getMessage());
body.put("rootCause", e.getRootCause() != null ? e.getRootCause().getMessage() : "No root cause");
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
自定义异常处理器
对于非Spring项目,可以自定义异常处理器:
public class CustomExceptionHandler {
public void handleException(HttpServletRequest request, HttpServletResponse response, Exception e)
throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("success", false);
errorResponse.put("error", e.getMessage());
errorResponse.put("errorCode", "INTERNAL_ERROR");
errorResponse.put("timestamp", System.currentTimeMillis());
if (e instanceof ServletException) {
errorResponse.put("details", ((ServletException) e).getRootCause().toString());
}
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(errorResponse));
out.flush();
}
}
使用Tomcat的Valve组件
对于更底层的控制,可以使用Tomcat的Valve组件:
public class ExceptionToJsonValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
getNext().invoke(request, response);
} catch (Exception e) {
convertToJsonResponse(response, e);
}
}
private void convertToJsonResponse(Response response, Exception e) throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("status", "error");
errorResponse.put("message", e.getMessage());
errorResponse.put("timestamp", System.currentTimeMillis());
if (e instanceof ServletException) {
errorResponse.put("exception", ((ServletException) e).getRootCause().getClass().getSimpleName());
} else {
errorResponse.put("exception", e.getClass().getSimpleName());
}
Writer writer = response.getWriter();
writer.write(new ObjectMapper().writeValueAsString(errorResponse));
writer.flush();
}
}
然后在Tomcat的context.xml中配置:
<Context>
<Valve className="com.your.package.ExceptionToJsonValve"/>
</Context>
最佳实践建议
- 统一错误码:定义一套统一的错误码,便于前端根据错误码进行不同处理。
- 记录错误日志:在转换异常为JSON的同时,确保记录完整的错误日志到服务器。
- 敏感信息过滤:避免将敏感信息(如堆栈跟踪、内部变量等)直接暴露给客户端。
- 区分客户端错误和服务端错误:对于400、401、403、404等客户端错误,返回相应的HTTP状态码。
- 使用JSON库:推荐使用Jackson或Gson等成熟的JSON库进行序列化。
示例JSON响应格式
{
"status": "error",
"code": 500,
"message": "Internal server error occurred",
"details": "java.lang.NullPointerException: Cannot invoke method on null object",
"timestamp": 1634567890123,
"path": "/api/users/123"
}
将Tomcat异常转换为JSON格式是提升Web应用用户体验和前后端协作效率的重要手段,通过Servlet Filter、Spring的@ControllerAdvice、自定义异常处理器或Tomcat Valve等方式,可以根据项目需求选择最适合的实现方案,关键在于保持错误响应的一致性、安全性和可维护性,确保前端能够方便地处理各种异常情况。



还没有评论,来说两句吧...