從零開始搭建企業管理系統(五):統一響應結果和全局異常處理

統一響應結果和全局異常處理

    • 前言
    • 統一響應結果
      • 定義響應結構
      • 定義響應對象
      • 定義響應狀態對象
      • 統一返回響應對象
        • 定義 Controller 攔截類
    • 全局異常處理

前言

做個功能之前我們想一下為什么要做統一響應結果和全局異常處理呢?

這是因為我們的項目采用的是前后端分離開發,并且使用 Restful 風格的接口形式,返回的業務數據以及異常都是以 JSON 的格式返回,但是目前我們返回的數據格式都是不一致的,比如如下的代碼:

@GetMapping("/{id}")
@Operation(summary = "根據用戶ID查詢用戶")
public UserEntity get(@PathVariable Long id) {return userService.get(id);
}@GetMapping("/list")
@Operation(summary = "查詢全部")
public List<UserEntity> lists() {return userService.lists();
}@GetMapping("/page")
@Operation(summary = "分頁查詢")
public Page<UserEntity> page(int page, int size) {return userService.page(PageRequest.of(page - 1, size));
}@PostMapping
@Operation(summary = "新增用戶")
public void save(@RequestBody UserEntity user) {userService.save(user);
}

由上面的接口返回值可以看出,我們4個接口就有4中返回數據格式,這時前端工作人員在對接的時候就接口她們就會很難受,需要寫很多的處理來處理不同的返回數據,非常的不方便,而且我們自己使用起來也不好管理,所以在實際開發中,為了降低開發人員之間的溝通成本,一般返回結果會定義成一個統一格式,具體的格式根據實際開發業務不同有所區別。

統一響應結果

定義響應結構

從前端的角度思考,調用后端接口后如果成功了則拿到想要的數據或者對應的信息提示,如果不成功也應該有相應的信息提示。從這個角度出發,我們將響應結果設計為:

code:響應狀態碼,根據狀態碼判斷是否成功

message:提示信息,根據調用的接口自定義

body:返回的數據

響應的結構如下:

{"code": 200,"message": "成功","body": [{"createUser": "admin","updateUser": "admin","createTime": "2023-12-09 14:14:20","updateTime": "2023-12-09 14:14:20","id": 2,"name": "金克斯","nickname": "爆爆","age": 18,"email": null,"password": "666"}]
}

定義響應對象

Spring 官方源碼里面也有一個類似的對象,類名為 ResponseEntity,如果你想直接用這個類也行的,但是這個類我使用起來有點不合我的習慣,所以還是自定義一個好點。

@Data
public class Response<T> implements Serializable {@Schema(description = "響應狀態")private Integer code;@Schema(description = "響應信息")private String message;@Schema(description = "響應數據")private T body;/*** 私有化構造函數*/private Response() {}/*** 構建 Response<T> 對象** @param body 響應數據* @param code 響應狀態對象*/public static <T> Response<T> build(@Nullable T body, ResponseCode code) {return build(body, code.getCode(), code.getMessage());}/*** 構建 Response<T> 對象** @param body    響應數據* @param code    響應狀態* @param message 響應信息*/public static <T> Response<T> build(@Nullable T body, Integer code, String message) {Response<T> response = new Response<>();response.setCode(code);response.setMessage(message);response.setBody(body);return response;}/*** 請求成功返回不帶數據*/public static <T> Response<T> ok() {return build(null, ResponseCode.OK);}/*** 請求成功返回帶數據*/public static <T> Response<T> ok(T body) {return build(body, ResponseCode.OK);}/*** 請求失敗返回500*/public static <T> Response<T> error() {return build(null, ResponseCode.ERROR);}/*** 請求失敗狀態碼*/public static <T> Response<T> error(ResponseCode code) {return build(null, code);}/*** 自定義請求失敗信息*/public static <T> Response<T> error(Integer code, String message) {return build(null, code, message);}}

定義響應狀態對象

public enum ResponseCode {/*** 成功*/OK(200, "OK"),/*** 失敗*/ERROR(500,"系統異常,請稍后重試");private final int code;private final String message;ResponseCode(int code, String message) {this.code = code;this.message = message;}public int getCode() {return code;}public String getMessage() {return message;}
}

統一返回響應對象

這里有兩種方式可以做到全局返回我們定義好的 Response 對象,第一種就是在定義方法的時候返回值都定義為 Resopnse 對象,這是可以完成的,但是我們還有更高級一點的用法,使用 Spring ResponseBodyAdvice類即可,ResponseBodyAdvice的作用就是攔截Controller方法的返回值,統一處理返回值/響應體,一般用來統一返回格式,加解密,簽名等等。

我們這里采用第二種方式來完成統一響應結果。

定義 Controller 攔截類
@RestControllerAdvice(basePackages = {"com.xm.module"})
public class ResponseAdviceHandler implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {// 響應都需要被攔截return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof String) {// String 類型不能封裝成 Response 返回return JSONUtil.toJsonStr(Response.ok(body));} else if (body instanceof Response) {// 如果是 Response 就直接返回了return body;} else {// 統一響應對象return Response.ok(body);}}
}

