最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解SpringBoot如何統(tǒng)一后端返回格式

 更新時(shí)間:2021年07月16日 12:04:30   作者:飄渺Jam的博客  
今天我們來(lái)聊一聊在基于SpringBoot前后端分離開(kāi)發(fā)模式下,如何友好的返回統(tǒng)一的標(biāo)準(zhǔn)格式以及如何優(yōu)雅的處理全局異常,感興趣的可以了解一下

今天我們來(lái)聊一聊在基于SpringBoot前后端分離開(kāi)發(fā)模式下,如何友好的返回統(tǒng)一的標(biāo)準(zhǔn)格式以及如何優(yōu)雅的處理全局異常。

首先我們來(lái)看看為什么要返回統(tǒng)一的標(biāo)準(zhǔn)格式?

為什么要對(duì)SpringBoot返回統(tǒng)一的標(biāo)準(zhǔn)格式

在默認(rèn)情況下,SpringBoot的返回格式常見(jiàn)的有三種:

第一種:返回 String

@GetMapping("/hello")
public String getStr(){
  return "hello,javadaily";
}

此時(shí)調(diào)用接口獲取到的返回值是這樣:

hello,javadaily

第二種:返回自定義對(duì)象

@GetMapping("/aniaml")
public Aniaml getAniaml(){
  Aniaml aniaml = new Aniaml(1,"pig");
  return aniaml;
}

此時(shí)調(diào)用接口獲取到的返回值是這樣:

{
  "id": 1,
  "name": "pig"
}

第三種:接口異常

@GetMapping("/error")
public int error(){
    int i = 9/0;
    return i;
}

此時(shí)調(diào)用接口獲取到的返回值是這樣:

{
  "timestamp": "2021-07-08T08:05:15.423+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "path": "/wrong"
}

基于以上種種情況,如果你和前端開(kāi)發(fā)人員聯(lián)調(diào)接口她們就會(huì)很懵逼,由于我們沒(méi)有給他一個(gè)統(tǒng)一的格式,前端人員不知道如何處理返回值。

還有甚者,有的同學(xué)比如小張喜歡對(duì)結(jié)果進(jìn)行封裝,他使用了Result對(duì)象,小王也喜歡對(duì)結(jié)果進(jìn)行包裝,但是他卻使用的是Response對(duì)象,當(dāng)出現(xiàn)這種情況時(shí)我相信前端人員一定會(huì)抓狂的。

所以我們項(xiàng)目中是需要定義一個(gè)統(tǒng)一的標(biāo)準(zhǔn)返回格式的。

定義返回標(biāo)準(zhǔn)格式

一個(gè)標(biāo)準(zhǔn)的返回格式至少包含3部分:

  • status 狀態(tài)值:由后端統(tǒng)一定義各種返回結(jié)果的狀態(tài)碼
  • message 描述:本次接口調(diào)用的結(jié)果描述
  • data 數(shù)據(jù):本次返回的數(shù)據(jù)
{
  "status":"100",
  "message":"操作成功",
  "data":"hello,javadaily"
}

當(dāng)然也可以按需加入其他擴(kuò)展值,比如我們就在返回對(duì)象中添加了接口調(diào)用時(shí)間

timestamp: 接口調(diào)用時(shí)間

定義返回對(duì)象

@Data
public class ResultData<t> {
  /** 結(jié)果狀態(tài) ,具體狀態(tài)碼參見(jiàn)ResultData.java*/
  private int status;
  private String message;
  private T data;
  private long timestamp ;


  public ResultData (){
    this.timestamp = System.currentTimeMillis();
  }


  public static <t> ResultData<t> success(T data) {
    ResultData<t> resultData = new ResultData<>();
    resultData.setStatus(ReturnCode.RC100.getCode());
    resultData.setMessage(ReturnCode.RC100.getMessage());
    resultData.setData(data);
    return resultData;
  }

  public static <t> ResultData<t> fail(int code, String message) {
    ResultData<t> resultData = new ResultData<>();
    resultData.setStatus(code);
    resultData.setMessage(message);
    return resultData;
  }

}

定義狀態(tài)碼

