【发布时间】:2017-12-30 23:24:32
【问题描述】:
TL;DR:当调用 HttpServletResponse.getStatus 和 HttpStatus.valueOf(HttpServletResponse.getStatus)).name() 时,@ExceptionHandler 函数返回 200 OK 而不是 400 Bad Request 的 MissingServletParameterException。 MissingServletParameterException 仅作为示例使用,其他异常也会发生。
你好,
我遇到的问题是我正在尝试将 Raygun(一个崩溃报告系统)与我们的 Java/Spring Boot 应用程序集成。我发现的最简单的方法是创建一个自定义异常处理程序,它将向用户显示错误消息并将异常传递给 Raygun。
最初,我尝试了这里建议的实现,并添加了我自己的 Raygun 实现 https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
@ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
private static ApiAccessToken accessToken = new ApiAccessToken();
private static String databaseName = null;
@ExceptionHandler(value = Exception.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with @ResponseStatus rethrow it and let
// the framework handle it
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
// Otherwise setup and send the user to a default error-view.
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
// Display the error message to the user, and send the exception to Raygun along with any user details provided.
RaygunClient client = new RaygunClient("<MyRaygunAPIKey>");
if (accessToken.getUsername() != null && accessToken.getDatabaseName() != null) {
ArrayList tags = new ArrayList<String>();
tags.add("username: " + accessToken.getUsername());
tags.add("database: " + accessToken.getDatabaseName());
client.Send(e, tags);
accessToken = null;
return mav;
} else if (databaseName != null) {
ArrayList tags = new ArrayList<String>();
tags.add("database: " + databaseName);
client.Send(e, tags);
databaseName = null;
return mav;
} else {
client.Send(e);
return mav;
}
}
我遇到的问题是我们有公共和私有 API 端点。私有 API 端点用于我们的 iOS 应用程序,而公共 API 端点没有前端。它们旨在让企业能够集成到自己的系统中(PowerBI、Postman、自定义集成等)。因此没有可以使用 ModelAndView 重定向到的视图。
相反,我决定不使用 ModelAndView,而是返回一个已格式化为模仿 Spring 的默认 JSON 错误消息的字符串。
@ExceptionHandler(value = Exception.class)
public @ResponseBody String defaultErrorHandler(HttpServletRequest req, HttpServletResponse resp, Exception e) throws Exception {
// Create a customised error message that imitates the Spring default Json error message
StringBuilder sb = new StringBuilder("{ \n")
.append(" \"timestamp\": ").append("\"").append(DateTime.now().toString()).append("\" \n")
.append(" \"status\": ").append(resp.getStatus()).append(" \n")
.append(" \"error\": ").append("\"").append(HttpStatus.valueOf(resp.getStatus()).name()).append("\" \n")
.append(" \"exception\": ").append("\"").append(e.getClass().toString().substring(6)).append("\" \n")
.append(" \"message\": ").append("\"").append(e.getMessage()).append("\" \n")
.append(" \"path\": ").append("\"").append(req.getServletPath()).append("\" \n")
.append("}");
String errorMessage = String.format(sb.toString());
// Display the error message to the user, and send the exception to Raygun along with any user details provided.
RaygunClient client = new RaygunClient("<MyRaygunAPIKey>");
if (accessToken.getUsername() != null && accessToken.getDatabaseName() != null) {
ArrayList tags = new ArrayList<String>();
tags.add("username: " + accessToken.getUsername());
tags.add("database: " + accessToken.getDatabaseName());
client.Send(e, tags);
accessToken = null;
return errorMessage;
} else if (databaseName != null) {
ArrayList tags = new ArrayList<String>();
tags.add("database: " + databaseName);
client.Send(e, tags);
databaseName = null;
return errorMessage;
} else {
client.Send(e);
return errorMessage;
}
}
唯一的问题是,当我故意引发异常时,它返回的 HTTP 状态为 200 OK,这显然是不正确的。
例如,defaultErrorHandler() 被注释掉(不向 Raygun 发送任何内容):
{
"timestamp": "2017-07-18T02:59:45.131+0000",
"status": 400,
"error": "Bad Request",
"exception":
"org.springframework.web.bind.MissingServletRequestParameterException",
"message": "Required String parameter ‘foo’ is not present",
"path": "/api/foo/bar/v1"
}
这是没有注释掉的(将异常发送给 Raygun):
{
"timestamp": "2017-07-25T06:21:53.895Z"
"status": 200
"error": "OK"
"exception": "org.springframework.web.bind.MissingServletRequestParameterException"
"message": "Required String parameter 'foo' is not present"
"path": "/api/foo/bar/V1"
}
任何关于我做错了什么的帮助或建议将不胜感激。感谢您的宝贵时间。
【问题讨论】:
-
你有没有尝试过类似
@ResponseStatus(HttpStatus.CONFLICT)的方法声明? -
是的,不幸的是它仍然返回 200 OK 状态。
-
尝试删除
@ResponseBody并添加@ResponseStatus。如果它不起作用,请在调试模式下检查您的代码是否正在运行,因为可以通过其他方式处理异常。 -
我不能使用
@Response Status,因为我不知道 HttpStatus 是什么。我只是以MissingServletRequestParameterException为例,而它可能是任何返回 400、403、405、415、500 等 HttpStatus 的异常。如果您检查我的答案,我现在可以正常工作了。不过感谢您的帮助。
标签: java spring http spring-boot exception-handling