《框架封裝 · 統一異常處理和返回值包裝》

📢 大家好,我是 【戰神劉玉棟】,有10多年的研發經驗,致力于前后端技術棧的知識沉淀和傳播。 💗
🌻 CSDN入駐不久,希望大家多多支持,后續會繼續提升文章質量,絕不濫竽充數,如需交流,歡迎留言評論。👍

文章目錄

    • 寫在前面的話
    • @RestControllerAdvice 實現異常處理
      • 基礎使用
      • 注解簡介
      • 實戰分析
    • ResponseBodyAdvice 實現返回值包裝
      • 技術說明
      • 實戰分析
      • 其他方式
    • 總結陳詞


寫在前面的話

此篇博文繼續介紹框架封裝過程中,關于統一異常處理和返回值包裝的具體方案,這本是一個相對常見的需求場景,此處結合實戰情況說明,各位看官可一睹為快。

技術棧:后端 SpringCloud + 前端 Vue/Nuxt


@RestControllerAdvice 實現異常處理

基礎使用

由于場景較簡單,也不構思了,可以直接實現,再來考慮內容。
由于是 SpringBoot 項目,直接使用注解@RestControllerAdvice的方式實現全局異常處理類。
先上一段示例代碼:

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler(value = Throwable.class)public ResultModel jsonErrorHandler(HttpServletRequest req, Throwable e) throws Exception {log.error("請求發生異常,URL:{},HTTP_METHOD:{},IP:{},錯誤信息:{}", req.getRequestURL().toString(),req.getMethod(), req.getRemoteAddr(), e.getMessage());ResultModel resultModel;//異常結果處理步驟return resultModel;}
}

注解簡介

@RestControllerAdvice是一個組合注解,由@ControllerAdvice、@ResponseBody組成,而@ControllerAdvice繼承了@Component,因此@RestControllerAdvice本質上是個Component,用于定義@ExceptionHandler,@InitBinder和@ModelAttribute方法,適用于所有使用@RequestMapping方法。
@RestControllerAdvice注解將作用在所有注解了@RequestMapping的控制器的方法上,該注解有一些屬性,可以設定具體的范圍。

Tips:上文提到的一些注解的基礎用法,網上資料很多,這邊不展開。

實戰分析

接下來談談博主所在企業是如何實現這一異常處理器的,它到底可以做,或者應該做哪些事情?
Step1、從上下文獲取鏈路ID,設置到響應頭,并設置響應狀態,代碼如下。

@ExceptionHandler(Exception.class)
public Object exceptionHandler(Exception ex) {IResult<?> result;try {String traceId = OnelinkContextHolder.getString(OnelinkConstant.TRACE_ID);// 響應頭增加鏈路IDthis.response.setHeader(OnelinkConstant.TRACE_ID, StrUtil.nullToEmpty(traceId));// 先默認設置HTTP狀態碼為500,然后根據具體異常處理再調整對應的狀態碼this.response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);// 統一分發并處理異常result = this.handleException(ex);} catch (Exception e) {log.error("全局異常處理發生錯誤", e);result = ResultVO.failure(ex.getMessage(), ExceptionUtil.stacktraceToString(e));}// 是否開啟異常處理指南if (!this.onelinkExceptionGuideProviders.isEmpty()) {this.appendExGuide(ex, result);}return result;
}

Step2、針對框架自定義的異常攔截器接口進行遍歷,先執行前置接口,再執行后置接口,這個思想貫穿整個框架搭建過程,預留給各小組的業務開發人員,更多擴展空間(那什么,遵循開閉原則,對修改關閉,對擴展開放)。

// 異常攔截器
if (this.interceptors != null) {for (WebExceptionInterceptor interceptor : this.interceptors) {ex = interceptor.beforeHandle(ex);}
}public interface WebExceptionInterceptor {/*** 全局異常處理前邏輯*/default Exception beforeHandle(Exception ex) {return ex;}/*** 全局異常處理后邏輯*/default Exception afterHandle(Exception ex, ResultVO<Object> resultVO) {return ex;}}

3、最后就是本職工作了,針對異常的不同類型,進行不同的組裝,比如ORA-開頭的異常做出翻譯處理等,還有一些異常日志記錄、是否異常指引等功能,這里不展開了。


ResponseBodyAdvice 實現返回值包裝

技術說明

0、ResponseBodyAdvice 是 Spring Framework 的 Web 模塊中的一個接口,它允許你在將響應體寫入 HTTP 響應之前攔截和修改它。它提供了一種全局定制響應處理邏輯的方式,適用于 Spring MVC 或 Spring WebFlux 應用程序。
1、ResponseBodyAdvice 可以在注解 @ResponseBody 將返回值處理成相應格式之前操作返回值,實現這個接口即可完成相應操作,可用于對response 數據的一些統一封裝或者加密等操作。
2、ResponseBodyAdvice 接口和 RequestBodyAdvice 接口類似,RequestBodyAdvice 是請求到Controller 之前攔截,做相應的處理操作,而ResponseBodyAdvice 是對Controller返回的{@code @ResponseBody}or a {@code ResponseEntity} 后,{@code HttpMessageConverter} 類型轉換之前攔截,進行相應的處理操作后,再將結果返回給客戶端。
3、實現 ResponseBodyAdvice 接口,需要重寫其 supports 和 beforeBodyWrite 方法。
1)supports方法:判斷是否要執行beforeBodyWrite方法,true為執行,false不執行。通過該方法可以選擇哪些類或那些方法的response要進行處理,其他的不進行處理。
2)beforeBodyWrite方法:對response方法進行具體操作處理。

