首頁  >  文章  >  Java  >  SpringBoot統一介面回傳及全域異常如何處理

SpringBoot統一介面回傳及全域異常如何處理

PHPz
PHPz轉載
2023-05-12 16:01:061838瀏覽

一、SpringBoot不使用統一回傳格式

預設情況下,SpringBoot會有以下三種回傳情況。

1.1 使用字串回傳

@GetMapping("/getUserName")
public String getUserName(){
    return "HuaGe";
}

呼叫介面傳回結果:

HuaGe

1.2 使用實體類別回傳

@GetMapping("/getUserName")
public User getUserName(){
    return new User("HuaGe",18,"男");
}

呼叫介面傳回結果:

{
  "name": "HuaGe",
  "age": "18",
  "性别": "男", 
}

1.3 異常情況下回傳

@GetMapping("/getUserName")
public static String getUserName(){
    HashMap hashMap = Maps.newHashMap();
    return hashMap.get(0).toString();
}

模擬一個空指標異常,在不做任何異常處理的情況下,可以看下SpringBoot的預設回傳結果:

{
    "timestamp": "2021-08-09T06:56:41.524+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "path": "/sysUser/getUserName"
}

對於上面這幾種情況,如果整個專案沒有定義統一的返回格式,五個後台開發人員定義五種返回格式,這樣不僅代碼臃腫,前後端對接效率低,而且還會有一些意向不到的情況發生,例如前端直接顯示異常詳情等,這給用戶的體驗是非常差的。

二、基礎玩法

專案中最常見到的是封裝一個工具類,類別中定義需要返回的字段信息,把需要返回前端的接口信息,通過該類進行封裝,這樣就可以解決返回格式不統一的現象了。

2.1 參數說明

  • code: 狀態碼,後台可以維護一套統一的狀態碼;

  • ##message: 描述訊息,介面呼叫成功/失敗的提示訊息;

  • data: 傳回資料。

2.2 流程說明

  • #新建Result類別

    ##
    public class Result<T> {
        
        private int code;
        
        private String message;
        
        private T data;
    
        public Result() {}
        public Result(int code, String message) {
            this.code = code;
            this.message = message;
        }
        
        /**
         * 成功
         */
        public static <T> Result<T> success(T data) {
            Result<T> result = new Result<T>();
            result.setCode(ResultMsgEnum.SUCCESS.getCode());
            result.setMessage(ResultMsgEnum.SUCCESS.getMessage());
            result.setData(data);
            return result;
        }
    
        /**
         * 失败
         */
        public static <T> Result<T> error(int code, String message) {
            return new Result(code, message);
        }
    }

定義回傳狀態碼

public enum ResultMsgEnum {
    SUCCESS(0, "成功"),
    FAIL(-1, "失败"),
    AUTH_ERROR(502, "授权失败!"),
    SERVER_BUSY(503, "服务器正忙,请稍后再试!"),
    DATABASE_OPERATION_FAILED(504, "数据库操作失败");
    private int code;
    private String message;

    ResultMsgEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }
    public int getCode() {
        return this.code;
    }
    
    public String getMessage() {
        return this.message;
    }
}

使用方式

上面兩個步驟定義了

資料回傳格式

狀態碼

,接下來就要看下在介面中如何使用了。

@GetMapping("/getUserName")
public Result getUserName(){
    return Result.success("huage");
}

呼叫結果如下,可以看到是我們在Result中定義的參數類型。
    {
        "code": 0,
        "message": "成功",
        "data": "huage"
    }
  • 這樣寫雖然能夠滿足日常需求,而且我相信很多小伙伴也是這麼用的,但是如果我們有大量的接口,然後在每一個接口中都使用

    Result.success來包裝回訊息,會新增很多重複程式碼,顯得不夠優雅,甚至都不好意思拿出去顯擺。肯定會有一種方式能夠再一次提高程式碼逼格,實現最優解。 三、進階用法

    基本用法學會後,接下來看點究極版本,主要用到如下兩個知識點,用法簡單,無論是拿出來教學妹,還是指點小姐姐,都是必備技能。
  • 3.1 類別介紹

  • ResponseBodyAdvice:
