国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

springboot優(yōu)雅的統(tǒng)一返回格式 + 全局異常處理(包括404等異常)

這篇具有很好參考價(jià)值的文章主要介紹了springboot優(yōu)雅的統(tǒng)一返回格式 + 全局異常處理(包括404等異常)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

目錄
  • 1.自定義枚舉類
  • 2.自定義統(tǒng)一返回格式類
  • 3.統(tǒng)一返回格式的高級(jí)實(shí)現(xiàn)
  • 4.全局異常處理
  • 5.更優(yōu)雅的全局異常處理
  • 6.處理404錯(cuò)誤

1.自定義枚舉類

public enum ReturnCode {
    RC200(200, "ok"),
    RC400(400, "請(qǐng)求失敗,參數(shù)錯(cuò)誤,請(qǐng)檢查后重試。"),
    RC404(404, "未找到您請(qǐng)求的資源。"),
    RC405(405, "請(qǐng)求方式錯(cuò)誤,請(qǐng)檢查后重試。"),
    RC500(500, "操作失敗,服務(wù)器繁忙或服務(wù)器錯(cuò)誤,請(qǐng)稍后再試。");

    // 自定義狀態(tài)碼
    private final int code;

    // 自定義描述
    private final String msg;

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

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

該枚舉類為我們和前端約定好的返回狀態(tài)碼和描述信息,可根據(jù)自己的需求修改狀態(tài)碼和描述

2.自定義統(tǒng)一返回格式類

@Data
public class R<T> {

    private Integer code; //狀態(tài)碼

    private String msg; //提示信息

    private T data; //數(shù)據(jù)

    private long timestamp;//接口請(qǐng)求時(shí)間

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

    public static <T> R<T> success(T data) {
        R<T> r = new R<>();
        r.setCode(ReturnCode.RC200.getCode());
        r.setMsg(ReturnCode.RC200.getMsg());
        r.setData(data);
        return r;
    }

    public static <T> R<T> error(int code, String msg) {
        R<T> r = new R<>();
        r.setCode(code);
        r.setMsg(msg);
        r.setData(null);
        return r;
    }
}

@Data注解為L(zhǎng)ombok工具類庫(kù)中的注解,提供類的get、set、equals、hashCode、canEqual、toString方法,使用時(shí)需配置Lombok,如不配置請(qǐng)手動(dòng)生成相關(guān)方法。

我們返回的信息至少包括code、msg、data三部分,其中code是我們后端和前端約定好的狀態(tài)碼,msg為提示信息,data為返回的具體數(shù)據(jù),沒(méi)有返回?cái)?shù)據(jù)則為null。除了這三部分外,你還可以定義一些其他字段,比如請(qǐng)求時(shí)間timestamp。

定義了統(tǒng)一返回類后,controller層返回?cái)?shù)據(jù)時(shí)統(tǒng)一使用R.success()方法封裝。

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public R<List<Student>> getStudent() {
        ArrayList<Student> list = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("name1");
        Student student2 = new Student();
        student2.setId(2);
        student2.setName("name2");
        list.add(student1);
        list.add(student2);
        return R.success(list);
    }
}

@Data
class Student {
    private Integer id;
    private String name;
}

例如在以上代碼中,我們的需求是查詢學(xué)生信息,我們調(diào)用這個(gè)test1接口就返回了以下的結(jié)果:

{
    "code": 200,
    "msg": "ok",
    "data": [
        {
            "id": 1,
            "name": "name1"
        },
        {
            "id": 2,
            "name": "name2"
        }
    ],
    "timestamp": 1692805971309
}

到這里我們已經(jīng)基本實(shí)現(xiàn)了統(tǒng)一返回格式,但是上面這種實(shí)現(xiàn)方式也有一個(gè)缺點(diǎn),就是每次返回?cái)?shù)據(jù)的時(shí)候都需要調(diào)用R.success()方法,非常麻煩,我們希望能夠在controller層里直接返回我們實(shí)際的數(shù)據(jù),即data字段中的內(nèi)容,然后自動(dòng)幫我們封裝到R.success()之中,因此我們需要一種更高級(jí)的方法。