public interface ResponseBodyAdvice<T> {/*** 1、選擇是否執行 beforeBodyWrite 方法,返回 true 執行,false 不執行* 2、通過 supports 方法,可以選擇對哪些類或方法的 Response 進行處理* @param returnType:返回類型* @param converterType:轉換器* @return :返回 true 則下面的 beforeBodyWrite  執行,否則不執行*/boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);/*** 對 Response 處理的具體執行方法* @param body:響應對象(response)中的響應體* @param returnType:控制器方法的返回類型* @param selectedContentType:通過內容協商選擇的內容類型* @param selectedConverterType:選擇寫入響應的轉換器類型* @param request:當前請求* @param response:當前響應* @return :返回傳入的主體或修改過的(可能是新的)主體*/@NullableT beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);
}
@ControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {// 根據返回類型和轉換器類型檢查是否應用此建議// 你可以在這里放置任何條件return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {// 在將響應體寫入輸出流之前修改它// 你可以在這里檢查或修改 'body' 對象return body;}
}

總結:ResponseBodyAdvice 接口允許在執行 @ResponseBody 或 ResponseEntity 控制器方法之后,但在使用 HttpMessageConverter 寫入響應體之前自定義響應,進行功能增強。通常用于加密,簽名,統一數據格式等。
注意:要使其生效參考框架代碼,關鍵點是@RestControllerAdvice。

實戰分析

可以用于針對返回數據進行處理,要特別注意如下點:

  • 異常結果的處理
  • Feign調用結果的處理
  • 普通數據的處理
  • 其他數據的處理

核心思路就是設置一個返回值類,根據返回數據的類型是否為該類進行判斷處理。

public Object beforeBodyWrite(Object responseBody,@NonNull MethodParameter methodParameter,@NonNull MediaType mediaType,@NonNull Class<? extends HttpMessageConverter<?>> clazz,@NonNull ServerHttpRequest serverHttpRequest,@NonNull ServerHttpResponse serverHttpResponse) {HttpHeaders reqHeaders = serverHttpRequest.getHeaders();String disableWrapperFlag = reqHeaders.getFirst(ResultWrapper.DISABLE_WRAPPER_HEADER_KEY);String rpcClient = reqHeaders.getFirst(RpcConstant.RPC_CLIENT_HEADER_NAME);if (this.couldSkip(mediaType, disableWrapperFlag, rpcClient)) {return responseBody;}Type type = methodParameter.getExecutable().getAnnotatedReturnType().getType();String traceId = this.traceIdProvider == null ? null : this.traceIdProvider.getTraceId();Object result;// 遠程調用直接返回if (responseBody instanceof ApiResult<?>) {result = responseBody;// 為返回結果設置鏈路ID} else if (responseBody instanceof IResult) {ResultVO<?> resultVO = (ResultVO<?>) responseBody;result = StrUtil.isBlank(resultVO.getTraceId()) ? resultVO.setTraceId(traceId) : resultVO;this.setResultEnv(resultVO);// 如果返回結果是字符串,不能直接返回ResultVO,否則會與StringHttpMessageConverter沖突} else if (responseBody instanceof String || type == String.class) {ResultVO<?> resultVO = ResultVO.success(responseBody).setTraceId(traceId);result = JSON.toJSONString(resultVO, SerializerFeature.WriteMapNullValue);serverHttpResponse.getHeaders().add("content-type", ContentType.JSON.toString());// 沒有被IResult包裝,默認使用ResultVO進行包裝} else {ResultVO<Object> resultVO = ResultVO.success(responseBody).setTraceId(traceId);this.setResultEnv(resultVO);result = resultVO;}return result;
}

還可以用于鏈路追蹤返回數據Span的數據二次處理,比如返回值長度截取等,具體不展開了。

