在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

SpringBoot 后端接口規(guī)范(中)

jf_78858299 ? 來源:架構(gòu)師 ? 作者:架構(gòu)師 ? 2023-05-05 17:01 ? 次閱讀

四、全局異常處理

參數(shù)校驗失敗會自動引發(fā)異常,我們當然不可能再去手動捕捉異常進行處理。但我們又不想手動捕捉這個異常,又要對這個異常進行處理,那正好使用[SpringBoot]全局異常處理來達到一勞永逸的效果!

1、基本使用

首先,我們需要新建一個類,在這個類上加上@ControllerAdvice@RestControllerAdvice注解,這個類就配置成全局處理類了。

這個根據(jù)你的Controller層用的是@Controller還是@RestController來決定。

然后在類中新建方法,在方法上加上@ExceptionHandler注解并指定你想處理的異常類型,接著在方法內(nèi)編寫對該異常的操作邏輯,就完成了對該異常的全局處理!我們現(xiàn)在就來演示一下對參數(shù)校驗失敗拋出的MethodArgumentNotValidException全局處理:

package com.csdn.demo1.global;

import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
@ResponseBody
public class ExceptionControllerAdvice {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        // 從異常對象中拿到ObjectError對象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 然后提取錯誤提示信息進行返回
        return objectError.getDefaultMessage();
    }
    
     /**
     * 系統(tǒng)異常 預(yù)期以外異常
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultVO? handleUnexpectedServer(Exception ex) {
        log.error("系統(tǒng)異常:", ex);
        // GlobalMsgEnum.ERROR是我自己定義的枚舉類
        return new ResultVO<>(GlobalMsgEnum.ERROR);
    }

    /**
     * 所以異常的攔截
     */
    @ExceptionHandler(Throwable.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultVO? exception(Throwable ex) {
        log.error("系統(tǒng)異常:", ex);
        return new ResultVO<>(GlobalMsgEnum.ERROR);
    }
}

我們再次進行測試,這次返回的就是我們制定的錯誤提示信息!我們通過全局異常處理優(yōu)雅的實現(xiàn)了我們想要的功能!

以后我們再想寫接口參數(shù)校驗,就只需要在入?yún)⒌某蓡T變量上加上Validator校驗規(guī)則注解,然后在參數(shù)上加上@Valid注解即可完成校驗,校驗失敗會自動返回錯誤提示信息,無需任何其他代碼!

2、自定義異常

在很多情況下,我們需要手動拋出異常,比如在業(yè)務(wù)層當有些條件并不符合業(yè)務(wù)邏輯,而使用自定義異常有諸多優(yōu)點:

  • 自定義異常可以攜帶更多的信息,不像這樣只能攜帶一個字符串。
  • 項目開發(fā)中經(jīng)常是很多人負責(zé)不同的模塊,使用自定義異常可以統(tǒng)一了對外異常展示的方式。
  • 自定義異常語義更加清晰明了,一看就知道是項目中手動拋出的異常。

我們現(xiàn)在就來開始寫一個自定義異常:

package com.csdn.demo1.global;

import lombok.Getter;

@Getter //只要getter方法,無需setter
public class APIException extends RuntimeException {
    private int code;
    private String msg;

    public APIException() {
        this(1001, "接口錯誤");
    }

    public APIException(String msg) {
        this(1001, msg);
    }

    public APIException(int code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }
}

然后在剛才的全局異常類中加入如下:

//自定義的全局異常
  @ExceptionHandler(APIException.class)
  public String APIExceptionHandler(APIException e) {
      return e.getMsg();
  }

這樣就對異常的處理就比較規(guī)范了,當然還可以添加對Exception的處理,這樣無論發(fā)生什么異常我們都能屏蔽掉然后響應(yīng)數(shù)據(jù)給前端,不過建議最后項目上線時這樣做,能夠屏蔽掉錯誤信息暴露給前端,在開發(fā)中為了方便調(diào)試還是不要這樣做。

另外,當我們拋出自定義異常的時候全局異常處理只響應(yīng)了異常中的錯誤信息msg給前端,并沒有將錯誤代碼code返回。這還需要配合數(shù)據(jù)統(tǒng)一響應(yīng)。

如果在多模塊使用,全局異常等公共功能抽象成子模塊,則在需要的子模塊中需要將該模塊包掃描加入,@SpringBootApplication(scanBasePackages = {"com.xxx"})

五、數(shù)據(jù)統(tǒng)一響應(yīng)

