欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot項目異常處理及返回結(jié)果統(tǒng)一

 更新時間:2022年08月08日 10:34:18   作者:夢想實現(xiàn)家_Z  
這篇文章主要介紹了Springboot項目異常處理及返回結(jié)果統(tǒng)一,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下

背景

在創(chuàng)建項目的初期,我們需要規(guī)范后端返回的數(shù)據(jù)結(jié)構(gòu),以便更好地與前端開發(fā)人員合作。

比如后端返回的數(shù)據(jù)為:

{
 ?"msg": "請?zhí)D(zhuǎn)登陸頁面",
}

此時前端無法確定后端服務(wù)的處理結(jié)果是成功的還是失敗的。在前端展示頁面,成功與失敗的展示是要作區(qū)分的,甚至不同的成功或失敗結(jié)果要做出不同的展現(xiàn)效果,這也就是我們?yōu)槭裁匆獙Ψ祷亟Y(jié)果做出統(tǒng)一規(guī)范的原因。

返回結(jié)果定義

public class ResultWrap<T, M> {
  //  方便前端判斷當(dāng)前請求處理結(jié)果是否正常
  private int code;
  //  業(yè)務(wù)處理結(jié)果
  private T data;
  //  產(chǎn)生錯誤的情況下,提示用戶信息
  private String message;
  //  產(chǎn)生錯誤情況下的異常堆棧,提示開發(fā)人員
  private String error;
  //  發(fā)生錯誤的時候,返回的附加信息
  private M metaInfo;
}
  • 1.為了把模糊的消息定性,我們給所有的返回結(jié)果都帶上一個code字段,前端可以根據(jù)這個字段來判斷我們的處理結(jié)果到底是成功的還是失敗的;比如code=200的時候,我們的處理結(jié)果一定是成功的,其他的code值全都是失敗的,并且我們會有多種code值,以便前端頁面可以根據(jù)不同的code值做出不同的交互動作。
  • 2.一般在處理成功的情況下,后端會返回一些業(yè)務(wù)數(shù)據(jù),比如返回訂單列表等,那么這些數(shù)據(jù)我們就放在字段data里面,只有業(yè)務(wù)邏輯處理成功的情況下,data字段里面才可能有數(shù)據(jù)。
  • 3.如若我們的處理失敗了,那么我們應(yīng)該會有對應(yīng)的消息提醒用戶為什么處理失敗了。比如文件上傳失敗了,用戶名或密碼錯誤等等,都是需要告知用戶的。
  • 4.除了需要告知用戶失敗原因,我們也需要保留一個字段給開發(fā)人員,當(dāng)錯誤是服務(wù)器內(nèi)部錯誤的時候,我們需要讓開發(fā)人員能第一時間定位是啥原因引起的,我們會把錯誤的堆棧信息給到error字段。
  • 5.當(dāng)發(fā)生異常的時候,我們還會需要返回一些額外的補充數(shù)據(jù)給前端,比如用戶登陸失敗一次和失敗多次需要不同的交互效果,此時我們會在metaInfo里面返回登陸失敗次數(shù);或者在用戶操作沒有權(quán)限的時候,根據(jù)用戶是否已登陸來確定此時是跳轉(zhuǎn)登陸頁面還是直接彈窗提示當(dāng)前操作沒有權(quán)限。

定義好返回結(jié)果后,我們和前端的交互數(shù)據(jù)結(jié)果就統(tǒng)一好了。

異常的定義

之所以定義一個統(tǒng)一的異常類,是為了把所有的異常全部匯總成一個異常,最終我們只需要在異常處理的時候單獨處理這一個異常即可。

@Data
public class AwesomeException extends Throwable {
 ?// 錯誤碼
  private int code;
 ?// 提示消息
  private String msg;
?
  public AwesomeException(int code, String msg, Exception e) {
    super(e);
    this.code = code;
    this.msg = msg;
  }
?
  public AwesomeException(int code, String msg) {
    this.code = code;
    this.msg = msg;
  }
}
  • 1.我們同樣需要一個與返回結(jié)果一致的code字段,這樣的話,我們在異常處理的時候,才能把當(dāng)前異常轉(zhuǎn)換成最終的ResultWrap結(jié)果。
  • 2.msg就是在產(chǎn)生異常的時候,需要給到用戶的提示消息。