@RestControllerAdvice@RestController注解的增強,可以實現三個方面的功能:

  1. 全局異常處理
  2. 全局數據綁定
  3. 全局數據預處理

basePackages 表示是只攔截 com.xm.module 下的響應

JSONUtil.toJsonStr(Response.ok(body));這個 JSONUtil 我是用的 hutool 工具包里面的,好用滴很。有需要的自己導入。Hutool 官網

啟動程序測試一下

在這里插入圖片描述

這里報了一個異常,而且我們發現異常信息的響應結果和我們預想的不一致,這個我們在下面全局異常攔截的時候在進行處理,先把這個接口異常給解決一下。

異常的大致意思是不能初始化代理,沒有 session,這是因為我們使用了懶加載的方法,修改一下這個接口方法。

@Override
public UserEntity get(Long id) {return userRepository.findById(id).orElse(null);
}

在這里插入圖片描述

ok,接口請求成功,并且響應的結構和我們預想的一致,nice。

全局異常處理

剛才我們看到,在接口拋出異常的時候,我們的響應結構和我們預想的不一致,我們希望在拋出異常的時候也能返回我們的 Response 對象,怎么實現呢,還是老辦法,上面我們提到了 @RestControllerAdvice@RestController注解的增強,可以實現全局異常處理,那我們就在使用這個注解進行全局異常的攔截處理吧。

@RestControllerAdvice
public class GlobalExceptionHandler {/*** 全局異常處理。* ExceptionHandler(Exception.class) 表示攔截 Exception 異常,這是所有異常都攔截* 我們還可以配置 ExceptionHandler(XXException.class) 等等進行更加精細的攔截操作** @param e 異常* @return Response*/@ExceptionHandler(Exception.class)public Response<String> exception(Exception e) {return Response.error(ResponseCode.ERROR);}}

編寫好了這個之后,我們再來測試一下異常的情況下返回值是什么樣的,

在這里插入圖片描述

ok,異常攔截成功,關于更多的異常,可以自己定義攔截即可,本節內容就到這里了,bye~

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

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

相關文章

【C++】小項目:C++實現通訊錄管理系統

文章目錄 1、系統需求完整代碼 1、系統需求 本文將利用C來實現一個通訊錄管理系統 系統中需要實現的功能如下&#xff1a; 添加聯系人&#xff1a;向通訊錄中添加新人&#xff0c;信息包括&#xff08;姓名、性別、年齡、聯系電話、家庭住址&#xff09;最多記錄1000人顯示聯…

LVGL | Demo實例使用說明

LVGL | Demo實例使用說明 時間&#xff1a;2023年12月10日21:51:17 文章目錄 LVGL | Demo實例使用說明Demos for LVGLAdd the examples to your projectsDemosWidgetsMusic playerKeypad and encoderBenchmarkStress Contributing Demos for LVGL Add the examples to your p…

基于SSM的酒店管理旅店系統(Java畢業設計)

大家好&#xff0c;我是DeBug&#xff0c;很高興你能來閱讀&#xff01;作為一名熱愛編程的程序員&#xff0c;我希望通過這些教學筆記與大家分享我的編程經驗和知識。在這里&#xff0c;我將會結合實際項目經驗&#xff0c;分享編程技巧、最佳實踐以及解決問題的方法。無論你是…

華為OD機試真題-密碼輸入檢測-2023年OD統一考試(C卷)

題目描述&#xff1a; 給定用戶密碼輸入流input&#xff0c;輸入流中字符<表示退格&#xff0c;可以清除前一個輸入的字符&#xff0c;請你編寫程序&#xff0c;輸出最終得到的密碼字符&#xff0c;并判斷密碼是否滿足如下的密碼安全要求。 密碼安全要求如下&#xff1a; …

【軟件測試】年薪30萬跟年薪15萬的面試有些什么區別?

1、什么是兼容性測試&#xff1f;兼容性測試側重哪些方面&#xff1f; 參考答案&#xff1a; 兼容測試主要是檢查軟件在不同的硬件平臺、軟件平臺上是否可以正常的運行&#xff0c;即是通常說的軟件的可移植性。 兼容的類型&#xff0c;如果細分的話&#xff0c;有平臺的兼容…

7記一次組網過程

這段時間學習了服務器、操作系統、網絡相關的知識&#xff0c;后面真的進行了一次組網操作。這次把組網的過程記錄下來&#xff0c;方便下次操作的時候有資料可查詢。 前期準備 要組網&#xff0c;首先要做好規劃&#xff0c;把前期要準備的事情提前做好&#xff0c;才能事半…

Numpy數組中數據的排序【sort(),argsort()與 lexsort()】 (第13講)

