成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

SpringBoot統(tǒng)一響應(yīng)體解決方案

figofuture / 1824人閱讀

摘要:前言最近在優(yōu)化自己之前基于的統(tǒng)一響應(yīng)體的實(shí)現(xiàn)方案。但是的狀態(tài)碼數(shù)量有限,而隨著業(yè)務(wù)的增長(zhǎng),狀態(tài)碼無(wú)法很好地表示業(yè)務(wù)中遇到的異常情況。

前言

最近在優(yōu)化自己之前基于Spring AOP的統(tǒng)一響應(yīng)體的實(shí)現(xiàn)方案。

什么是統(tǒng)一響應(yīng)體呢?在目前的前后端分離架構(gòu)下,后端主要是一個(gè)RESTful API的數(shù)據(jù)接口。

但是HTTP的狀態(tài)碼數(shù)量有限,而隨著業(yè)務(wù)的增長(zhǎng),HTTP狀態(tài)碼無(wú)法很好地表示業(yè)務(wù)中遇到的異常情況。

那么可以通過修改響應(yīng)返回的JSON數(shù)據(jù),讓其帶上一些固有的字段,例如以下這樣的

{
    "code": 10000,
    "msg": "success",
    "data": {
        "id": 2,
        "name": "test"
    }
}

其中關(guān)鍵屬性的用途如下:

code為返回結(jié)果的狀態(tài)碼

msg為返回結(jié)果的消息

data為返回的業(yè)務(wù)數(shù)據(jù)

3個(gè)屬性為固有屬性,每次響應(yīng)結(jié)果都會(huì)有帶有它們。

需求

希望實(shí)現(xiàn)一個(gè)能夠代替基于AOP的實(shí)現(xiàn)方案,需要滿足以下幾點(diǎn):

原有的基于AOP的實(shí)現(xiàn)方案需要Controller的返回類型為Object,需要新方案不限制返回類型

原有的基于AOP的實(shí)現(xiàn)方案需要通過切面表達(dá)式+注解控制切點(diǎn)的Controller(注解的包名修改會(huì)導(dǎo)致切面表達(dá)式的修改,即需要修改兩處地方),需要新方案能夠基于注解,而不需要修改切面表達(dá)式

方案思路

基于上述的需求,選擇使用SpringController增強(qiáng)機(jī)制,其中關(guān)鍵的類為以下3個(gè):

@ControllerAdvice:類注解,用于指定Controller增強(qiáng)處理器類。

ResponseBodyAdvice:接口,實(shí)現(xiàn)后beforeBodyWrite()方法后可以對(duì)響應(yīng)的body進(jìn)行修改,需要結(jié)合@ControllerAdvice使用。

@ExceptionHandler:方法注解,用于指定異常處理方法,需要結(jié)合@ControllerAdvice@ResponseBody使用。

示例關(guān)鍵代碼

本示例使用的Spring Boot版本為2.1.6.RELEASE,同時(shí)需要開發(fā)工具安裝lombok插件

引入依賴
    
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.projectlombok
            lombok
            true
        

        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    
統(tǒng)一響應(yīng)體

Controller增強(qiáng)后統(tǒng)一響應(yīng)體對(duì)應(yīng)的對(duì)象

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

/**
 * 統(tǒng)一的公共響應(yīng)體
 * @author NULL
 * @date 2019-07-16
 */
@Data
@AllArgsConstructor
public class ResponseResult implements Serializable {
    /**
     * 返回狀態(tài)碼
     */
    private Integer code;
    /**
     * 返回信息
     */
    private String msg;
    /**
     * 數(shù)據(jù)
     */
    private Object data;

}
統(tǒng)一響應(yīng)注解

統(tǒng)一響應(yīng)注解是一個(gè)標(biāo)記是否開啟統(tǒng)一響應(yīng)增強(qiáng)的注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 統(tǒng)一響應(yīng)注解
* 添加注解后,統(tǒng)一響應(yīng)體才能生效 * @author NULL * @date 2019-07-16 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface BaseResponse { }
狀態(tài)碼枚舉

統(tǒng)一響應(yīng)體中返回的狀態(tài)碼code和狀態(tài)信息msg對(duì)應(yīng)的枚舉類

/**
 * 返回狀態(tài)碼
 *
 * @author NULL
 * @date 2019-07-16
 */
public enum ResponseCode {
    /**
     * 成功返回的狀態(tài)碼
     */
    SUCCESS(10000, "success"),
    /**
     * 資源不存在的狀態(tài)碼
     */
    RESOURCES_NOT_EXIST(10001, "資源不存在"),
    /**
     * 所有無(wú)法識(shí)別的異常默認(rèn)的返回狀態(tài)碼
     */
    SERVICE_ERROR(50000, "服務(wù)器異常");
    /**
     * 狀態(tài)碼
     */
    private int code;
    /**
     * 返回信息
     */
    private String msg;

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

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}
業(yè)務(wù)異常類