這樣的話,我們的后端開發(fā)人員遇到異常的情況,只需要通過創(chuàng)建AwesomeException異常對象拋出即可,不需要再為創(chuàng)建什么異常而煩惱了。

異常的處理

我們下面需要針對所有拋出的異常進行統(tǒng)一的處理:

import com.example.awesomespring.exception.AwesomeException;
import com.example.awesomespring.vo.ResultWrap;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
?
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @className ExceptionHandler
 * @description:
 */
@Slf4j
@RestControllerAdvice
public class AwesomeExceptionHandler {
  /**
   * 捕獲沒有用戶權(quán)限的異常
   *
   * @return
   */
  @ExceptionHandler(AuthorizationException.class)
  public ResultWrap handleException(AuthorizationException e) {
    return ResultWrap.failure(401, "您暫時沒有訪問權(quán)限!", e);
  }
?
  /**
   * 處理AwesomeException
   *
   * @param e
   * @param request
   * @param response
   * @return
   */
  @ExceptionHandler(AwesomeException.class)
  public ResultWrap handleAwesomeException(AwesomeException e, HttpServletRequest request, HttpServletResponse response) {
    return ResultWrap.failure(e);
  }
?
  /**
   * 專門針對運行時異常
   *
   * @param e
   * @return
   */
  @ExceptionHandler(RuntimeException.class)
  public ResultWrap handleRuntimeException(RuntimeException e) {
    return ResultWrap.failure(e);
  }
}

在項目中,我們集成了shiro權(quán)限管理框架,因為它拋出的異常沒有被我們的AwesomeException包裝,所以這個AuthorizationException異常需要我們單獨處理。

AwesomeException是我們大多數(shù)業(yè)務(wù)邏輯拋出來的異常,我們根據(jù)AwesomeException里面的code、msg和它包裝的cause,封裝成一個最終的響應(yīng)數(shù)據(jù)ResultWrap。

另一個RuntimeException是必須要額外處理的,任何開發(fā)人員都無法保證自己的代碼是完全沒有bug的,任何的空指針異常都會影響用戶體驗,這種編碼性的錯誤我們需要通過統(tǒng)一的錯誤處理讓它變得更柔和一點。

返回結(jié)果的處理

我們需要針對所有的返回結(jié)果進行檢查,如果不是ResultWrap類型的返回數(shù)據(jù),我們需要包裝一下,以便保證我們和前端開發(fā)人員達成的共識。

import com.example.awesomespring.vo.ResultWrap;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.Objects;
?
/**
 * @className AwesomeResponseAdvice
 * @description:
 */
@RestControllerAdvice
public class AwesomeResponseAdvice implements ResponseBodyAdvice {
  @Override
  public boolean supports(MethodParameter returnType, Class converterType) {
 ? ?// 如果返回String,那么就不包裝了。
    if (StringHttpMessageConverter.class.isAssignableFrom(converterType)) {
      return false;
    }
 ? ?// 有一些情況是不需要包裝的,比如調(diào)用第三方API返回的數(shù)據(jù),所以我們做了一個自定義注解來避免所以的結(jié)果都被包裝成ResultWrap。
    boolean ignore = false;
    IgnoreResponseAdvice ignoreResponseAdvice =
        returnType.getMethodAnnotation(IgnoreResponseAdvice.class);
 ? ?// 如果我們在方法上添加了IgnoreResponseAdvice注解,那么就不要攔截包裝了
    if (Objects.nonNull(ignoreResponseAdvice)) {
      ignore = ignoreResponseAdvice.value();
      return !ignore;
    }
 ? ?// 如果我們在類上面添加了IgnoreResponseAdvice注解,也在方法上面添加了IgnoreResponseAdvice注解,那么以方法上的注解為準。
    Class<?> clazz = returnType.getDeclaringClass();
    ignoreResponseAdvice = clazz.getDeclaredAnnotation(IgnoreResponseAdvice.class);
    RestController restController = clazz.getDeclaredAnnotation(RestController.class);
    if (Objects.nonNull(ignoreResponseAdvice)) {
      ignore = ignoreResponseAdvice.value();
    } else if (Objects.isNull(restController)) {
      ignore = true;
    }
    return !ignore;
  }
?
  @Override
  public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
 ? ?// 如果返回結(jié)果為null,那么我們直接返回ResultWrap.success()
    if (Objects.isNull(body)) {
      return ResultWrap.success();
    }
 ? ?// // 如果返回結(jié)果已經(jīng)是ResultWrap,直接返回
    if (body instanceof ResultWrap) {
      return body;
    }
 ? ?// 否則我們把返回結(jié)果包裝成ResultWrap
    return ResultWrap.success(body);
  }
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
?
/**
 * @className IgnoreResponseAdvice
 * @description:
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreResponseAdvice {
 ?// 是否忽略ResponseAdvice;決定了是否要包裝返回數(shù)據(jù)
  boolean value() default true;
}

至此,我們把整個異常處理與返回結(jié)果的統(tǒng)一處理全部關(guān)聯(lián)起來了,我們后端的開發(fā)人員無論是返回異常還是返回ResultWrap或者其他數(shù)據(jù)結(jié)果,都能很好地保證與前端開發(fā)人員的正常協(xié)作,不必為數(shù)據(jù)結(jié)構(gòu)的變化過多地溝通。

完整代碼

ResultWrap.java

import com.example.awesomespring.exception.AwesomeException;
import com.example.awesomespring.util.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
?
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Objects;
?
/**
 * @className ResultWrap
 * @description:
 */