String responseTempStr = JSONObject.toJSONString(responseBody);
String truncatedResult = responseTempStr.length() > 2000 ? responseTempStr.substring(0, 2000) + "..." : responseTempStr;
span.tag(TraceSpanConstant.HTTP_RESPONSE, truncatedResult);

其他方式

如果您的項目需要針對返回值做了一些自定義擴展或處理,除了可以使用ResponseBodyAdvice,還可以考慮一下下面兩個關鍵詞:MessageConvertersHandlerMethodReturnValueHandler,這里篇幅受限就不展開了。


總結陳詞

上文介紹了框架封裝人員,針對框架的統一異常和返回值包裝的處理過程,僅供參考。
本系列博文后續繼續更新,介紹框架搭建人員如何以恰當的方式應對各式各樣的情況,這也是此專欄的主題。
后續將持續更新,請多多支持!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/41289.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/41289.shtml
英文地址,請注明出處:http://en.pswp.cn/web/41289.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

貪心算法-以高校科研管理系統為例

1.貪心算法介紹 1.算法思路 貪心算法的基本思路是從問題的某一個初始解出發一步一步地進行&#xff0c;根據某個優化測度&#xff0c;每一 步都要確保能獲得局部最優解。每一步只考慮一 個數據&#xff0c;其選取應該滿足局部優化的條件。若下 一個數據和部分最優解連在一起…

JavaEE初階-網絡原理1

文章目錄 前言一、UDP報頭二、UDP校驗和2.1 CRC2.2 md5 前言 學習一個網絡協議&#xff0c;最主要就是學習的報文格式&#xff0c;對于UDP來說&#xff0c;應用層數據到達UDP之后&#xff0c;會給應用層數據報前面加上UDP報頭。 UDP數據報UDP包頭載荷 一、UDP報頭 如上圖UDP的…

Kubernetes(K8s) kubectl 常用命令

文章目錄 一、常用命令1.1 kubectl describe 命令 二、kubectl 命令中的簡寫三、Helm3.1 常用命令&#xff1a;3.2 遇到的問題3.2.1 cannot re-use a name that is still in use 四、Containerd 一、常用命令 檢查 k8s 各節點狀態&#xff0c;確保k8s集群各節點狀態正常&#x…

概率基礎——矩陣正態分布matrix normal distribution

矩陣正態分布-matrix normal distribution 定義性質應用 最近碰到了這個概念&#xff0c;記錄一下 矩陣正態分布是一種推廣的正態分布&#xff0c;它應用于矩陣形式的數據。矩陣正態分布在多維數據分析、貝葉斯統計和機器學習中有廣泛的應用。其定義和性質如下&#xff1a; 定…

Emacs之解決:java-mode占用C-c C-c問題(一百四十六)

簡介&#xff1a; CSDN博客專家&#xff0c;專注Android/Linux系統&#xff0c;分享多mic語音方案、音視頻、編解碼等技術&#xff0c;與大家一起成長&#xff01; 優質專欄&#xff1a;Audio工程師進階系列【原創干貨持續更新中……】&#x1f680; 優質專欄&#xff1a;多媒…

【django項目使用easycython編譯】Cannot convert Unicode string to ‘str‘ implicitly.

django項目編譯遇到的問題 報錯條件 需要編譯的python源碼里面的函數寫了type hint&#xff0c;尤其是return的type hint&#xff0c; 當type hint是str時&#xff0c;但是變量確實f-string格式化后得到的&#xff0c;編譯時會報錯 報錯原因 easycython會檢查變量類型&…

軟件開發中的原型開發與需求文檔開發:哪個更優?

1. 引言 在軟件開發過程中&#xff0c;選擇合適的開發方法對于項目的成功至關重要。基于原型開發和基于需求文檔開發是兩種常見的開發方法&#xff0c;各自有其優點和缺點。在項目復雜性、客戶需求和資源限制等因素的影響下&#xff0c;開發團隊需要慎重選擇適合的開發方法。 …

C++語言相關的常見面試題目(二)

1.vector底層實現原理 以下是 std::vector 的一般底層實現原理&#xff1a; 內存分配&#xff1a;當創建一個 std::vector 對象時&#xff0c;會分配一塊初始大小的連續內存空間來存儲元素。這個大小通常會隨著 push_back() 操作而動態增加。 容量和大小&#xff1a;std::vec…

element-plus 的form表單組件之el-radio(單選按鈕組件)

單選按鈕組件適用于同一組類型的選項只能互斥選擇的場景&#xff0c;就是支持單選。單選組件包含以下3個組件 組件名作用el-radio-group單選組組件&#xff0c;子元素可以是el-radio或el-radio-button&#xff0c;v-mode綁定單選組的響應式屬性el-radio單選組件&#xff0c;la…

