基于Spring實現(xiàn)自定義錯誤信息返回詳解
背景
Spring 提供了 @RestControllerAdvice
用來實現(xiàn) HTTP 協(xié)議的全局異常處理。在異常信息的處理上通常只返回特定的 Response 對象,如下。
@Slf4j @RestControllerAdvice public class RestExceptionResolver { @ExceptionHandler(Exception.class) public ResponseEntity<?> processException(Exception ex) { BodyBuilder builder; Response response; ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class); if (responseStatus != null) { builder = ResponseEntity.status(responseStatus.value()); response = Response.buildFailure("500", responseStatus.reason()); } else { builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR); response = Response.buildFailure("500", ex.getMessage()); } this.process(ex, response); return builder.body(response); } }
作為基礎(chǔ)框架,筆者就遇到項目A 要求返回 Response1 對象,項目B 要求返回 Response2 對象,這個時候,適配起來就很痛苦,例如下方的代碼。
@Slf4j @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) @Data public class Response extends DTO { private static final long serialVersionUID = 1L; private boolean success; private String errCode; // 項目A 要求錯誤碼是字符型 private String errMessage; // 項目A 要求用這個名字 private int code; // 項目B 要求錯誤碼是整型 private String message; // 項目B 要求用這個名字 }
另外,@RestControllerAdvice
只適用于 Web 異常捕獲,我們還要考慮其他組件的情況,例如 Dubbo 捕獲 RPC 異常、Sentinel 組件觸發(fā)限流、Spring Security 安全框架拋出認(rèn)證異常。
目標(biāo)
不需要修改基礎(chǔ)框架,允許業(yè)務(wù)方自行擴(kuò)展異常返回對象。
實現(xiàn)
將 Response
提煉為 Builder 模式,改為 ResponseBuilder.builder()
構(gòu)建返回對象。
@Slf4j @RestControllerAdvice public class RestExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<?> resolveException(Exception ex) { BodyBuilder builder; Object response; ResponseStatus status = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class); if (status != null) { builder = ResponseEntity.status(status.value()); response = ResponseBuilder.builder().buildFailure("500", status.reason()); } else { builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR); response = ResponseBuilder.builder().buildFailure("500", ex.getMessage()); } this.postProcess(ex); return builder.body(response); } } public interface ResponseBuilder<T> { static ResponseBuilder<?> builder() { // 嘗試從業(yè)務(wù)項目獲取自定義的 Spring Bean ResponseBuilder<?> builder = ApplicationContextHelper.getBean(ResponseBuilder.class); if (builder != null) { return builder; } // 如果業(yè)務(wù)項目沒有自定義 Bean,返回默認(rèn)的 Builder return DefalutResponseBuilder.getInstance(); } T buildSuccess(); <Body> T buildSuccess(Body data); T buildFailure(String errCode, String errMessage, Object... params); } public class DefalutResponseBuilder implements ResponseBuilder<Response> { private static final DefaultResponseBuilder INSTANCE = new DefaultResponseBuilder(); private DefaultResponseBuilder() {} public static DefaultResponseBuilder getInstance() { return INSTANCE; } @Override public Response buildSuccess() { Response response = new Response(); response.setSuccess(true); return response; } @Override public <Body> Response buildSuccess(Body data) { SingleResponse<Body> response = new SingleResponse<>(); response.setSuccess(true); response.setData(data); return response; } @Override public Response buildFailure(String errCode, String errMessage, Object... params) { Response response = new Response(); response.setSuccess(false); response.setErrCode(errCode); response.setErrMessage(MessageFormatter.arrayFormat(message, placeholders).getMessage()); return response; } } @Slf4j @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) @Data public class Response extends DTO { private static final long serialVersionUID = 1L; private boolean success; private String errCode; private String errMessage; }
業(yè)務(wù)方覺得 Response
不能滿足需求,重新定義了新對象,如下。
@Slf4j @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) @Data public class CustomResponse { private static final long serialVersionUID = 1L; private boolean success; private int code; // 要求錯誤碼是整型 private String message; // 前端要求用這個名字 }
創(chuàng)建 CustomResponseBuilder
包裝 CustomResponse
對象,并標(biāo)記 @Component
注解,放入 Spring Bean 管理。
@Component public class CustomResponseBuilder implements ResponseBuilder<CustomResponse> { @Override public CustomResponse buildSuccess() { CustomResponse response = new CustomResponse(); response.setSuccess(true); return response; } @Override public <Body> CustomResponse buildSuccess(Body data) { // 略 } @Override public CustomResponse buildFailure(int code, String message, Object... params) { CustomResponse response = new CustomResponse(); response.setSuccess(false); response.setCode(code); response.setMessage(MessageFormatter.arrayFormat(message, placeholders).getMessage()); return response; } }
上述已提到 ResponseBuilder.builder()
優(yōu)先查找 Spring Bean,所以 CustomResponseBuilder
覆蓋了框架內(nèi)置的 DefaultResponseBuilder
類,全局異常捕獲器返回結(jié)果時,就能返回業(yè)務(wù)方自定義的 CustomResponse
對象,這樣,不需要改動框架,就能滿足業(yè)務(wù)需求。
產(chǎn)出
根據(jù)這個思路,我們分別實現(xiàn)了 Web 異常、Dubbo 異常、Sentinel 限流、Security 認(rèn)證等各種場景的異常處理機(jī)制,業(yè)務(wù)方只需要自行創(chuàng)建 ResponseBuilder
擴(kuò)展自己的返回對象即可,不需要修改框架。
到此這篇關(guān)于基于Spring實現(xiàn)自定義錯誤信息返回詳解的文章就介紹到這了,更多相關(guān)Spring自定義錯誤信息內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
教你怎么用SpringBoot+Mybati-Plus快速搭建代碼
Mybatis自身通過了逆向工程來幫助我們快速生成代碼,但Mybatis-plus卻更加強(qiáng)大,不僅僅可以生成dao,pojo,mapper,還有基本的controller和service層代碼,接下來我們來寫一個簡單的人門案例是看看如何mybatis-plus是怎么實現(xiàn)的,需要的朋友可以參考下2021-06-06Java面試崗常見問題之ArrayList和LinkedList的區(qū)別
ArrayList和LinkedList作為我們Java中最常使用的集合類,很多人在被問到他們的區(qū)別時,憋了半天僅僅冒出一句:一個是數(shù)組一個是鏈表。這樣回答簡直讓面試官吐血。為了讓兄弟們打好基礎(chǔ),我們通過實際的使用測試,好好說一下ArrayList和LinkedList的區(qū)別這道經(jīng)典的面試題2022-01-01Java基于Netty實現(xiàn)Http server的實戰(zhàn)
本文主要介紹了Java基于Netty實現(xiàn)Http server的實戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02SpringCache緩存抽象之CacheManager與自定義鍵生成方式
本文將深入探討Spring Cache的核心組件CacheManager及自定義鍵生成策略,幫助開發(fā)者掌握緩存配置與優(yōu)化技巧,從而構(gòu)建高效可靠的緩存系統(tǒng),希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04SpringBoot整合MybatisPlus實現(xiàn)增刪改查功能
MybatisPlus是國產(chǎn)的第三方插件,?它封裝了許多常用的CURDapi,免去了我們寫mapper.xml的重復(fù)勞動。本文將整合MybatisPlus實現(xiàn)增刪改查功能,感興趣的可以了解一下2022-05-05Java IO流體系繼承結(jié)構(gòu)圖_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了Java IO流體系繼承結(jié)構(gòu)圖,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-05-05