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

SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況

 更新時(shí)間:2024年05月17日 14:55:17   作者:sw-code  
這篇文章主要介紹了SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

原因

在springboot項(xiàng)目里我們希望接口返回的數(shù)據(jù)包含至少三個(gè)屬性:

  • code:請(qǐng)求接口的返回碼,成功或者異常等返回編碼,例如定義請(qǐng)求成功。
  • message:請(qǐng)求接口的描述,也就是對(duì)返回編碼的描述。
  • data:請(qǐng)求接口成功,返回的結(jié)果。
{
  "code":20000,
  "message":"成功",
  "data":{
    "info":"測(cè)試成功"
  }
}

開(kāi)發(fā)環(huán)境

  • 工具:IDEA
  • SpringBoot版本:2.2.2.RELEASE

依賴(lài)

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.62</version>
</dependency>
<!-- Spring web -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>
<!-- swagger3 -->
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-boot-starter</artifactId>
  <version>3.0.0</version>
</dependency>

創(chuàng)建 SpringBoot 工程#

新建 SpringBoot 項(xiàng)目common_utils,包名com.spring.utils

返回結(jié)果統(tǒng)一

創(chuàng)建code枚舉

在com.spring.utils中創(chuàng)建 pojo 包,并添加枚舉ResultCode

public enum ResultCode {

  /* 成功狀態(tài)碼 */
  SUCCESS(20000, "成功"),
  /* 參數(shù)錯(cuò)誤 */
  PARAM_IS_INVALID(1001, "參數(shù)無(wú)效"),
  PARAM_IS_BLANK(1002, "參數(shù)為空"),
  PARAM_TYPE_BIND_ERROR(1003, "參數(shù)類(lèi)型錯(cuò)誤"),
  PARAM_NOT_COMPLETE(1004, "參數(shù)缺失"),
  /* 用戶(hù)錯(cuò)誤 2001-2999*/
  USER_NOTLOGGED_IN(2001, "用戶(hù)未登錄"),
  USER_LOGIN_ERROR(2002, "賬號(hào)不存在或密碼錯(cuò)誤"),
  SYSTEM_ERROR(10000, "系統(tǒng)異常,請(qǐng)稍后重試");

  private Integer code;
  private String message;

  private ResultCode(Integer code, String message) {
    this.code = code;
    this.message = message;
  }

  public Integer code() {
    return this.code;
  }
  public String message() {
    return this.message;
  }
}

**可根據(jù)項(xiàng)目自定義,結(jié)果返回碼

創(chuàng)建返回結(jié)果實(shí)體

在 pojo 包中添加返回結(jié)果類(lèi)R**

@Data
@ApiModel(value = "返回結(jié)果實(shí)體類(lèi)", description = "結(jié)果實(shí)體類(lèi)")
public class R implements Serializable {

  private static final long serialVersionUID = 1L;

  @ApiModelProperty(value = "返回碼")
  private Integer code;

  @ApiModelProperty(value = "返回消息")
  private String message;

  @ApiModelProperty(value = "返回?cái)?shù)據(jù)")
  private Object data;

  private R() {

  }

  public R(ResultCode resultCode, Object data) {
    this.code = resultCode.code();
    this.message = resultCode.message();
    this.data = data;
  }

  private void setResultCode(ResultCode resultCode) {
    this.code = resultCode.code();
    this.message = resultCode.message();
  }

  // 返回成功
  public static R success() {
    R result = new R();
    result.setResultCode(ResultCode.SUCCESS);
    return result;
  }
  // 返回成功
  public static R success(Object data) {
    R result = new R();
    result.setResultCode(ResultCode.SUCCESS);
    result.setData(data);
    return result;
  }

  // 返回失敗
  public static R fail(Integer code, String message) {
    R result = new R();
    result.setCode(code);
    result.setMessage(message);
    return result;
  }
  // 返回失敗
  public static R fail(ResultCode resultCode) {
    R result = new R();
    result.setResultCode(resultCode);
    return result;
  }
}

自定義一個(gè)注解