3.統(tǒng)一返回格式的高級(jí)實(shí)現(xiàn)

我們需要利用springboot的ResponseBodyAdvice類來(lái)實(shí)現(xiàn)這個(gè)功能,ResponseBodyAdvice的作用:攔截Controller方法的返回值,統(tǒng)一處理返回值/響應(yīng)體

/**
 * 攔截controller返回值,封裝后統(tǒng)一返回格式
 */
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //如果Controller返回String的話,SpringBoot不會(huì)幫我們自動(dòng)封裝而直接返回,因此我們需要手動(dòng)轉(zhuǎn)換成json。
        if (o instanceof String) {
            return objectMapper.writeValueAsString(R.success(o));
        }
        //如果返回的結(jié)果是R對(duì)象,即已經(jīng)封裝好的,直接返回即可。
        //如果不進(jìn)行這個(gè)判斷,后面進(jìn)行全局異常處理時(shí)會(huì)出現(xiàn)錯(cuò)誤
        if (o instanceof R) {
            return o;
        }
        return R.success(o);
    }
}

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

  1. 全局異常處理
  2. 全局?jǐn)?shù)據(jù)綁定
  3. 全局?jǐn)?shù)據(jù)預(yù)處理

經(jīng)過(guò)上面的處理后,我們就不需要在controller層使用R.success()進(jìn)行封裝了,直接返回原始數(shù)據(jù),springboot就會(huì)幫我們自動(dòng)封裝。

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public List<Student> getStudent() {
        ArrayList<Student> list = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("name1");
        Student student2 = new Student();
        student2.setId(2);
        student2.setName("name2");
        list.add(student1);
        list.add(student2);
        return list;
    }
}

@Data
class Student {
    private Integer id;
    private String name;
}

此時(shí)我們調(diào)用接口返回的數(shù)據(jù)依然是自定義統(tǒng)一返回格式的json數(shù)據(jù)

{
    "code": 200,
    "msg": "ok",
    "data": [
        {
            "id": 1,
            "name": "name1"
        },
        {
            "id": 2,
            "name": "name2"
        }
    ],
    "timestamp": 1692805971325
}

需要注意的是,即使我們controller層的接口返回類型是void,ResponseBodyAdvice類依然會(huì)幫我們自動(dòng)封裝,其中data字段為null。返回的格式如下:

{
    "code": 200,
    "msg": "ok",
    "data": null,
    "timestamp": 1692805971336
}

4.全局異常處理

如果我們不做統(tǒng)一異常處理,當(dāng)后端出現(xiàn)異常時(shí),返回的數(shù)據(jù)就變成了下面這樣:

后端接口:

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public String getStudent() {
        int i = 1/0;
        return "hello";
    }
}

返回json:

{
    "code": 200,
    "msg": "ok",
    "data": {
        "timestamp": "2023-08-23T16:13:57.818+00:00",
        "status": 500,
        "error": "Internal Server Error",
        "path": "/test/test1"
    },
    "timestamp": 1692807237832
}

code返回了200,又在data中顯示500錯(cuò)誤,這顯然不是我們想要的結(jié)果,我們想要的結(jié)果應(yīng)該時(shí)code返回500,data返回null。解決的方式有很多,你可以通過(guò)try catch的方式來(lái)捕獲,但是我們并不知道什么時(shí)候會(huì)出現(xiàn)異常,而且手動(dòng)寫try catch并不方便。因此我們需要進(jìn)行全局異常處理 。

@Slf4j
@RestControllerAdvice
@ResponseBody
public class RestExceptionHandler {