@Data
@AllArgsConstructor
public class ResultWrap<T, M> {
  //  方便前端判斷當(dāng)前請求處理結(jié)果是否正常
  private int code;
  //  業(yè)務(wù)處理結(jié)果
  private T data;
  //  產(chǎn)生錯誤的情況下,提示用戶信息
  private String message;
  //  產(chǎn)生錯誤情況下的異常堆棧,提示開發(fā)人員
  private String error;
  //  發(fā)生錯誤的時候,返回的附加信息
  private M metaInfo;
?
  /**
   * 成功帶處理結(jié)果
   *
   * @param data
   * @param <T>
   * @return
   */
  public static <T> ResultWrap success(T data) {
    return new ResultWrap(HttpStatus.OK.value(), data, StringUtils.EMPTY, StringUtils.EMPTY, null);
  }
?
  /**
   * 成功不帶處理結(jié)果
   *
   * @return
   */
  public static ResultWrap success() {
    return success(HttpStatus.OK.name());
  }
?
  /**
   * 失敗
   *
   * @param code
   * @param message
   * @param error
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, String error, M metaInfo) {
    return new ResultWrap(code, null, message, error, metaInfo);
  }
?
  /**
   * 失敗
   *
   * @param code
   * @param message
   * @param error
   * @param metaInfo
   * @param <M>
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, Throwable error, M metaInfo) {
    String errorMessage = StringUtils.EMPTY;
    if (Objects.nonNull(error)) {
      errorMessage = toStackTrace(error);
    }
    return failure(code, message, errorMessage, metaInfo);
  }
?
  /**
   * 失敗
   *
   * @param code
   * @param message
   * @param error
   * @return
   */
  public static ResultWrap failure(int code, String message, Throwable error) {
    return failure(code, message, error, null);
  }
?
  /**
   * 失敗
   *
   * @param code
   * @param message
   * @param metaInfo
   * @param <M>
   * @return
   */
  public static <M> ResultWrap failure(int code, String message, M metaInfo) {
    return failure(code, message, StringUtils.EMPTY, metaInfo);
  }
?
  /**
   * 失敗
   *
   * @param e
   * @return
   */
  public static ResultWrap failure(AwesomeException e) {
    return failure(e.getCode(), e.getMsg(), e.getCause());
  }
?
?
  /**
   * 失敗
   *
   * @param e
   * @return
   */
  public static ResultWrap failure(RuntimeException e) {
    return failure(500, "服務(wù)異常,請稍后訪問!", e.getCause());
  }
?
  private static final String APPLICATION_JSON_VALUE = "application/json;charset=UTF-8";
?
  /**
   * 把結(jié)果寫入響應(yīng)中
   *
   * @param response
   */
  public void writeToResponse(HttpServletResponse response) {
    int code = this.getCode();
    if (Objects.isNull(HttpStatus.resolve(code))) {
      response.setStatus(HttpStatus.OK.value());
    } else {
      response.setStatus(code);
    }
    response.setContentType(APPLICATION_JSON_VALUE);
    try (PrintWriter writer = response.getWriter()) {
      writer.write(JsonUtil.obj2String(this));
      writer.flush();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
?
  /**
   * 獲取異常堆棧信息
   *
   * @param e
   * @return
   */
  private static String toStackTrace(Throwable e) {
    if (Objects.isNull(e)) {
      return StringUtils.EMPTY;
    }
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    try {
      e.printStackTrace(pw);
      return sw.toString();
    } catch (Exception e1) {
      return StringUtils.EMPTY;
    }
  }
}

AwesomeException.java

?import lombok.Data;
?
/**
 * @className AwesomeException
 * @description:
 */
@Data
public class AwesomeException extends Throwable {
?
  private int code;
  private String msg;
  public AwesomeException(int code, String msg, Exception e) {
    super(e);
    this.code = code;
    this.msg = msg;
  }
  public AwesomeException(int code, String msg) {
    this.code = code;
    this.msg = msg;
  }
}

AwesomeExceptionHandler.java

import com.example.awesomespring.exception.AwesomeException;
import com.example.awesomespring.vo.ResultWrap;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
?
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
?
/**
 * @className ExceptionHandler
 * @description:
 */
@Slf4j
@RestControllerAdvice
public class AwesomeExceptionHandler {
  /**
   * 捕獲沒有用戶權(quán)限的異常
   *
   * @return
   */
  @ExceptionHandler(AuthorizationException.class)
  public ResultWrap handleException(AuthorizationException e) {
    return ResultWrap.failure(401, "您暫時沒有訪問權(quán)限!", e);
  }
?
  /**
   * 處理AwesomeException
   *
   * @param e
   * @param request
   * @param response
   * @return
   */
  @ExceptionHandler(AwesomeException.class)
  public ResultWrap handleAwesomeException(AwesomeException e, HttpServletRequest request, HttpServletResponse response) {
    return ResultWrap.failure(e);
  }
?
  /**
   * 專門針對運行時異常
   *
   * @param e
   * @return
   */
  @ExceptionHandler(RuntimeException.class)
  public ResultWrap handleRuntimeException(RuntimeException e) {
    return ResultWrap.failure(e);
  }
}

IgnoreResponseAdvice.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
?
/**
 * @className IgnoreResponseAdvice
 * @description:
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreResponseAdvice {
?
  boolean value() default true;
}

AwesomeResponseAdvice.java

import com.example.awesomespring.vo.ResultWrap;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
?
import java.util.Objects;
?
/**
 * @className AwesomeResponseAdvice
 * @description:
 */
@RestControllerAdvice
public class AwesomeResponseAdvice implements ResponseBodyAdvice {
  @Override
  public boolean supports(MethodParameter returnType, Class converterType) {
    if (StringHttpMessageConverter.class.isAssignableFrom(converterType)) {
      return false;
    }
    boolean ignore = false;
    IgnoreResponseAdvice ignoreResponseAdvice =
        returnType.getMethodAnnotation(IgnoreResponseAdvice.class);
    if (Objects.nonNull(ignoreResponseAdvice)) {
      ignore = ignoreResponseAdvice.value();
      return !ignore;
    }
    Class<?> clazz = returnType.getDeclaringClass();
    ignoreResponseAdvice = clazz.getDeclaredAnnotation(IgnoreResponseAdvice.class);
    RestController restController = clazz.getDeclaredAnnotation(RestController.class);
    if (Objects.nonNull(ignoreResponseAdvice)) {
      ignore = ignoreResponseAdvice.value();
    } else if (Objects.isNull(restController)) {
      ignore = true;
    }
    return !ignore;
  }
?
  @Override
  public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    if (Objects.isNull(body)) {
      return ResultWrap.success();
    }
    if (body instanceof ResultWrap) {
      return body;
    }
    return ResultWrap.success(body);
  }
}

使用示例

// 這里只要返回AwesomeException,就會被ExceptionHandler處理掉,包裝成ResultWrap
@PostMapping("/image/upload")
String upload(@RequestPart("userImage") MultipartFile userImage) throws AwesomeException {
    fileService.putObject("video", userImage);
    return "success";
}
?
// 加上IgnoreResponseAdvice注解,該返回結(jié)果就不會被包裝
@IgnoreResponseAdvice
@GetMapping("/read")
Boolean read() {
    return true;
}

到此這篇關(guān)于Springboot項目異常處理及返回結(jié)果統(tǒng)一的文章就介紹到這了,更多相關(guān)Springboot異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot使用hibernate validation對參數(shù)校驗的實現(xiàn)方法