此介面是SpringMVC 4.1提供的,它允許在執行

@ResponseBody

後自訂返回數據,用來封裝統一資料格式傳回;
  • @RestControllerAdvice:

    此註解是對Controller進行增強的,可以全域擷取拋出的異常。
  • 3.2 用法說明ResponseAdvice

    類別;
  • 實現
  • ResponseBodyAdvice
接口,實作

supportsbeforeBodyWrite方法;

該類別用於統一封裝controller中介面的傳回結果。

@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 是否开启功能 true:是 
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    /**
     * 处理返回结果
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //处理字符串类型数据
        if(o instanceof String){
            try {
                return objectMapper.writeValueAsString(Result.success(o));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return Result.success(o);
    }
}

我們可以透過

getUserName

介面測試一下,會發現和直接使用

Result

傳回的結果是一致的。

不過,細心的小夥伴們肯定注意到了,在

ResponseAdvice我們全部使用了Result.success(o)

來處理結果,對於error類型的結果未做處理。我們來看下,發生異常情況時,回傳結果是什麼樣子呢?繼續使用上面HashMap空指標異常的程式碼,測試結果如下:

{
    "code": 0,
    "message": "成功",
    "data": {
        "timestamp": "2021-08-09T09:33:26.805+00:00",
        "status": 405,
        "error": "Method Not Allowed",
        "path": "/sysUser/getUserName"
    }
}
雖然格式上沒有毛病,但在code、data欄位的特定資料上是不友善或不正確的。不處理好這些事情,會嚴重影響自己在前端妹妹心中的高大形象的,這是絕對不能容忍的。

3.3 全域例外處理器

以前我們遇到例外時,第一時間想到的應該是try..catch..finnal吧,不過這種方式會導致大量程式碼重複,維護困難,邏輯臃腫等問題,這不是我們想要的結果。 今天我們要用的全域異常處理方式,用起來是比較簡單的。首先新增一個類,增加@RestControllerAdvice

註解,該註解的作用花哥上面已經介紹過,就不再嘮叨了。 ###
@RestControllerAdvice
public class CustomerExceptionHandler {
    
}
###如果我們有想要攔截的例外類型,就新增一個方法,使用###@ExceptionHandler###註解修飾,註解參數為目標例外類型。 ######例如:controller中介面發生Exception異常時,就會進入到###Execption###方法中進行捕獲,將雜亂的異常訊息,轉換成指定格式後交給###ResponseAdvice# ##方法進行統一格式封裝並回傳給前端小夥伴。 ###
@RestControllerAdvice
@Slf4j
public class CustomerExceptionHandler {

    @ExceptionHandler(AuthException.class)
    public String ErrorHandler(AuthorizationException e) {
        log.error("没有通过权限验证!", e);
        return "没有通过权限验证!";
    }

    @ExceptionHandler(Exception.class)
    public Result Execption(Exception e) {
        log.error("未知异常!", e);
        return Result.error(ResultMsgEnum.SERVER_BUSY.getCode(),ResultMsgEnum.SERVER_BUSY.getMessage());
    }
}

再次调用接口getUserName查看返回结果,会发现还是有一些问题,因为我们在CustomerExceptionHandler中已经将接口返回结果封装成Result类型,而代码执行到统一结果返回类ResponseAdvice时,又会结果再次封装,就出现了如下问题。

{
    "code": 0,
    "message": "成功",
    "data": {
        "code": 503,
        "message": "服务器正忙,请稍后再试!",
        "data": null
    }
}

3.4 统一返回结果处理类最终版

解决上述问题非常简单,只要在beforeBodyWrite中增加一条判断即可。

@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 是否开启功能 true:开启
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    /**
     * 处理返回结果
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //处理字符串类型数据
        if(o instanceof String){
            try {
                return objectMapper.writeValueAsString(Result.success(o));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        //返回类型是否已经封装
        if(o instanceof Result){
            return o;
        }
        return Result.success(o);
    }
}

以上是SpringBoot統一介面回傳及全域異常如何處理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除