    /**
     * 處理異常
     *
     * @param e otherException
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R<String> exception(Exception e) {
        log.error("異常 exception = {}", e.getMessage(), e);
        return R.error(ReturnCode.RC500.getCode(), ReturnCode.RC500.getMsg());
    }
}

說(shuō)明:

  1. @RestControllerAdvice,RestController的增強(qiáng)類,可用于實(shí)現(xiàn)全局異常處理器
  2. @ExceptionHandler,統(tǒng)一處理某一類異常,比如要獲取空指針異??梢?code>@ExceptionHandler(NullPointerException.class)

除此之外,你還可以使用@ResponseStatus來(lái)指定客戶端收到的http狀態(tài)碼,如@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)則客戶端收到的http狀態(tài)碼為500。如果不指定,則默認(rèn)返回200。在這里我們并沒(méi)有指定,因此我們的請(qǐng)求返回的http狀態(tài)碼全部是200,當(dāng)出現(xiàn)異常時(shí),我們可以修改統(tǒng)一返回格式中code的狀態(tài)碼,來(lái)表明具體情況。

具體效果如下:

接口:

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public void test() {
        int i = 1/0; //發(fā)生除0異常
    }
}

返回json:

{
    "code": 500,
    "msg": "操作失敗,服務(wù)器繁忙或服務(wù)器錯(cuò)誤,請(qǐng)稍后再試。",
    "data": null,
    "timestamp": 1692808061062
}

基本上實(shí)現(xiàn)了我們的需求。

5.更優(yōu)雅的全局異常處理

在上面的全局異常處理中,我們直接捕獲了Exception.class,無(wú)論什么異常都統(tǒng)一處理,但實(shí)際上我們需要根據(jù)不同的異常進(jìn)行不同的處理,如空指針異??赡苁乔岸藗鲄㈠e(cuò)誤,以及我們的自定義異常等。

自定義異常如下:

@Getter
@Setter
public class BusinessException extends RuntimeException {
    private int code;
    private String msg;

    public BusinessException() {
    }

    public BusinessException(ReturnCode returnCode) {
        this(returnCode.getCode(),returnCode.getMsg());
    }

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

注:@Getter@Setter分別提供了get和set方法,同樣需要Lombok依賴。

我們?cè)谌之惓L幚碇锌梢允褂?code>@ExceptionHandler指定異常類型,分別處理不同的異常

@Slf4j
@RestControllerAdvice
@ResponseBody
public class RestExceptionHandler {

    /**
     * 處理自定義異常
     *
     * @param e BusinessException
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public R<String> businessException(BusinessException e) {
        log.error("業(yè)務(wù)異常 code={}, BusinessException = {}", e.getCode(), e.getMessage(), e);
        return R.error(e.getCode(), e.getMsg());
    }

    /**
     * 處理空指針的異常
     *
     * @param e NullPointerException
     * @return
     * @description 空指針異常定義為前端傳參錯(cuò)誤,返回400
     */
    @ExceptionHandler(NullPointerException.class)
    public R<String> nullPointerException(NullPointerException e) {
        log.error("空指針異常 NullPointerException ", e);
        return R.error(ReturnCode.RC400.getCode(), ReturnCode.RC400.getMsg());
    }