Numpy數組中數據的排序【sort(),argsort()與 lexsort()】 (第13講) ??????? ??博主 侯小啾 感謝您的支持與信賴。?? ???????????????????????????????????????????????????????????????????…

【C++ 程序設計入門基礎】- 第3節-循環結構02

目錄 while 語句 案例 while 循環 輸入一個整數 n &#xff0c;輸出 1~n 的所有整數。 查看運行結果&#xff1a; while 語句結構解析 do while 語句 案例 do while 循環 輸入一個整數n&#xff0c;輸出1&#xff5e;n的所有整數。 查看運行結果 while、do while的區別 …

AE-制作唯美星空粒子動態視頻

目錄 1.新建合成 2.導入一張星空圖片,拖入到新建的合成中 3.新建純色層面命名為bj

【git 相關操作】

git status - 查看當前狀態 git add - 將文件添加到暫存區 git commit -m "msg" - 提交暫存區文件到本地倉庫 git push origin master - 本地倉庫文件推送到遠程倉庫 git merge - 合并分支 git clone - 從指定地址克隆項目 git log - 查看commit日志 git stash push …

SpringSecurity6 | 登錄成功后的JSON處理

?作者簡介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;熱愛Java后端開發者&#xff0c;一個想要與大家共同進步的男人&#x1f609;&#x1f609; &#x1f34e;個人主頁&#xff1a;Leo的博客 &#x1f49e;當前專欄&#xff1a; Java從入門到精通 ?特色專欄&#xf…

Android:java.lang.SecurityException: Provider must not be exporte

java.lang.SecurityException: Provider must not be exporte 解決方案 首先在AndroidManifest.xml中添加provider android:authorities&#xff1a; 是用來標識provider的唯一標識&#xff0c;在同一部手機上一個"authority"串只能被一個app使用&#xff0c;沖突的話…

架構LNMP

目錄 1.安裝Nginx服務 2.安裝 MySQL 服務 3.安裝配置 PHP 解析環境 4.部署 Discuz&#xff01;社區論壇 Web 應用 1.安裝Nginx服務 實驗準備 systemctl stop firewalld systemctl disable firewalld setenforce 0 安裝依賴包 yum -y install pcre-devel zlib-devel gcc…

JavaScript中的async await基本使用

目錄 1. async await是什么2. 為什么會出現3. 需要怎么去使用4. 最終的結果解決什么5. 使用的注意點6. 常用的技巧 JavaScript中的async await是一種處理異步代碼的語法糖&#xff0c;它可以讓我們更加方便地處理異步操作&#xff0c;避免了回調地獄和Promise鏈式調用的問題。 …

Jmeter測試實踐:文件下載接口

一 Jmeter步驟 1.打開jmeter4.0&#xff0c;新建測試計劃&#xff0c;添加線程組。根據實際情況配置線程屬性。 2.添加HTTP請求。根據接口文檔進行配置。 Basic部分修改如下&#xff0c;Advanced部分保持默認。這里的參數id是文件的id&#xff0c;我進行了參數化&#xff0c…

vue基本運用之常見問題及案例代碼

前言 一些 Vue.js 的常見問題以及對應的案例代碼 Vue實例生命周期 問題描述:如何在Vue的生命周期鉤子中調用其他鉤子?案例代碼: new Vue({el: #app, created: function () {console.log(m

Redis哨兵(sentinel)

文章目錄 簡介搭建框架具體步驟主要文件參數開始配置 案例分析原有的master掛了 哨兵運行流程和選舉原理主觀下線客觀下線(Objectively Down)選舉出領導者哨兵(哨兵中選出兵王) 選新的master使用建議 簡介 將某一個從庫轉換為新主庫&#xff0c;繼續對外服務將某一個從庫轉換為…

?operator --- 標準運算符替代函數?

源代碼: Lib/operator.py operator 模塊提供了一套與Python的內置運算符對應的高效率函數。例如&#xff0c;operator.add(x, y) 與表達式 xy 相同。 許多函數名與特殊方法名相同&#xff0c;只是沒有雙下劃線。為了向后兼容性&#xff0c;也保留了許多包含雙下劃線的函數。為了…

Java - HashMap

數組和鏈表 數組&#xff1a; 存儲區間是連續&#xff0c;且占用內存嚴重&#xff0c;空間復雜也很大&#xff0c;時間復雜為O&#xff08;1&#xff09; 優點&#xff1a;是隨機讀取效率很高&#xff0c;原因數組是連續&#xff08;隨機訪問性強&#xff0c;查找速度快&#x…

properties配置和讀取

如何配置和讀取屬性文件 1.屬性文件介紹1.1 什么是屬性文件1.2屬性文件規范1.3 屬性文件優缺點 2.屬性文件讀取4.spring和屬性文件4.1利用注解讀取4.2配置文件里直接引用 4.屬性文件寫入5.注意事項5.總結 1.屬性文件介紹 1.1 什么是屬性文件 Java開發中&#xff0c;我們經常需…