新建包annotation,并添加ResponseResult注解類(lèi) 

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface ResponseResult {

}

定義攔截器

新建包interceptor,并添加ResponseResultInterceptorJava類(lèi)

@Component
public class ResponseResultInterceptor implements HandlerInterceptor {
  //標(biāo)記名稱(chēng)
  public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
    // TODO Auto-generated method stub
    if (handler instanceof HandlerMethod) {
      final HandlerMethod handlerMethod = (HandlerMethod) handler;
      final Class<?> clazz = handlerMethod.getBeanType();
      final Method method = handlerMethod.getMethod();
      // 判斷是否在類(lèi)對(duì)象上添加了注解
      if (clazz.isAnnotationPresent(ResponseResult.class)) {
        // 設(shè)置此請(qǐng)求返回體,需要包裝,往下傳遞,在ResponseBodyAdvice接口進(jìn)行判斷
        request.setAttribute(RESPONSE_RESULT_ANN, clazz.getAnnotation(ResponseResult.class));
      } else if (method.isAnnotationPresent(ResponseResult.class)) {
        request.setAttribute(RESPONSE_RESULT_ANN, method.getAnnotation(ResponseResult.class));
      }
    }
    return true;
  }
}

用于攔截請(qǐng)求,判斷 Controller 是否添加了@ResponseResult注解

注冊(cè)攔截器

新建包c(diǎn)onfig,并添加WebAppConfig配置類(lèi)

@Configuration
public class WebAppConfig implements WebMvcConfigurer {

    // SpringMVC 需要手動(dòng)添加攔截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        ResponseResultInterceptor interceptor = new ResponseResultInterceptor();
        registry.addInterceptor(interceptor);
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

方法返回值攔截處理器

新建包handler,并添加ResponseResultHandler配置類(lèi),實(shí)現(xiàn)ResponseBodyAdvice重寫(xiě)兩個(gè)方法

import org.springframework.web.method.HandlerMethod;
/**
 * 使用 @ControllerAdvice & ResponseBodyAdvice 
 * 攔截Controller方法默認(rèn)返回參數(shù),統(tǒng)一處理返回值/響應(yīng)體
 */
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
   
   // 標(biāo)記名稱(chēng)
   public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";

   
   // 判斷是否要執(zhí)行 beforeBodyWrite 方法,true為執(zhí)行,false不執(zhí)行,有注解標(biāo)記的時(shí)候處理返回值
   @Override
   public boolean supports(MethodParameter arg0, Class<? extends HttpMessageConverter<?>> arg1) {
      ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      HttpServletRequest request = sra.getRequest();
      // 判斷請(qǐng)求是否有包裝標(biāo)記
      ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ANN);
      return responseResultAnn == null ? false : true;
   }
   
   // 對(duì)返回值做包裝處理,如果屬于異常結(jié)果,則需要再包裝
   @Override
   public Object beforeBodyWrite(Object body, MethodParameter arg1, MediaType arg2,
                          Class<? extends HttpMessageConverter<?>> arg3, ServerHttpRequest arg4, ServerHttpResponse arg5) {
      if (body instanceof R) {
         return (R) body;
      }
      return R.success(body);
   }
}

實(shí)現(xiàn)ResponseBodyAdvice重寫(xiě)兩個(gè)方法

添加@ControllerAdvice注解

測(cè)試#

新建包c(diǎn)ontroller,并添加TestController測(cè)試類(lèi)

@RestController
@ResponseResult
public class TestController {

  @GetMapping("/test")
  public Map<String, Object> test() {
    HashMap<String, Object> data = new HashMap<>();
    data.put("info", "測(cè)試成功");
    return data;
  }
}

添加@ResponseResult注解

啟動(dòng)項(xiàng)目,在默認(rèn)端口: 8080

瀏覽器訪問(wèn)地址:localhost:8080/test

{"code":20000,"message":"成功","data":{"info":"測(cè)試成功"}}

總結(jié)

1、創(chuàng)建code枚舉和返回結(jié)果實(shí)體類(lèi)

2、自定義一個(gè)注解@ResponseResult