統(tǒng)一數(shù)據(jù)響應(yīng)是我們自己自定義一個響應(yīng)體類,無論后臺是運行正常還是發(fā)生異常,響應(yīng)給前端的數(shù)據(jù)格式是不變的!這里我包括了響應(yīng)信息代碼code和響應(yīng)信息說明msg,首先可以設(shè)置一個枚舉規(guī)范響應(yīng)體中的響應(yīng)碼和響應(yīng)信息。

@Getter
public enum ResultCode {
    SUCCESS(1000, "操作成功"),
    FAILED(1001, "響應(yīng)失敗"),
    VALIDATE_FAILED(1002, "參數(shù)校驗失敗"),
    ERROR(5000, "未知錯誤");
    private int code;
    private String msg;
    ResultCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

自定義響應(yīng)體

package com.csdn.demo1.global;

import lombok.Getter;

@Getter
public class ResultVO<T> {
    /**
     * 狀態(tài)碼,比如1000代表響應(yīng)成功
     */
    private int code;
    /**
     * 響應(yīng)信息,用來說明響應(yīng)情況
     */
    private String msg;
    /**
     * 響應(yīng)的具體數(shù)據(jù)
     */
    private T data;
    
    public ResultVO(T data) {
        this(ResultCode.SUCCESS, data);
    }

    public ResultVO(ResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.data = data;
    }
}

最后需要修改全局異常處理類的返回類型

@RestControllerAdvice
public class ExceptionControllerAdvice {

    @ExceptionHandler(APIException.class)
    public ResultVO<String> APIExceptionHandler(APIException e) {
        // 注意哦,這里傳遞的響應(yīng)碼枚舉
        return new ResultVO<>(ResultCode.FAILED, e.getMsg());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<String> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 注意哦,這里傳遞的響應(yīng)碼枚舉
        return new ResultVO<>(ResultCode.VALIDATE_FAILED, objectError.getDefaultMessage());
    }
}

最后在controller層進行接口信息數(shù)據(jù)的返回

@GetMapping("/getUser")
public ResultVO<User> getUser() {
    User user = new User();
    user.setId(1L);
    user.setAccount("12345678");
    user.setPassword("12345678");
    user.setEmail("123@qq.com");

    return new ResultVO<>(user);
}

經(jīng)過測試,這樣響應(yīng)碼和響應(yīng)信息只能是枚舉規(guī)定的那幾個,就真正做到了響應(yīng)數(shù)據(jù)格式、響應(yīng)碼和響應(yīng)信息規(guī)范化、統(tǒng)一化!

還有一種全局返回類如下

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Msg {
    //狀態(tài)碼
    private int code;
    //提示信息
    private String msg;
    //用戶返回給瀏覽器的數(shù)據(jù)
    private Map<String,Object> data = new HashMap<>();

    public static Msg success() {
        Msg result = new Msg();
        result.setCode(200);
        result.setMsg("請求成功!");
        return result;
    }

    public static Msg fail() {
        Msg result = new Msg();
        result.setCode(400);
        result.setMsg("請求失敗!");
        return result;
    }

    public static Msg fail(String msg) {
        Msg result = new Msg();
        result.setCode(400);
        result.setMsg(msg);
        return result;
    }

    public Msg(ReturnResult returnResult){
        code = returnResult.getCode();
        msg = returnResult.getMsg();
    }

    public Msg add(String key,Object value) {
        this.getData().put(key, value);
        return this;
    }
}

六、全局處理響應(yīng)數(shù)據(jù)(可選擇)

接口返回統(tǒng)一響應(yīng)體 + 異常也返回統(tǒng)一響應(yīng)體,其實這樣已經(jīng)很好了,但還是有可以優(yōu)化的地方。要知道一個項目下來定義的接口搞個幾百個太正常不過了,要是每一個接口返回數(shù)據(jù)時都要用響應(yīng)體來包裝一下好像有點麻煩,有沒有辦法省去這個包裝過程呢?

當然是有的,還是要用到全局處理。但是為了擴展性,就是允許繞過數(shù)據(jù)統(tǒng)一響應(yīng)(這樣就可以提供多方使用),我們可以自定義注解,利用注解來選擇是否進行全局響應(yīng)包裝

首先創(chuàng)建自定義注解,作用相當于全局處理類開關(guān):

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) // 表明該注解只能放在方法上
public @interface NotResponseBody {
}