    /**
     * 處理其他異常
     *
     * @param e otherException
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R<String> exception(Exception e) {
        log.error("未知異常 exception = {}", e.getMessage(), e);
        return R.error(ReturnCode.RC500.getCode(), ReturnCode.RC500.getMsg());
    }
}

需要注意的是一個(gè)異常只會(huì)被捕獲一次,比如空指針異常,只會(huì)被第二個(gè)方法捕獲,處理之后不會(huì)再被最后一個(gè)方法捕獲。當(dāng)上面兩個(gè)方法都沒(méi)有捕獲到指定異常時(shí),最后一個(gè)方法指定了@ExceptionHandler(Exception.class)就可以捕獲到所有的異常,相當(dāng)于if elseif else語(yǔ)句

分別測(cè)試自定義異常、空指針異常以及其他異常:

  1. 自定義異常

    接口:

@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/test1")
    public void test() {
        throw new BusinessException(ReturnCode.RC500.getCode(),"發(fā)生異常");
    }
}

? 返回json:

{
    "code": 500,
    "msg": "發(fā)生異常",
    "data": null,
    "timestamp": 1692809118244
}
  1. 空指針異常:

    接口:

    @RestController
    @RequestMapping("/test")
    public class TestController {
        @PostMapping("/test1")
        public void test(int id, String name) {
            System.out.println(id + name);
            boolean equals = name.equals("11");
        }
    }
    

    請(qǐng)求:

    返回json:

    {
        "code": 400,
        "msg": "請(qǐng)求失敗,參數(shù)錯(cuò)誤,請(qǐng)檢查后重試。",
        "data": null,
        "timestamp": 1692809456917
    }
    
  2. 其他異常:

    接口:

    @RestController
    @RequestMapping("/test")
    public class TestController {
        @PostMapping("/test1")
        public void test() {
           throw new RuntimeException("發(fā)生異常");
        }
    }
    

    返回json:

    {
        "code": 500,
        "msg": "操作失敗,服務(wù)器繁忙或服務(wù)器錯(cuò)誤,請(qǐng)稍后再試。",
        "data": null,
        "timestamp": 1692809730234
    }
    

6.處理404錯(cuò)誤

即使我們配置了全局異常處理,當(dāng)出現(xiàn)404 not found等4xx錯(cuò)誤時(shí),依然會(huì)出現(xiàn)意外情況:

返回json:

{
    "code": 200,
    "msg": "ok",
    "data": {
        "timestamp": "2023-08-23T17:01:15.102+00:00",
        "status": 404,
        "error": "Not Found",
        "path": "/test/nullapi"
    },
    "timestamp": 1692810075116
}

我們可以看到發(fā)生404錯(cuò)誤時(shí)控制臺(tái)并沒(méi)有報(bào)異常,原因是404錯(cuò)誤并不屬于異常,全局異常處理自然不會(huì)去捕獲并處理。因此我們的解決方法是當(dāng)出現(xiàn)4xx錯(cuò)誤時(shí),讓springboot直接報(bào)異常,這樣我們的全局異常處理就可以捕獲到。

application.yml配置文件增加以下配置項(xiàng):

#  當(dāng)HTTP狀態(tài)碼為4xx時(shí)直接拋出異常
spring:
  mvc:
    throw-exception-if-no-handler-found: true
  #  關(guān)閉默認(rèn)的靜態(tài)資源路徑映射
  web:
    resources:
      add-mappings: false

現(xiàn)在當(dāng)我們?cè)俅握?qǐng)求一個(gè)不存在的接口是,控制臺(tái)會(huì)報(bào)NoHandlerFoundException異常,然后被全局異常處理捕獲到并統(tǒng)一返回

返回json:

{
    "code": 500,
    "msg": "操作失敗,服務(wù)器繁忙或服務(wù)器錯(cuò)誤,請(qǐng)稍后再試。",
    "data": null,
    "timestamp": 1692810621545
}

當(dāng)發(fā)生404錯(cuò)誤時(shí),我們可以在全局異常處理中單獨(dú)對(duì)NoHandlerFoundException異常進(jìn)行處理。

/**
     * 處理404異常
     *
     * @param e NoHandlerFoundException
     * @return
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public R<String> noHandlerFoundException(HttpServletRequest req, Exception e) {
        log.error("404異常 NoHandlerFoundException, method = {}, path = {} ", req.getMethod(), req.getServletPath(), e);
        return R.error(ReturnCode.RC404.getCode(), ReturnCode.RC404.getMsg());
    }

在上面中,我們使用@ExceptionHandler(NoHandlerFoundException.class)單獨(dú)捕獲處理404異常,統(tǒng)一返回格式中code也設(shè)置為404。你也可以使用@ResponseStatus(HttpStatus.NOT_FOUND)注解指定http狀態(tài)碼為404

現(xiàn)在當(dāng)我們?cè)俅伟l(fā)生404異常時(shí),返回json如下:

{
    "code": 404,
    "msg": "未找到您請(qǐng)求的資源。",
    "data": null,
    "timestamp": 1692811047868
}

控制臺(tái)日志:

同理我們還可以為405錯(cuò)誤進(jìn)行配置,405錯(cuò)誤對(duì)應(yīng)的異常為HttpRequestMethodNotSupportedException

/**
     * 處理請(qǐng)求方式錯(cuò)誤(405)異常
     *
     * @param e HttpRequestMethodNotSupportedException
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<String> HttpRequestMethodNotSupportedException(HttpServletRequest req, Exception e) {
        log.error("請(qǐng)求方式錯(cuò)誤(405)異常 HttpRequestMethodNotSupportedException, method = {}, path = {}", req.getMethod(), req.getServletPath(), e);
        return R.error(ReturnCode.RC405.getCode(), ReturnCode.RC405.getMsg());
    }

返回json:

{
    "code": 405,
    "msg": "請(qǐng)求方式錯(cuò)誤,請(qǐng)檢查后重試。",
    "data": null,
    "timestamp": 1692811288226
}

控制臺(tái)日志:

全局異常處理RestExceptionHandler類完整代碼如下:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-667273.html

package com.tuuli.config;

import com.tuuli.common.BusinessException;
import com.tuuli.common.R;
import com.tuuli.common.ReturnCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;

/**
 * 全局異常處理
 */
@Slf4j
@RestControllerAdvice
@ResponseBody
public class RestExceptionHandler {