業(yè)務(wù)異常類是用于識(shí)別業(yè)務(wù)相關(guān)的異常,需要注意這個(gè)異常類強(qiáng)制需要以ResponseCode作為構(gòu)造方法入?yún)?,這樣可以通過捕獲異常獲得返回的狀態(tài)碼信息

import com.rjh.web.response.ResponseCode;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 業(yè)務(wù)異常類,繼承運(yùn)行時(shí)異常,確保事務(wù)正?;貪L
 *
 * @author NULL
 * @since  2019-07-16
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class BaseException extends RuntimeException{

    private ResponseCode code;

    public BaseException(ResponseCode code) {
        this.code = code;
    }

    public BaseException(Throwable cause, ResponseCode code) {
        super(cause);
        this.code = code;
    }
}
異常處理類

用于處理Controller運(yùn)行時(shí)未捕獲的異常的處理類。

import com.rjh.web.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 異常處理器
 *
 * @author NULL
 * @since  2019-07-16
 */
@ControllerAdvice(annotations = BaseResponse.class)
@ResponseBody
@Slf4j
public class ExceptionHandlerAdvice {
    /**
     * 處理未捕獲的Exception
     * @param e 異常
     * @return 統(tǒng)一響應(yīng)體
     */
    @ExceptionHandler(Exception.class)
    public ResponseResult handleException(Exception e){
        log.error(e.getMessage(),e);
        return new ResponseResult(ResponseCode.SERVICE_ERROR.getCode(),ResponseCode.SERVICE_ERROR.getMsg(),null);
    }

    /**
     * 處理未捕獲的RuntimeException
     * @param e 運(yùn)行時(shí)異常
     * @return 統(tǒng)一響應(yīng)體
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseResult handleRuntimeException(RuntimeException e){
        log.error(e.getMessage(),e);
        return new ResponseResult(ResponseCode.SERVICE_ERROR.getCode(),ResponseCode.SERVICE_ERROR.getMsg(),null);
    }

    /**
     * 處理業(yè)務(wù)異常BaseException
     * @param e 業(yè)務(wù)異常
     * @return 統(tǒng)一響應(yīng)體
     */
    @ExceptionHandler(BaseException.class)
    public ResponseResult handleBaseException(BaseException e){
        log.error(e.getMessage(),e);
        ResponseCode code=e.getCode();
        return new ResponseResult(code.getCode(),code.getMsg(),null);
    }
}
響應(yīng)增強(qiáng)類

Conrtoller增強(qiáng)的統(tǒng)一響應(yīng)體處理類,需要注意異常處理類已經(jīng)進(jìn)行了增強(qiáng),所以需要判斷一下返回的對(duì)象是否為統(tǒng)一響應(yīng)體對(duì)象。

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 統(tǒng)一響應(yīng)體處理器
 * @author NULL
 * @date 2019-07-16
 */
@ControllerAdvice(annotations = BaseResponse.class)
@Slf4j
public class ResponseResultHandlerAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        log.info("returnType:"+returnType);
        log.info("converterType:"+converterType);
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(MediaType.APPLICATION_JSON.equals(selectedContentType) || MediaType.APPLICATION_JSON_UTF8.equals(selectedContentType)){ // 判斷響應(yīng)的Content-Type為JSON格式的body
            if(body instanceof ResponseResult){ // 如果響應(yīng)返回的對(duì)象為統(tǒng)一響應(yīng)體,則直接返回body
                return body;
            }else{
                // 只有正常返回的結(jié)果才會(huì)進(jìn)入這個(gè)判斷流程,所以返回正常成功的狀態(tài)碼
                ResponseResult responseResult =new ResponseResult(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMsg(),body);
                return responseResult;
            }
        }
        // 非JSON格式body直接返回即可
        return body;
    }
}
使用示例

首先準(zhǔn)備一個(gè)User對(duì)象

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;

/**
 * 用戶類
 * @author NULL
 * @date 2019-07-16
 */
@Data
@EqualsAndHashCode
public class User implements Serializable {

    private Integer id;

    private String name;
    
}

然后是準(zhǔn)備一個(gè)簡(jiǎn)單的UserController即可

import com.rjh.web.entity.User;
import com.rjh.web.exception.BaseException;
import com.rjh.web.response.BaseResponse;
import com.rjh.web.response.ResponseCode;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 測(cè)試用的Controller
 *
 * @author NULL
 * @date 2019-07-16
 */
@BaseResponse
@RestController
@RequestMapping("users")
public class UserController {

    @GetMapping("/{userId}")
    public User getUserById(@PathVariable Integer userId){
        if(userId.equals(0)){
            throw new BaseException(ResponseCode.RESOURCES_NOT_EXIST);
        }
        if(userId.equals(1)){
            throw new RuntimeException();
        }
        User user=new User();
        user.setId(userId);
        user.setName("test");
        return user;
    }
    
}
運(yùn)行結(jié)果

在瀏覽器直接訪問http://127.0.0.1:8080/users/0,則返回結(jié)果如下(結(jié)果經(jīng)過格式化處理):

{
    "code": 10001,
    "msg": "資源不存在",
    "data": null
}

