TL; DR:@ExceptionHandler
函数在致电200 OK
&时为400 Bad Request
返回MissingServletParameterException
而不是HttpServletResponse.getStatus
HttpStatus.valueOf(HttpServletResponse.getStatus)).name()
。 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"
}
对于我做错的任何帮助或建议将不胜感激。谢谢你的时间。
答案 0 :(得分:0)
在你的控制器建议中,尝试这种方式将异常类型映射到Http-Status
,如下所示:
if (ex instanceof MyException)
{//just an example.
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}
else
{//all other unhandled exceptions
return new ResponseEntity<>(e, HttpStatus.INTERNAL_SERVER_ERROR);
}
这里MyException是您在运行时抛出的异常类型。说我正在处理错误的请求。
答案 1 :(得分:0)
我仍然不确定为何在抛出异常时返回200 OK
状态。但是我已经意识到我在尝试创建一个模仿Spring的默认json错误消息的字符串时所做的事情过于复杂而根本没有必要。
一旦我将异常发送到Raygun,我就可以重新抛出异常并让框架像使用@ResponseStatus
注释的任何异常一样处理它。
@ExceptionHandler(value = Exception.class)
public void defaultErrorHandler(Exception e) throws Exception {
RaygunClient client = new RaygunClient("<MyRaygunAPIKey>");
// 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 send the exception Raygun and then rethrow it and let the framework handle it
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;
throw e;
} else if (databaseName != null) {
ArrayList tags = new ArrayList<String>();
tags.add("database: " + databaseName);
client.Send(e, tags);
databaseName = null;
throw e;
} else {
client.Send(e);
throw e;
}
}
一个简单的实现方式如下:
@ExceptionHandler(value = Exception.class)
public void defaultErrorHandler(Exception e) throws Exception {
RaygunClient client = new RaygunClient("<MyRaygunAPIKey>");
// 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 send the exception Raygun and then rethrow it and let the framework handle it
else {
client.Send(e);
throw e;
}
}