public enum ReturnCode {
    /**操作成功**/
    RC100(100,"操作成功"),
    /**操作失敗**/
    RC999(999,"操作失敗"),
    /**服務(wù)限流**/
    RC200(200,"服務(wù)開(kāi)啟限流保護(hù),請(qǐng)稍后再試!"),
    /**服務(wù)降級(jí)**/
    RC201(201,"服務(wù)開(kāi)啟降級(jí)保護(hù),請(qǐng)稍后再試!"),
    /**熱點(diǎn)參數(shù)限流**/
    RC202(202,"熱點(diǎn)參數(shù)限流,請(qǐng)稍后再試!"),
    /**系統(tǒng)規(guī)則不滿足**/
    RC203(203,"系統(tǒng)規(guī)則不滿足要求,請(qǐng)稍后再試!"),
    /**授權(quán)規(guī)則不通過(guò)**/
    RC204(204,"授權(quán)規(guī)則不通過(guò),請(qǐng)稍后再試!"),
    /**access_denied**/
    RC403(403,"無(wú)訪問(wèn)權(quán)限,請(qǐng)聯(lián)系管理員授予權(quán)限"),
    /**access_denied**/
    RC401(401,"匿名用戶訪問(wèn)無(wú)權(quán)限資源時(shí)的異常"),
    /**服務(wù)異常**/
    RC500(500,"系統(tǒng)異常,請(qǐng)稍后重試"),

    INVALID_TOKEN(2001,"訪問(wèn)令牌不合法"),
    ACCESS_DENIED(2003,"沒(méi)有權(quán)限訪問(wèn)該資源"),
    CLIENT_AUTHENTICATION_FAILED(1001,"客戶端認(rèn)證失敗"),
    USERNAME_OR_PASSWORD_ERROR(1002,"用戶名或密碼錯(cuò)誤"),
    UNSUPPORTED_GRANT_TYPE(1003, "不支持的認(rèn)證模式");



    /**自定義狀態(tài)碼**/
    private final int code;
    /**自定義描述**/
    private final String message;

    ReturnCode(int code, String message){
        this.code = code;
        this.message = message;
    }


    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

統(tǒng)一返回格式

@GetMapping("/hello")
public ResultData<string> getStr(){
	return ResultData.success("hello,javadaily");
}

此時(shí)調(diào)用接口獲取到的返回值是這樣:

{
  "status": 100,
  "message": "hello,javadaily",
  "data": null,
  "timestamp": 1625736481648,
  "httpStatus": 0
}

這樣確實(shí)已經(jīng)實(shí)現(xiàn)了我們想要的結(jié)果,我在很多項(xiàng)目中看到的都是這種寫(xiě)法,在Controller層通過(guò)ResultData.success()對(duì)返回結(jié)果進(jìn)行包裝后返回給前端。

看到這里我們不妨停下來(lái)想想,這樣做有什么弊端呢?

最大的弊端就是我們后面每寫(xiě)一個(gè)接口都需要調(diào)用ResultData.success()這行代碼對(duì)結(jié)果進(jìn)行包裝,重復(fù)勞動(dòng),浪費(fèi)體力;而且還很容易被其他老鳥(niǎo)給嘲笑。

所以呢我們需要對(duì)代碼進(jìn)行優(yōu)化,目標(biāo)就是不要每個(gè)接口都手工制定ResultData返回值。

高級(jí)實(shí)現(xiàn)方式

要優(yōu)化這段代碼很簡(jiǎn)單,我們只需要借助SpringBoot提供的ResponseBodyAdvice即可。

ResponseBodyAdvice的作用:攔截Controller方法的返回值,統(tǒng)一處理返回值/響應(yīng)體,一般用來(lái)統(tǒng)一返回格式,加解密,簽名等等。

先來(lái)看下ResponseBodyAdvice的源碼:

public interface ResponseBodyAdvice<t> {
		/**
		* 是否支持advice功能
		* true 支持,false 不支持
		*/
    boolean supports(MethodParameter var1, Class<!--? extends HttpMessageConverter<?-->> var2);

