春@ExceptionHandler返回错误代码的HTTPStatus
TL; DR:调用HttpServletResponse.getStatus
& HttpStatus.valueOf(HttpServletResponse.getStatus)).name()
时@ExceptionHandler
函数返回200 OK
而不是400 Bad Request
为MissingServletParameterException
。 MissingServletParameterException
仅作为示例使用,其他例外情况也会发生。春@ExceptionHandler返回错误代码的HTTPStatus
你好,
我遇到的问题是,我想Raygun(崩溃报告系统)与我们的Java /春季启动应用程序集成。我发现最简单的方法是创建一个会显示错误信息给用户,以及将异常传递到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,邮差,定制集成等)。所以没有可以使用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;
}
}
与此唯一的问题是,当我故意导致异常被抛出,它的200 OK
HTTP状态这显然是不正确的返回。
例如,这是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"
}
任何帮助或建议我做错了将不胜感激。感谢您的时间。
我还无法确定何时正被抛出的异常为什么它返回一个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;
}
}
裸露的骨头implentation应该是这样的:
@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;
}
}
在您的控制器建议尝试这种方式异常类型映射到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是例外的,你在运行时抛出的类型。假设我正在处理错误的请求。
这有效,但它不完全是我所追求的。我想要一个异常处理程序来处理所有抛出的异常。尽管我现在已经明白了。 –
你尝试这样'@ResponseStatus(HttpStatus.CONFLICT)'近方法的声明? –
是的,不幸的是它仍然返回200 OK状态。 –
尝试删除'@ ResponseBody'并添加'@ ResponseStatus'。如果它不起作用,请在调试模式下检查您的代码是否正在运行,因为可以通过其他方式处理异常。 –