3、定義攔截器,攔截請(qǐng)求,判斷Controller上是否添加了@ResponseResult注解。如果添加了注解在request中添加注解標(biāo)記,往下傳遞

4、添加@ControllerAdvice注解 ,實(shí)現(xiàn)ResponseBodyAdvice接口,并重寫(xiě)兩個(gè)方法,通過(guò)判斷request中是否有注解標(biāo)記,如果有就往下執(zhí)行,進(jìn)一步包裝。沒(méi)有就直接返回,不需包裝。

問(wèn)題

1、如果要返回錯(cuò)誤結(jié)果,這種方法顯然不方便

@GetMapping("/fail")
public R error() {
  int res = 0; // 查詢(xún)結(jié)果數(shù)
  if( res == 0 ) {
    return R.fail(10001, "沒(méi)有數(shù)據(jù)");
  }
  return R.success(res);
}

2、我們需要對(duì)錯(cuò)誤和異常進(jìn)行進(jìn)一步的封裝

封裝錯(cuò)誤和異常結(jié)果#

創(chuàng)建錯(cuò)誤結(jié)果實(shí)體#

在包pojo中添加ErrorResult實(shí)體類(lèi)

/**
 * 異常結(jié)果包裝類(lèi)
 * @author sw-code
 *
 */
public class ErrorResult {

  private Integer code;

  private String message;

  private String exception;

  public Integer getCode() {
    return code;
  }

  public void setCode(Integer code) {
    this.code = code;
  }

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }

  public String getException() {
    return exception;
  }

  public void setException(String exception) {
    this.exception = exception;
  }

  public static ErrorResult fail(ResultCode resultCode, Throwable e, String message) {
    ErrorResult errorResult = ErrorResult.fail(resultCode, e);
    errorResult.setMessage(message);
    return errorResult;
  }

  public static ErrorResult fail(ResultCode resultCode, Throwable e) {
    ErrorResult errorResult = new ErrorResult();
    errorResult.setCode(resultCode.code());
    errorResult.setMessage(resultCode.message());
    errorResult.setException(e.getClass().getName());
    return errorResult;
  }
  public static ErrorResult fail(Integer code, String message) {
    ErrorResult errorResult = new ErrorResult();
    errorResult.setCode(code);
    errorResult.setMessage(message);
    return errorResult;
  }    
}

自定義異常類(lèi)

在包pojo中添加BizException實(shí)體類(lèi),繼承RuntimeException

@Data
public class BizException extends RuntimeException {

  /**
     * 錯(cuò)誤碼
     */
  private Integer code;

  /**
     * 錯(cuò)誤信息
     */
  private String message;

  public BizException() {
    super();
  }

  public BizException(ResultCode resultCode) {
    super(resultCode.message());
    this.code = resultCode.code();
    this.message = resultCode.message();
  }

  public BizException(ResultCode resultCode, Throwable cause) {
    super(resultCode.message(), cause);
    this.code = resultCode.code();
    this.message = resultCode.message();
  }

  public BizException(String message) {
    super(message);
    this.setCode(-1);
    this.message = message;
  }

  public BizException(Integer code, String message) {
    super(message);
    this.code = code;
    this.message = message;
  }

  public BizException(Integer code, String message, Throwable cause) {
    super(message, cause);
    this.code = code;
    this.message = message;
  }

  @Override
  public synchronized Throwable fillInStackTrace() {
    return this;
  }
}

全局異常處理類(lèi)

在包handler中添加GlobalExceptionHandler,添加@RestControllerAdvice注解

