2017-07-25 59 views
0

TL; DR:调用HttpServletResponse.getStatus & HttpStatus.valueOf(HttpServletResponse.getStatus)).name()@ExceptionHandler函数返回200 OK而不是400 Bad RequestMissingServletParameterExceptionMissingServletParameterException仅作为示例使用,其他例外情况也会发生。春@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" 
} 

任何帮助或建议我做错了将不胜感激。感谢您的时间。

+0

你尝试这样'@ResponseStatus(HttpStatus.CONFLICT)'近方法的声明? –

+0

是的,不幸的是它仍然返回200 OK状态。 –

+0

尝试删除'@ ResponseBody'并添加'@ ResponseStatus'。如果它不起作用,请在调试模式下检查您的代码是否正在运行,因为可以通过其他方式处理异常。 –

回答

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; 
    } 
} 

裸露的骨头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; 
    } 
} 
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是例外的,你在运行时抛出的类型。假设我正在处理错误的请求。

+0

这有效,但它不完全是我所追求的。我想要一个异常处理程序来处理所有抛出的异常。尽管我现在已经明白了。 –