其次創(chuàng)建一個類并加上注解使其成為全局處理類。然后繼承ResponseBodyAdvice接口重寫其中的方法,即可對我們的controller進行增強操作,具體看代碼和注釋:

package com.csdn.demo1.global;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@RestControllerAdvice(basePackages = {"com.scdn.demo1.controller"}) // 注意哦,這里要加上需要掃描的包
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class? class="hljs-keyword"extends HttpMessageConverter?> aClass) {
       // 如果接口返回的類型本身就是ResultVO那就沒有必要進行額外的操作,返回false
        // 如果方法上加了我們的自定義注解也沒有必要進行額外的操作
        return !(returnType.getParameterType().equals(ResultVO.class) || returnType.hasMethodAnnotation(NotResponseBody.class));
    }

    @Override
    public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class? class="hljs-keyword"extends HttpMessageConverter?> aClass, ServerHttpRequest request, ServerHttpResponse response) {
        // String類型不能直接包裝,所以要進行些特別的處理
        if (returnType.getGenericParameterType().equals(String.class)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                // 將數(shù)據(jù)包裝在ResultVO里后,再轉(zhuǎn)換為json字符串響應(yīng)給前端
                return objectMapper.writeValueAsString(new ResultVO<>(data));
            } catch (JsonProcessingException e) {
                throw new APIException("返回String類型錯誤");
            }
        }
        // 將原本的數(shù)據(jù)包裝在ResultVO里
        return new ResultVO<>(data);
    }
}

重寫的這兩個方法是用來在controller將數(shù)據(jù)進行返回前進行增強操作,supports方法要返回為true才會執(zhí)行beforeBodyWrite方法,所以如果有些情況不需要進行增強操作可以在supports方法里進行判斷。

對返回數(shù)據(jù)進行真正的操作還是在beforeBodyWrite方法中,我們可以直接在該方法里包裝數(shù)據(jù),這樣就不需要每個接口都進行數(shù)據(jù)包裝了,省去了很多麻煩。此時controller只需這樣寫就行了:

@GetMapping("/getUser")
//@NotResponseBody  //是否繞過數(shù)據(jù)統(tǒng)一響應(yīng)開關(guān)
public User getUser() {
    User user = new User();
    user.setId(1L);
    user.setAccount("12345678");
    user.setPassword("12345678");
    user.setEmail("123@qq.com");
    // 注意哦,這里是直接返回的User類型,并沒有用ResultVO進行包裝
    return user;
}

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8598

    瀏覽量

    151157
  • URL
    URL
    +關(guān)注

    關(guān)注

    0

    文章

    139

    瀏覽量

    15340
  • 后端
    +關(guān)注

    關(guān)注

    0

    文章

    31

    瀏覽量

    2237
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    173

    瀏覽量

    179