    springboot使用hibernate validation對參數(shù)校驗的實現(xiàn)方法

    這篇文章主要介紹了spring-boot 使用hibernate validation對參數(shù)進行優(yōu)雅的校驗,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Java語言中的內(nèi)存泄露代碼詳解

    Java語言中的內(nèi)存泄露代碼詳解

    這篇文章主要介紹了Java語言中的內(nèi)存泄露代碼詳解,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • Java8?Stream流的常用方法匯總

    Java8?Stream流的常用方法匯總

    Java8?API添加了一個新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于Java8?Stream流的常用方法,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • Java調(diào)用HTTPS接口實現(xiàn)繞過SSL認證

    Java調(diào)用HTTPS接口實現(xiàn)繞過SSL認證

    SSL認證是確保通信安全的重要手段,有的時候為了方便調(diào)用,我們會繞過SSL認證,這篇文章主要介紹了Java如何調(diào)用HTTPS接口實現(xiàn)繞過SSL認證,需要的可以參考下
    2023-11-11
  • 使用Java實現(xiàn)6種常見負載均衡算法

    使用Java實現(xiàn)6種常見負載均衡算法

    Java負載均衡算法也是分布式系統(tǒng)中的重要組成部分,用于將來自客戶端的請求分配到不同的后端服務(wù)器上,本文將介紹常見的Java負載均衡算法,輪詢法、加權(quán)隨機法……一次性讓你了解?6?種常見負載均衡算法
    2023-06-06
  • Java 通過位運算求一個集合的所有子集方法

