[英]Global Exception Handling in Jersey & Spring?
我正在使用 Jersey & Spring 3.2 和 Open CMIS 開發 RESTful web 服務。
我沒有使用 Spring 的 MVC 模式,它只是 Spring IOC 和 Jersey SpringServlet,控制器類類似於下面的代碼
@GET
@Path("/{objId:.+}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public statusMsg addObject(@PathParam("objId") String objId{
return repoService.addObject(objId);
}
在 repoService 中,我正在執行業務邏輯以使用 CMIS 添加對象,我的問題是我捕獲了大約 5 個與 CMIS 相關的異常,然后是基本異常,即 Exception 但是對於每個服務方法,我都必須重復它,而我沒有想做。
我在 Google 上搜索,發現 @ControllerAdvice 是解決此類問題的最佳解決方案,您可以定義所有已檢查和未檢查的異常,並從應用程序中刪除所有 try catch 塊。 但它只適用於 MVC 模式。
問題 1:有沒有辦法在上面的 Jersey-Spring 框架中使用它?
經過更多研究,我發現 Jersey 提供了 ExceptionMapper 來處理自定義異常,但我想捕獲更多 CMIS 異常或默認異常或 IO 異常等。
問題 2:我如何使用 ExceptionMapper 做到這一點?
問題 3:我是否采用正確的方法,或者您是否建議使用更好的方法來處理此類問題。
提前致謝。
我將 jersey2.11 與 Tomcat 一起使用,並且幾乎將異常句柄與 ExceptionMapper 一起使用。 (在域邏輯中,只有 DB 回滾過程使用 try-catch 代碼。)
我認為帶有@Provider 的 ExceptionMapper 會自動選擇正確的 ExceptionMapper。 所以我想這個函數滿足於“我想捕獲更多的 CMIS 異常或默認異常或 IO 異常等”。
這段代碼是我處理 ExceptionMapper 的設計代碼。
@GET
@Produces("application/json")
public String getUser(@NotNull @QueryParam("id") String id,
@NotNull @QueryParam("token") String token) throws Exception { // This level throws exceptions handled by ExceptionMapper
someComplexMethod(id, token); // possible throw Exception, IOException or other exceptions.
return CLICHED_MESSAGE;
}
AbstractExceptionMapper.java (所有 ExceptionMapper 類都擴展了這個抽象類)
public abstract class AbstractExceptionMapper {
private static Logger logger = LogManager.getLogger(); // Example log4j2.
protected Response errorResponse(int status, ResponseEntity responseEntity) {
return customizeResponse(status, responseEntity);
}
protected Response errorResponse(int status, ResponseEntity responseEntity, Throwable t) {
logger.catching(t); // logging stack trace.
return customizeResponse(status, responseEntity);
}
private Response customizeResponse(int status, ResponseEntity responseEntity) {
return Response.status(status).entity(responseEntity).build();
}
}
ExceptionMapper.java (至少這個映射器可以捕獲任何未定義指定異常映射器的異常。)
@Provider
public class ExceptionMapper extends AbstractExceptionMapper implements
javax.ws.rs.ext.ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception e) {
// ResponseEntity class's Member Integer code, String message, Object data. For response format.
ResponseEntity re = new ResponseEntity(Code.ERROR_MISC);
return this.errorResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, re, e);
}
}
WebApplicationExceptionMapper.java (指定WebApplicationException)
@Provider
public class WebApplicationExceptionMapper extends AbstractExceptionMapper implements
ExceptionMapper<WebApplicationException> {
@Override
public Response toResponse(WebApplicationException e) {
ResponseEntity re = new ResponseEntity(Code.ERROR_WEB_APPLICATION);
return this.errorResponse(e.getResponse().getStatus(), re, e);
}
}
ConstraintViolationExceptionMapper.java (指定Hibernate Validator ConstraintViolationException)
@Provider
public class ConstraintViolationExceptionMapper extends AbstractExceptionMapper implements
ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(ConstraintViolationException e) {
ResponseEntity re = new ResponseEntity(Code.ERROR_CONSTRAINT_VIOLATION);
List<Map<String, ?>> data = new ArrayList<>();
Map<String, String> errorMap;
for (final ConstraintViolation<?> error : e.getConstraintViolations()) {
errorMap = new HashMap<>();
errorMap.put("attribute", error.getPropertyPath().toString());
errorMap.put("message", error.getMessage());
data.add(errorMap);
}
re.setData(data);
return this.errorResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, re, e);
}
}
.. 和其他指定異常可以創建 ExceptionMapper 類。
根據我的經驗,Exception Mapper 是專注於域邏輯的高級思想。 它可以從域邏輯中去除無聊的分散的 try-catch 塊代碼。 所以我希望你能在問題 3 中感受到“是的,我是”來解決你環境中的問題。
您沒有在應用程序中的任何地方使用過 try catch 和 throw。
我的代碼設計使用 throws at 這樣的方法,這使得通過 ExceptionMapper 類進行管理。
public String getUser(@NotNull @QueryParam("id") String id,
@NotNull @QueryParam("token") String token) throws Exception
所以在上面的方法中,我只為我可以預期的所有異常創建了 1 個類,對於任何未知異常,基本異常將在那里捕獲。 現在,無論在我的應用程序中的任何地方,如果發生任何異常,都會出現 CentralControllerException 並發送回帶有 http 狀態代碼的相應響應。 Q.2. 您是否預見到上述方法中的任何問題。
我認為如果簡單的項目或從不更新/修改項目(項目生命周期短時間),您的一類異常映射器方法就可以了。 但是......我從不采取這種方法。 簡單來說,如果需要管理更多的異常,這個方法就會變得龐大而復雜,並且變得難以閱讀和維護。
在我的政策中,OOP 應該在任何級別的代碼(類計划、DI 計划)中使用多態策略,這種方法的某些部分旨在消除代碼中的 if/switch 塊。 而這種思想使得每個方法代碼短小、簡單,清晰“領域邏輯”,代碼變得不易修改。
所以我創建了 ExceptionMapper 並委托給 DI 哪個 ExceptionMapper 類管理異常。 (所以 DI 管理替換你的單個類如果塊管理哪個異常處理,這通常是類似於 Extract xxx http://refactoring.com/catalog/extractClass.html 的重構方法。在我們討論的情況下,單個類和一個方法太忙了,因此提取每個接近的 ExceptionMapper 類並 DI 調用合適的類和方法策略。)
Btw,目前系統處理結果是一樣的。 但是如果需要降低未來的開發成本,就不應該采取接近一類的異常處理方案。 因為如果放棄簡單的代碼和重構狀態,項目代碼會死得更快。
這是我的想法以及為什么會這樣。
問候。
感謝您的回復。 我可以看到您根據異常類型和行為創建了多個類。
一季度。 在您的服務方法中,您是否拋出任何異常,例如
public void addObject(String objId) throws WebApplicationException{
}
或者您沒有在應用程序中的任何地方使用 try catch 和 throw。
實際上,我已經嘗試了一些在我的 Web 應用程序中我沒有在任何地方使用 try、catch 和 throws 的東西,並且在我的 CentralControllerException 中我提到了如下:
public class CentralControllerHandler implements ExceptionMapper<Exception> {
@Override
@Produces(MediaType.APPLICATION_JSON)
public Response toResponse(Exception ex) {
if(ex instanceof CmisContentAlreadyExistsException){
log.error(ex);
// send Response status as 400
}
if(ex instanceof IOException){
log.error(ex);
// send Response status as 400
}
return Response;
}
}
所以在上面的方法中,我只為我可以預期的所有異常創建了 1 個類,對於任何未知異常,基本異常將在那里捕獲。
現在,無論在我的應用程序中的任何地方,如果發生任何異常,都會出現 CentralControllerException 並發送回帶有 http 狀態代碼的相應響應。
Q.2. 您是否預見到上述方法中的任何問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.