/**
 * 全局異常處理類(lèi)
 * @RestControllerAdvice(@ControllerAdvice),攔截異常并統(tǒng)一處理
 * @author sw-code
 *
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

  /**
     * 處理自定義的業(yè)務(wù)異常
     * @param e    異常對(duì)象
     * @param request    request
     * @return    錯(cuò)誤結(jié)果
     */
  @ExceptionHandler(BizException.class)
  public ErrorResult bizExceptionHandler(BizException e, HttpServletRequest request) {
    log.error("發(fā)生業(yè)務(wù)異常!原因是: {}", e.getMessage());
    return ErrorResult.fail(e.getCode(), e.getMessage());
  }

  // 攔截拋出的異常,@ResponseStatus:用來(lái)改變響應(yīng)狀態(tài)碼
  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  @ExceptionHandler(Throwable.class)
  public ErrorResult handlerThrowable(Throwable e, HttpServletRequest request) {
    log.error("發(fā)生未知異常!原因是: ", e);
    ErrorResult error = ErrorResult.fail(ResultCode.SYSTEM_ERROR, e);
    return error;
  }

  // 參數(shù)校驗(yàn)異常
  @ExceptionHandler(BindException.class)
  public ErrorResult handleBindExcpetion(BindException e, HttpServletRequest request) {
    log.error("發(fā)生參數(shù)校驗(yàn)異常!原因是:",e);
    ErrorResult error = ErrorResult.fail(ResultCode.PARAM_IS_INVALID, e, e.getAllErrors().get(0).getDefaultMessage());
    return error;
  }

  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
    log.error("發(fā)生參數(shù)校驗(yàn)異常!原因是:",e);
    ErrorResult error = ErrorResult.fail(ResultCode.PARAM_IS_INVALID,e,e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
    return error;
  }    
}

添加注解@RestControllerAdvice(@ControllerAdvice),攔截異常并統(tǒng)一處理

修改方法返回值攔截處理器#

將錯(cuò)誤和異常結(jié)果也進(jìn)行統(tǒng)一封裝

/**
 * 使用 @ControllerAdvice & ResponseBodyAdvice 
 * 攔截Controller方法默認(rèn)返回參數(shù),統(tǒng)一處理返回值/響應(yīng)體
 */
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {

  // 標(biāo)記名稱(chēng)
  public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";


  // 判斷是否要執(zhí)行 beforeBodyWrite 方法,true為執(zhí)行,false不執(zhí)行,有注解標(biāo)記的時(shí)候處理返回值
  @Override
  public boolean supports(MethodParameter arg0, Class<? extends HttpMessageConverter<?>> arg1) {
    ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = sra.getRequest();
    // 判斷請(qǐng)求是否有包裝標(biāo)記
    ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ANN);
    return responseResultAnn == null ? false : true;
  }


  // 對(duì)返回值做包裝處理,如果屬于異常結(jié)果,則需要再包裝
  @Override
  public Object beforeBodyWrite(Object body, MethodParameter arg1, MediaType arg2,
                                Class<? extends HttpMessageConverter<?>> arg3, ServerHttpRequest arg4, ServerHttpResponse arg5) {
    if (body instanceof ErrorResult) {
      ErrorResult error = (ErrorResult) body;
      return R.fail(error.getCode(), error.getMessage());
    } else if (body instanceof R) {
      return (R) body;
    }
    return R.success(body);
  }
}
測(cè)試#
Copy 
@GetMapping("/fail")
public Integer error() {
  int res = 0; // 查詢(xún)結(jié)果數(shù)
  if( res == 0 ) {
    throw new BizException("沒(méi)有數(shù)據(jù)");
  }
  return res;
}

返回結(jié)果

{"code":-1,"message":"沒(méi)有數(shù)據(jù)","data":null}