    Java 通過位運算求一個集合的所有子集方法

    下面小編就為大家?guī)硪黄狫ava 通過位運算求一個集合的所有子集方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • 詳解JNI到底是什么

    詳解JNI到底是什么

    JNI是Java Native Interface的縮寫,通過使用 Java本地接口書寫程序,可以確保代碼在不同的平臺上方便移植。從Java1.1開始,JNI標準成為java平臺的一部分,它允許Java代碼和其他語言寫的代碼進行交互
    2021-06-06
  • 深入學(xué)習(xí)java8?中的CompletableFuture

    深入學(xué)習(xí)java8?中的CompletableFuture

    本文主要介紹了java8中的CompletableFuture,CompletableFuture實現(xiàn)了CompletionStage接口和Future接口,前者是對后者的一個擴展,增加了異步回調(diào)、流式處理、多個Future組合處理的能力,使Java在處理多任務(wù)的協(xié)同工作時更加順暢便利,下文需要的朋友可以參考一下
    2022-05-05
  • 解決springboot利用ConfigurationProperties注解配置數(shù)據(jù)源無法讀取配置信息問題

    解決springboot利用ConfigurationProperties注解配置數(shù)據(jù)源無法讀取配置信息問題

    今天在學(xué)習(xí)springboot利用ConfigurationProperties注解配置數(shù)據(jù)源的使用遇到一個問題無法讀取配置信息,發(fā)現(xiàn)全部為null,糾結(jié)是哪里出了問題呢,今天一番思考,問題根源找到,下面把我的解決方案分享到腳本之家平臺,感興趣的朋友一起看看吧
    2021-05-05
  • java中刪除文件/文件夾的3種方法示例小結(jié)

    java中刪除文件/文件夾的3種方法示例小結(jié)

    這篇文章主要介紹了java中刪除文件/文件夾的3種方法示例小結(jié),第一種是通過io刪除文件,第二種是通過Files.walk刪除文件,第三種是通過 Files.walkFileTree刪除文件,本文結(jié)合示例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2023-10-10

最新評論