階段三:項目開發---搭建項目前后端系統基礎架構:任務9:導入空管基礎數據

任務描述 本階段任務是導入項目的基礎數據&#xff0c;包括空管基礎數據和離線的實時飛行數據&#xff08;已經脫敏&#xff09;。 任務指導 本階段任務需要導入兩種數據&#xff1a; 1、在MySQL中導入空管基礎數據 kongguan.sql空管基礎數據表說明&#xff1a; 1告警信息…

OpenCV直方圖計算函數calcHist的使用

操作系統&#xff1a;ubuntu22.04OpenCV版本&#xff1a;OpenCV4.9IDE:Visual Studio Code編程語言&#xff1a;C11 功能描述 圖像的直方圖是一種統計表示方法&#xff0c;用于展示圖像中不同像素強度&#xff08;通常是灰度值或色彩強度&#xff09;出現的頻率分布。具體來說…

對MsgPack與JSON進行序列化的效率比較

序列化是將對象轉換為字節流的過程&#xff0c;以便在內存或磁盤上存儲。常見的序列化方法包括MsgPack和JSON。以下將詳細探討MsgPack和JSON在序列化效率方面的差異。 1. MsgPack的效率&#xff1a; 優點&#xff1a; 高壓縮率&#xff1a; MsgPack采用高效的二進制編碼格式&…

Embedding理解

一、概念 Embedding 可以理解為一種將概念、物體或信息轉換為數字序列的數值表示方法。它是溝通兩個不同世界或領域的橋梁,能夠把各種類型的數據(如文本、圖像、視頻等)映射到一個向量空間中。 在這個向量空間里,相似的項目(例如語義上相近的單詞、相似的圖像或相關的視…

cs231n作業1——SVM

參考文章&#xff1a;cs231n assignment1——SVM SVM 訓練階段&#xff0c;我們的目的是為了得到合適的 &#x1d44a; 和 &#x1d44f; &#xff0c;為實現這一目的&#xff0c;我們需要引進損失函數&#xff0c;然后再通過梯度下降來訓練模型。 def svm_loss_naive(W, …

【Qt】Qt概述

目錄 一. 什么是Qt 二. Qt的優勢 三. Qt的應用場景 四. Qt行業發展方向 一. 什么是Qt Qt是一個跨平臺的C圖形用戶界面應用程序框架&#xff0c;為應用程序開發者提供了建立藝術級圖形界面所需的所有功能。 Qt是完全面向對象的&#xff0c;很容易擴展&#xff0c;同時Qt為開發…

從打印到監測:納米生物墨水助力3D生物打印與組織監測平臺?

從打印到監測&#xff1a;納米生物墨水助力3D生物打印與組織監測平臺&#xff1f; 在 3D 組織工程中&#xff0c;納米生物墨水是將納米材料與 ECM 水凝膠結合&#xff0c;以提高其打印性和功能性的重要策略。納米生物墨水可以增強水凝膠的機械性能、導電性、生物活性&#xff…

汽車報價資訊app小程序模板源碼

藍色實用的汽車報價&#xff0c;汽車新聞資訊&#xff0c;最新上市汽車資訊類小程序前端模板。包含&#xff1a;選車、資訊列表、榜單、我的主頁、報價詳情、資訊詳情、詢底價、登錄、注冊、車貸&#xff0c;油耗、意見反饋、關于我們等等。這是一款非常全的汽車報價小程序模板…

MNIST 數據集 ubyte 格式介紹

train-images-idx1-ubyte 文件是用于存儲 MNIST 數據集中手寫數字圖像數據的文件。與標簽文件類似&#xff0c;這個文件使用的是一種簡單而緊湊的二進制格式。具體的文件格式如下&#xff1a; 文件頭&#xff08;Header&#xff09;&#xff1a; 文件頭部分包含了一些描述文件內…

Ubuntu 20版本安裝Redis教程,以及登陸

第一步 切換到root用戶&#xff0c;使用su命令&#xff0c;進行切換。 輸入&#xff1a; su - 第二步 使用apt命令來搜索redis的軟件包&#xff0c;輸入命令&#xff1a;apt search redis 第三步 選擇需要的redis版本進行安裝&#xff0c;本次選擇默認版本&#xff0c;redis5.…

Emacs 的優點及與 DE 的比較

一、引言 在編程領域&#xff0c;對于工具的選擇一直是開發者們熱議的話題。今天&#xff0c;我們來探討一下 Emacs 及其所具有的優點&#xff0c;并思考使用 Emacs 寫程序是否真的比使用集成開發環境&#xff08;IDE&#xff09;更方便。 二、Emacs 的優點 高度可定制性 可以…