在瀏覽器直接訪問http://127.0.0.1:8080/users/1,則返回結(jié)果如下(結(jié)果經(jīng)過格式化處理):

{
    "code": 50000,
    "msg": "服務(wù)器異常",
    "data": null
}

在瀏覽器直接訪問http://127.0.0.1:8080/users/2,則返回結(jié)果如下(結(jié)果經(jīng)過格式化處理):

{
    "code": 10000,
    "msg": "success",
    "data": {
        "id": 2,
        "name": "test"
    }
}

由運(yùn)行結(jié)果可以得知統(tǒng)一響應(yīng)增強(qiáng)其實(shí)已經(jīng)生效了,而且能夠很好的處理異常。

示例代碼地址

下面是這個(gè)示例的代碼地址,如果覺得不錯(cuò)或者幫助到你,希望大家給個(gè)Star
https://github.com/spring-bas...

參考資料

https://docs.spring.io/spring...

https://docs.spring.io/spring...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75452.html

相關(guān)文章

  • Spring Boot 2.x 系列教程:WebFlux REST API 全局異常處理 Error

    摘要:挺多人咨詢的,異常處理用切面注解去實(shí)現(xiàn)去全局異常處理。全局異常處理類,代碼如下代碼解析如下抽象類是用來(lái)處理全局錯(cuò)誤時(shí)進(jìn)行擴(kuò)展和實(shí)現(xiàn)注解標(biāo)記的切面排序,值越小擁有越高的優(yōu)先級(jí),這里設(shè)置優(yōu)先級(jí)偏高。 本文內(nèi)容 為什么要全局異常處理? WebFlux REST 全局異常處理實(shí)戰(zhàn) 小結(jié) 摘錄:只有不斷培養(yǎng)好習(xí)慣,同時(shí)不斷打破壞習(xí)慣,我們的行為舉止才能夠自始至終都是正確的。 一、為什么要全局...

    BicycleWarrior 評(píng)論0 收藏0
  • 慕課網(wǎng)_《SpringBoot進(jìn)階之Web進(jìn)階》學(xué)習(xí)總結(jié)

    摘要:時(shí)間年月日星期日說(shuō)明本文部分內(nèi)容均來(lái)自慕課網(wǎng)。慕課網(wǎng)教學(xué)示例源碼個(gè)人學(xué)習(xí)源碼第一章課程介紹課程介紹本課程緊接著小時(shí)學(xué)會(huì)課程,請(qǐng)先看入門課。異常返回通知在連接點(diǎn)拋出異常后執(zhí)行。 時(shí)間:2017年3月19日星期日說(shuō)明:本文部分內(nèi)容均來(lái)自慕課網(wǎng)。@慕課網(wǎng):http://www.imooc.com教學(xué)示例源碼:https://github.com/zccodere/s...個(gè)人學(xué)習(xí)源碼:htt...

    lifefriend_007 評(píng)論0 收藏0
  • 【ShareBook】1-后臺(tái)框架與小程序用戶登錄接口實(shí)戰(zhàn)

    摘要:注冊(cè)流程是從小程序簡(jiǎn)稱,以下替代獲取用戶的,給到服務(wù)器,服務(wù)器會(huì)用還有自己的等信息一起去微信服務(wù)器請(qǐng)求用戶數(shù)據(jù),注意每一個(gè)所對(duì)應(yīng)的用戶都是不一樣的。 本博客 貓叔的博客,轉(zhuǎn)載請(qǐng)申明出處閱讀本文約 5分鐘適讀人群:Java后端、Java初級(jí)、小程序前端 前后端項(xiàng)目的地址 ShareBookServer ShareBookClient 小程序前端 showImg(https://seg...

    zorro 評(píng)論0 收藏0
  • SpringBoot 中 @SpringBootApplication注解背后的三結(jié)構(gòu)探秘

    摘要:概述約定大于配置的功力讓我們?nèi)玢宕猴L(fēng),在我之前寫的文章從到也對(duì)比過和這兩個(gè)框架,不過最終以超高的代碼信噪比和易上手性讓我們映像頗深。至于,我想在非時(shí)代大家應(yīng)該不陌生吧,作用是配置容器,也即形式的容器的配置類所使用。 showImg(https://segmentfault.com/img/remote/1460000015822144); 概 述 SpringBoot 約定大于配置...

    Tecode 評(píng)論0 收藏0
  • SpringBoot RocketMQ 整合使用和監(jiān)控

    摘要:前提通過前面兩篇文章可以簡(jiǎn)單的了解和安裝,今天就將和整合起來(lái)使用。然后我運(yùn)行之前的整合項(xiàng)目,查看監(jiān)控信息如下總結(jié)整篇文章講述了與整合和監(jiān)控平臺(tái)的搭建。 showImg(https://segmentfault.com/img/remote/1460000013232432?w=1920&h=1277); 前提 通過前面兩篇文章可以簡(jiǎn)單的了解 RocketMQ 和 安裝 RocketMQ...

    Jacendfeng 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<