	  /**
		* 對(duì)返回的數(shù)據(jù)進(jìn)行處理
		*/
    @Nullable
    T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<!--? extends HttpMessageConverter<?-->> var4, ServerHttpRequest var5, ServerHttpResponse var6);
}

我們只需要編寫(xiě)一個(gè)具體實(shí)現(xiàn)類即可

/**
 * @author jam
 * @date 2021/7/8 10:10 上午
 */
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<object> {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter methodParameter, Class<!--? extends HttpMessageConverter<?-->> aClass) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<!--? extends HttpMessageConverter<?-->> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if(o instanceof String){
            return objectMapper.writeValueAsString(ResultData.success(o));
        }        
        return ResultData.success(o);
    }
}

需要注意兩個(gè)地方:

@RestControllerAdvice注解

@RestControllerAdvice@RestController注解的增強(qiáng),可以實(shí)現(xiàn)三個(gè)方面的功能:

  • 全局異常處理
  • 全局?jǐn)?shù)據(jù)綁定全
  • 局?jǐn)?shù)據(jù)預(yù)處理

String類型判斷

if(o instanceof String){
  return objectMapper.writeValueAsString(ResultData.success(o));
} 

這段代碼一定要加,如果Controller直接返回String的話,SpringBoot是直接返回,故我們需要手動(dòng)轉(zhuǎn)換成json。

經(jīng)過(guò)上面的處理我們就再也不需要通過(guò)ResultData.success()來(lái)進(jìn)行轉(zhuǎn)換了,直接返回原始數(shù)據(jù)格式,SpringBoot自動(dòng)幫我們實(shí)現(xiàn)包裝類的封裝。

@GetMapping("/hello")
public String getStr(){
    return "hello,javadaily";
}

此時(shí)我們調(diào)用接口返回的數(shù)據(jù)結(jié)果為:

@GetMapping("/hello")
public String getStr(){
  return "hello,javadaily";
}

是不是感覺(jué)很完美,別急,還有個(gè)問(wèn)題在等著你呢。

接口異常問(wèn)題

此時(shí)有個(gè)問(wèn)題,由于我們沒(méi)對(duì)Controller的異常進(jìn)行處理,當(dāng)我們調(diào)用的方法一旦出現(xiàn)異常,就會(huì)出現(xiàn)問(wèn)題,比如下面這個(gè)接口

@GetMapping("/wrong")
public int error(){
    int i = 9/0;
    return i;
}

返回的結(jié)果為:

這顯然不是我們想要的結(jié)果,接口都報(bào)錯(cuò)了還返回操作成功的響應(yīng)碼,前端看了會(huì)打人的。

別急,接下來(lái)我們進(jìn)入第二個(gè)議題,如何優(yōu)雅的處理全局異常。

SpringBoot為什么需要全局異常處理器

不用手寫(xiě)try...catch,由全局異常處理器統(tǒng)一捕獲

使用全局異常處理器最大的便利就是程序員在寫(xiě)代碼時(shí)不再需要手寫(xiě)try...catch了,前面我們講過(guò),默認(rèn)情況下SpringBoot出現(xiàn)異常時(shí)返回的結(jié)果是這樣:

{
  "timestamp": "2021-07-08T08:05:15.423+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "path": "/wrong"
}

這種數(shù)據(jù)格式返回給前端,前端是看不懂的,所以這時(shí)候我們一般通過(guò)try...catch來(lái)處理異常

@GetMapping("/wrong")
public int error(){
    int i;
    try{
        i = 9/0;
    }catch (Exception e){
        log.error("error:{}",e);
        i = 0;
    }
    return i;
}

我們追求的目標(biāo)肯定是不需要再手動(dòng)寫(xiě)try...catch了,而是希望由全局異常處理器處理。

對(duì)于自定義異常,只能通過(guò)全局異常處理器來(lái)處理

@GetMapping("error1")
public void empty(){
	throw  new RuntimeException("自定義異常");
}

當(dāng)我們引入Validator參數(shù)校驗(yàn)器的時(shí)候,參數(shù)校驗(yàn)不通過(guò)會(huì)拋出異常,此時(shí)是無(wú)法用try...catch捕獲的,只能使用全局異常處理器。