我們無(wú)需擔(dān)心返回類(lèi)型,如果需要返回錯(cuò)誤提示信息,可以直接拋出自定義異常(BizException),并添加自定義錯(cuò)誤信息。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 微信隨機(jī)生成紅包金額算法java版

    微信隨機(jī)生成紅包金額算法java版

    這篇文章主要為大家詳細(xì)介紹了java和php版的微信隨機(jī)生成紅包金額算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • 解決spring mvc 多數(shù)據(jù)源切換,不支持事務(wù)控制的問(wèn)題

    解決spring mvc 多數(shù)據(jù)源切換,不支持事務(wù)控制的問(wèn)題

    下面小編就為大家?guī)?lái)一篇解決spring mvc 多數(shù)據(jù)源切換,不支持事務(wù)控制的問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • Java正則表達(dá)式的基本用法和實(shí)例大全

    Java正則表達(dá)式的基本用法和實(shí)例大全

    這篇文章主要給大家介紹了關(guān)于Java正則表達(dá)式的基本用法和實(shí)例的相關(guān)資料,大家在使用Java正則表達(dá)式的時(shí)候可查閱這篇文章,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • springboot中bean的加載順序問(wèn)題

    springboot中bean的加載順序問(wèn)題

    這篇文章主要介紹了springboot中bean的加載順序問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java Feign微服務(wù)接口調(diào)用方法詳細(xì)講解

    Java Feign微服務(wù)接口調(diào)用方法詳細(xì)講解

    現(xiàn)如今微服務(wù)架構(gòu)十分流行,而采用微服務(wù)構(gòu)建系統(tǒng)也會(huì)帶來(lái)更清晰的業(yè)務(wù)劃分和可擴(kuò)展性。java如果使用微服務(wù)就離不開(kāi)springcloud,我這里是把服務(wù)注冊(cè)到nacos上,各個(gè)服務(wù)之間的調(diào)用使用feign
    2023-01-01
  • Spring-Cloud Eureka注冊(cè)中心實(shí)現(xiàn)高可用搭建

    Spring-Cloud Eureka注冊(cè)中心實(shí)現(xiàn)高可用搭建

    這篇文章主要介紹了Spring-Cloud Eureka注冊(cè)中心實(shí)現(xiàn)高可用搭建,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • 詳解Java爬蟲(chóng)利器Jsoup

    詳解Java爬蟲(chóng)利器Jsoup

    Jsoup是一款Java語(yǔ)言開(kāi)發(fā)的HTML解析器,用于解析HTML文檔以及對(duì)HTML文檔進(jìn)行操作,處理等,本文就將詳細(xì)給大家介紹一下Java中的爬蟲(chóng)利器Jsoup,感興趣的同學(xué)可以參考一下
    2023-06-06
  • Java String字符串補(bǔ)0或空格的實(shí)現(xiàn)代碼

    Java String字符串補(bǔ)0或空格的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Java String字符串補(bǔ)0或空格的實(shí)現(xiàn)代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧
    2016-09-09
  • Spring核心容器之BeanDefinition解析

    Spring核心容器之BeanDefinition解析

    這篇文章主要介紹了Spring核心容器之BeanDefinition解析,Spring 將管理的對(duì)象稱(chēng)之為 Bean,容器會(huì)先實(shí)例化 Bean,然后自動(dòng)注入,實(shí)例化的過(guò)程就需要依賴(lài) BeanDefinition,需要的朋友可以參考下
    2023-11-11
  • SpringBoot Redis實(shí)現(xiàn)接口冪等性校驗(yàn)方法詳細(xì)講解

    SpringBoot Redis實(shí)現(xiàn)接口冪等性校驗(yàn)方法詳細(xì)講解

    這篇文章主要介紹了SpringBoot Redis實(shí)現(xiàn)接口冪等性校驗(yàn)方法,近期一個(gè)老項(xiàng)目出現(xiàn)了接口冪等性校驗(yàn)問(wèn)題,前端加了按鈕置灰,依然被人拉著接口參數(shù)一頓輸出,還是重復(fù)調(diào)用了接口,通過(guò)復(fù)制粘貼,完成了后端接口冪等性調(diào)用校驗(yàn)
    2022-11-11

最新評(píng)論

福海县| 桂东县| 苗栗市| 铜山县| 安丘市| 柯坪县| 凉城县| 东源县| 眉山市| 光泽县| 德惠市| 道真| 芜湖县| 长汀县| 延边| 桑日县| 阳谷县| 改则县| 巴中市| 乌苏市| 樟树市| 克拉玛依市| 如东县| 中方县| 盐池县| 德兴市| 同心县| 桃园市| 乌拉特后旗| 漳浦县| 昌宁县| 扎兰屯市| 井冈山市| 久治县| 尉犁县| 互助| 东光县| 蒲江县| 莒南县| 玉溪市| 沙湾县|