    /**
     * 處理自定義異常
     *
     * @param e BusinessException
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public R<String> businessException(BusinessException e) {
        log.error("業(yè)務(wù)異常 code={}, BusinessException = {}", e.getCode(), e.getMessage(), e);
        return R.error(e.getCode(), e.getMsg());
    }

    /**
     * 處理空指針的異常
     *
     * @param e NullPointerException
     * @return
     * @description 空指針異常定義為前端傳參錯(cuò)誤,返回400
     */
    @ExceptionHandler(value = NullPointerException.class)
    public R<String> nullPointerException(NullPointerException e) {
        log.error("空指針異常 NullPointerException ", e);
        return R.error(ReturnCode.RC400.getCode(), ReturnCode.RC400.getMsg());
    }

    /**
     * 處理404異常
     *
     * @param e NoHandlerFoundException
     * @return
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    //@ResponseStatus(HttpStatus.NOT_FOUND)
    public R<String> noHandlerFoundException(HttpServletRequest req, Exception e) {
        log.error("404異常 NoHandlerFoundException, method = {}, path = {} ", req.getMethod(), req.getServletPath(), e);
        return R.error(ReturnCode.RC404.getCode(), ReturnCode.RC404.getMsg());
    }

    /**
     * 處理請(qǐng)求方式錯(cuò)誤(405)異常
     *
     * @param e HttpRequestMethodNotSupportedException
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<String> HttpRequestMethodNotSupportedException(HttpServletRequest req, Exception e) {
        log.error("請(qǐng)求方式錯(cuò)誤(405)異常 HttpRequestMethodNotSupportedException, method = {}, path = {}", req.getMethod(), req.getServletPath(), e);
        return R.error(ReturnCode.RC405.getCode(), ReturnCode.RC405.getMsg());
    }

    /**
     * 處理其他異常
     *
     * @param e otherException
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R<String> exception(Exception e) {
        log.error("未知異常 exception = {}", e.getMessage(), e);
        return R.error(ReturnCode.RC500.getCode(), ReturnCode.RC500.getMsg());
    }
}

到了這里,關(guān)于springboot優(yōu)雅的統(tǒng)一返回格式 + 全局異常處理(包括404等異常)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包