SpringBoot集成參數(shù)校驗(yàn)請(qǐng)參考這篇文章SpringBoot開(kāi)發(fā)秘籍 - 集成參數(shù)校驗(yàn)及高階技巧

如何實(shí)現(xiàn)全局異常處理器

@Slf4j
@RestControllerAdvice
public class RestExceptionHandler {
    /**
     * 默認(rèn)全局異常處理。
     * @param e the e
     * @return ResultData
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultData<string> exception(Exception e) {
        log.error("全局異常信息 ex={}", e.getMessage(), e);
        return ResultData.fail(ReturnCode.RC500.getCode(),e.getMessage());
    }

}

有三個(gè)細(xì)節(jié)需要說(shuō)明一下:

  • @RestControllerAdvice,RestController的增強(qiáng)類,可用于實(shí)現(xiàn)全局異常處理器
  • @ExceptionHandler,統(tǒng)一處理某一類異常,從而減少代碼重復(fù)率和復(fù)雜度,比如要獲取自定義異??梢?code>@ExceptionHandler(BusinessException.class)
  • @ResponseStatus指定客戶端收到的http狀態(tài)碼

體驗(yàn)效果

這時(shí)候我們調(diào)用如下接口:

@GetMapping("error1")
public void empty(){
    throw  new RuntimeException("自定義異常");
}

返回的結(jié)果如下:

{
  "status": 500,
  "message": "自定義異常",
  "data": null,
  "timestamp": 1625795902556
}

基本滿足我們的需求了。

但是當(dāng)我們同時(shí)啟用統(tǒng)一標(biāo)準(zhǔn)格式封裝功能ResponseAdviceRestExceptionHandler全局異常處理器時(shí)又出現(xiàn)了新的問(wèn)題:

{
  "status": 100,
  "message": "操作成功",
  "data": {
    "status": 500,
    "message": "自定義異常",
    "data": null,
    "timestamp": 1625796167986
  },
  "timestamp": 1625796168008
}

此時(shí)返回的結(jié)果是這樣,統(tǒng)一格式增強(qiáng)功能會(huì)給返回的異常結(jié)果再次封裝,所以接下來(lái)我們需要解決這個(gè)問(wèn)題。

全局異常接入返回的標(biāo)準(zhǔn)格式

要讓全局異常接入標(biāo)準(zhǔn)格式很簡(jiǎn)單,因?yàn)槿之惓L幚砥饕呀?jīng)幫我們封裝好了標(biāo)準(zhǔn)格式,我們只需要直接返回給客戶端即可。

@SneakyThrows
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<!--? extends HttpMessageConverter<?-->> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
  if(o instanceof String){
    return objectMapper.writeValueAsString(ResultData.success(o));
  }
  if(o instanceof ResultData){
    return o;
  }
  return ResultData.success(o);
}

關(guān)鍵代碼:

if(o instanceof ResultData){
  return o;
}

如果返回的結(jié)果是ResultData對(duì)象,直接返回即可。

這時(shí)候我們?cè)僬{(diào)用上面的錯(cuò)誤方法,返回的結(jié)果就符合我們的要求了。

{
  "status": 500,
  "message": "自定義異常",
  "data": null,
  "timestamp": 1625796580778
}

好了,今天的文章就到這里了,希望通過(guò)這篇文章你能掌握如何在你項(xiàng)目中友好實(shí)現(xiàn)統(tǒng)一標(biāo)準(zhǔn)格式到返回并且可以優(yōu)雅的處理全局異常。

github地址:https://github.com/jianzh5/cloud-blog/

到此這篇關(guān)于詳解SpringBoot如何統(tǒng)一后端返回格式的文章就介紹到這了,更多相關(guān)SpringBoot 統(tǒng)一后端返回格式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Java多線程編程中線程的啟動(dòng)、中斷或終止操作

    詳解Java多線程編程中線程的啟動(dòng)、中斷或終止操作

    在Java中start和tun方法可用被用來(lái)啟動(dòng)線程,而用interrupt方法來(lái)中斷或終止線程,以下我們就來(lái)詳解Java多線程編程中線程的啟動(dòng)、中斷或終止操作
    2016-07-07
  • java如何實(shí)現(xiàn)post請(qǐng)求webservice服務(wù)端

    java如何實(shí)現(xiàn)post請(qǐng)求webservice服務(wù)端

    這篇文章主要介紹了java如何實(shí)現(xiàn)post請(qǐng)求webservice服務(wù)端,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • JavaEE7+Websockets+GlassFish4打造聊天室

    JavaEE7+Websockets+GlassFish4打造聊天室

    Java EE 7已經(jīng)發(fā)布很久了,新增加了很多新的功能和特性,如新增或更新了不少的JSR標(biāo)準(zhǔn)。其中特別受到關(guān)注的是Websockets。它的一個(gè)好處之一是減少了不必要的網(wǎng)絡(luò)流量。它主要是用于在客戶機(jī)和服務(wù)器之間建立單一的雙向連接。
    2015-09-09
  • java之swing表格實(shí)現(xiàn)方法

    java之swing表格實(shí)現(xiàn)方法

    這篇文章主要介紹了java之swing表格實(shí)現(xiàn)方法,以實(shí)例形式分析了swing構(gòu)建表格的方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • java實(shí)現(xiàn)的AES加密算法完整實(shí)例

    java實(shí)現(xiàn)的AES加密算法完整實(shí)例

    這篇文章主要介紹了java實(shí)現(xiàn)的AES加密算法,結(jié)合完整實(shí)例形式分析了AES加密類的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2016-07-07
  • java的三種隨機(jī)數(shù)生成方式

    java的三種隨機(jī)數(shù)生成方式

    主要介紹了java的三種隨機(jī)數(shù)生成方式的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下
    2021-07-07
  • Java 讀取圖片的mimeType的方法

    Java 讀取圖片的mimeType的方法

    本篇文章主要介紹了Java 讀取圖片的mimeType的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • Linux將Spring Boot項(xiàng)目的Jar包注冊(cè)為開(kāi)機(jī)自啟動(dòng)系統(tǒng)服務(wù)的操作方法

    Linux將Spring Boot項(xiàng)目的Jar包注冊(cè)為開(kāi)機(jī)自啟動(dòng)系統(tǒng)服務(wù)的操作方法

    jar文件是從maven package打包出來(lái)的,config/application.yml是原先在項(xiàng)目的resources文件夾里,外置出來(lái)方便適配開(kāi)發(fā)環(huán)境和正式環(huán)境,這篇文章主要介紹了Linux將Spring Boot項(xiàng)目的Jar包注冊(cè)為開(kāi)機(jī)自啟動(dòng)系統(tǒng)服務(wù)的操作方法,需要的朋友可以參考下
    2023-10-10
  • Springboot使用異步方法優(yōu)化Service邏輯,提高接口響應(yīng)速度方式

    Springboot使用異步方法優(yōu)化Service邏輯,提高接口響應(yīng)速度方式

    這篇文章主要介紹了Springboot使用異步方法優(yōu)化Service邏輯,提高接口響應(yīng)速度方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • spring-boot-maven-plugin?配置有啥用

    spring-boot-maven-plugin?配置有啥用

    這篇文章主要介紹了spring-boot-maven-plugin?配置是干啥的,這個(gè)是SpringBoot的Maven插件,主要用來(lái)打包的,通常打包成jar或者war文件,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08

最新評(píng)論

图木舒克市| 甘肃省| 易门县| 岱山县| 清徐县| 宜都市| 孟津县| 五河县| 松滋市| 如东县| 丰原市| 新乐市| 商洛市| 高尔夫| 曲松县| 蓝山县| 安福县| 巴里| 芜湖县| 鹰潭市| 南城县| 红桥区| 高雄县| 保康县| 买车| 津市市| 闽清县| 墨玉县| 泾阳县| 玉屏| 台江县| 达拉特旗| 邵东县| 桂平市| 财经| 张家港市| 彩票| 铜陵市| 大厂| 宜昌市| 商都县|