2017-07-25 235 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

這有效,但它不完全是我所追求的。我想要一個異常處理程序來處理所有拋出的異常。儘管我現在已經明白了。 –