收藏 人收藏

    評論

    相關(guān)推薦

    《CC2480接口規(guī)范》()

    《CC2480接口規(guī)范》()歡迎研究ZigBee的朋友和我交流。。。
    發(fā)表于 08-12 12:35

    SpringBoot的Druid介紹

    SpringBootDruid數(shù)據(jù)源配置
    發(fā)表于 05-07 09:21

    SpringBoot知識總結(jié)

    SpringBoot干貨學(xué)習(xí)總結(jié)
    發(fā)表于 08-01 10:40

    怎么學(xué)習(xí)SpringBoot

    SpringBoot學(xué)習(xí)之路(X5)- 整合JPA
    發(fā)表于 06-10 14:52

    怎樣去使用springboot

    怎樣去使用springboot呢?學(xué)習(xí)springboot需要懂得哪些?
    發(fā)表于 10-25 07:13

    SpringBoot應(yīng)用啟動運行run方法

    )、refreshContext(context);SpringBoot刷新IOC容器【創(chuàng)建IOC容器對象,并初始化容器,創(chuàng)建容器的每一個組件】;如果是web應(yīng)用創(chuàng)建**AnnotationConfigEmbeddedWebApplicationContext**,否則
    發(fā)表于 12-20 06:16

    基于DSP控制的電力線通信模擬前端接口設(shè)計

    基于DSP控制的電力線通信模擬前端接口設(shè)計
    發(fā)表于 10-20 15:51 ?5次下載
    基于DSP控制的電力線通信模擬前<b class='flag-5'>端接口</b>設(shè)計

    數(shù)字接口—單端接口與差動接口的對比

    數(shù)字接口—單端接口與差動接口的對比
    發(fā)表于 11-07 08:07 ?0次下載
    數(shù)字<b class='flag-5'>接口</b>—單<b class='flag-5'>端接口</b>與差動<b class='flag-5'>接口</b>的對比

    為什么建議你替換掉SpringBoot框架的Tomcat?

    SpringBoot框架,我們使用最多的是Tomcat,這是SpringBoot默認的容器技術(shù),而且是內(nèi)嵌式的Tomcat。
    的頭像 發(fā)表于 01-07 10:26 ?1096次閱讀

    什么是 SpringBoot

    本文從為什么要有 `SpringBoot`,以及 `SpringBoot` 到底方便在哪里開始入手,逐步分析了 `SpringBoot` 自動裝配的原理,最后手寫了一個簡單的 `start` 組件,通過實戰(zhàn)來體會了 `
    的頭像 發(fā)表于 04-07 11:28 ?1315次閱讀
    什么是 <b class='flag-5'>SpringBoot</b>?

    如何在SpringBoot解決Redis的緩存穿透等問題

    今天給大家介紹一下如何在SpringBoot解決Redis的緩存穿透、緩存擊穿、緩存雪崩的問題。
    的頭像 發(fā)表于 04-28 11:35 ?731次閱讀

    SpringBoot 后端接口規(guī)范(上)

    一個后端接口大致分為四個部分組成: 接口地址(url)、接口請求方式(get、post等)、請求數(shù)據(jù)(request)、響應(yīng)數(shù)據(jù)(response) 。雖然說后端接口的編寫并沒有統(tǒng)一
    的頭像 發(fā)表于 05-05 17:00 ?787次閱讀
    <b class='flag-5'>SpringBoot</b> <b class='flag-5'>后端接口</b><b class='flag-5'>規(guī)范</b>(上)

    SpringBoot 后端接口規(guī)范(下)

    一個后端接口大致分為四個部分組成:接口地址(url)、接口請求方式(get、post等)、請求數(shù)據(jù)(request)、響應(yīng)數(shù)據(jù)(response)。雖然說后端接口的編寫并沒有統(tǒng)一
    的頭像 發(fā)表于 05-05 17:02 ?627次閱讀

    后端分離必備的接口規(guī)范

    隨著互聯(lián)網(wǎng)的高速發(fā)展,前端頁面的展示、交互體驗越來越靈活、炫麗,響應(yīng)體驗也要求越來越高,后端服務(wù)的高并發(fā)、高可用、高性能、高擴展等特性的要求也愈加苛刻,從而導(dǎo)致前后端研發(fā)各自專注于自己擅長的領(lǐng)域深耕細作。
    的頭像 發(fā)表于 05-15 17:16 ?862次閱讀
    前<b class='flag-5'>后端</b>分離必備的<b class='flag-5'>接口</b><b class='flag-5'>規(guī)范</b>

    springboot后端交互流程

    Boot 進行開發(fā)時,前后端交互是一個非常重要的部分,本文將詳細介紹 Spring Boot 前后端交互的流程。 前后端交互的基本原理 在前后端交互的過程
    的頭像 發(fā)表于 11-22 16:00 ?2127次閱讀
    主站蜘蛛池模板: 亚洲免费一级视频| 免费国产不卡午夜福在线| 亚洲成a人片在线看| www.亚洲综合| 一本在线免费视频| 激情五月开心网| 国产你懂的在线观看| 成年女人毛片免费观看97| xx毛片| 午夜影视免费| 奇米影视7777| 久久久久久久影院| 午夜久久精品| 成人在线看片| 丁香狠狠| 影音先锋五月天| 国内精品视频免费观看| 免看一级a一片成人123| 婷婷色在线视频| 亚洲成人黄色网址| 亚洲成a人在线播放www| 四虎国产精品4hu永久| 三级视频国产| 免费日本黄色| 女人张开腿让男人做爽爽| 欧美精品二区| 狠狠躁夜夜躁人人躁婷婷视频| 国产中出视频| 99久久婷婷免费国产综合精品| 午夜爽爽性刺激一区二区视频| 天堂最新版在线www在线| 免费一级e一片在线播放| 久久国产中文字幕| 夜恋秀场欧美成人影院| 亚洲综合激情另类专区| 韩漫免费网站无遮挡羞羞漫画| 伊人久久大杳蕉综合大象| 性欧美人与zooz| 欧美视频色| 国产好深好硬好爽我还要视频| 一区免费|