一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - SpringBoot中如何統(tǒng)一接口返回與全局異常處理詳解

SpringBoot中如何統(tǒng)一接口返回與全局異常處理詳解

2021-12-15 11:59l拉不拉米 Java教程

全局異常處理是個(gè)比較重要的功能,一般在項(xiàng)目里都會(huì)用到,這篇文章主要給大家介紹了關(guān)于SpringBoot中如何統(tǒng)一接口返回與全局異常處理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下

背景

在分布式、微服務(wù)盛行的今天,絕大部分項(xiàng)目都采用的微服務(wù)框架,前后端分離方式。前端和后端進(jìn)行交互,前端按照約定請(qǐng)求URL路徑,并傳入相關(guān)參數(shù),后端服務(wù)器接收請(qǐng)求,進(jìn)行業(yè)務(wù)處理,返回?cái)?shù)據(jù)給前端。維護(hù)一套完善且規(guī)范的接口是非常有必要的, 這樣不僅能夠提高對(duì)接效率,也可以讓我的代碼看起來(lái)更加簡(jiǎn)潔優(yōu)雅。

使用統(tǒng)一返回結(jié)果時(shí),還有一種情況,就是程序的報(bào)錯(cuò)是由于運(yùn)行時(shí)異常導(dǎo)致的結(jié)果,有些異常是我們?cè)跇I(yè)務(wù)中拋出的,有些是無(wú)法提前預(yù)知。

因此,我們需要定義一個(gè)統(tǒng)一的全局異常,在Controller捕獲所有異常,并且做適當(dāng)處理,并作為一種結(jié)果返回。

 

統(tǒng)一接口返回

定義API返回碼枚舉類

public enum ResultCode {
/* 成功狀態(tài)碼 */
SUCCESS(200, "成功"),

/* 錯(cuò)誤狀態(tài)碼 */
NOT_FOUND(404, "請(qǐng)求的資源不存在"),
INTERNAL_ERROR(500, "服務(wù)器內(nèi)部錯(cuò)誤"),
PARAMETER_EXCEPTION(501, "請(qǐng)求參數(shù)校驗(yàn)異常"),

/* 業(yè)務(wù)狀態(tài)碼 */
USER_NOT_EXIST_ERROR(10001, "用戶不存在"),
 ;

private Integer code;
private String message;

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

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

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

定義正常響應(yīng)的API統(tǒng)一返回體

@Data
public class Result<T> implements Serializable {
private Integer code;
private String message;
private boolean success = true;
private T data;
@JsonIgnore
private ResultCode resultCode;

private Result() {
 }

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

public Result(ResultCode resultCode, T data) {
  this.code = resultCode.code();
  this.message = resultCode.message();
  this.data = data;
 }

public static <T> Result<T> success() {
  Result<T> result = new Result<>();
  result.setResultCode(ResultCode.SUCCESS);
  return result;
 }

public static <T> Result<T> success(T data) {
  Result<T> result = new Result<>();
  result.setResultCode(ResultCode.SUCCESS);
  result.setData(data);
    return result;
 }
}

定義異常響應(yīng)的API統(tǒng)一返回體

@Data
public class ErrorResult implements Serializable {
private Integer code;
private String message;
private boolean success = false;
@JsonIgnore
private ResultCode resultCode;

public static ErrorResult error() {
  ErrorResult result = new ErrorResult();
  result.setResultCode(ResultCode.INTERNAL_ERROR);
  return result;
 }

public static ErrorResult error(String message) {
  ErrorResult result = new ErrorResult();
  result.setCode(ResultCode.INTERNAL_ERROR.code());
  result.setMessage(message);
  return result;
 }

public static ErrorResult error(Integer code, String message) {
  ErrorResult result = new ErrorResult();
  result.setCode(code);
  result.setMessage(message);
  return result;
 }

public static ErrorResult error(ResultCode resultCode, String message) {
  ErrorResult result = new ErrorResult();
  result.setResultCode(resultCode);
  result.setMessage(message)
  return result;
 }
}

編寫包裝返回結(jié)果的自定義注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD}) //作用于方法和類(接口)上
@Documented
public @interface ResponseResult {
}

定義返回結(jié)果攔截器

@Component
public class ResponseResultInterceptor implements HandlerInterceptor {
/* 使用統(tǒng)一返回體的標(biāo)識(shí) */
private static final String RESPONSE_RESULT_ANNOTATION = "RESPONSE-RESULT-ANNOTATION";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
  // 正在處理請(qǐng)求的方法bean
  if (handler instanceof HandlerMethod) {
    final HandlerMethod handlerMethod = (HandlerMethod) handler;
    // 獲取當(dāng)前類
    final Class<?> clazz = handlerMethod.getBeanType();
    // 獲取當(dāng)前方法
    final Method method = handlerMethod.getMethod();
    // 判斷是否在類對(duì)象上加了注解
    if (clazz.isAnnotationPresent(ResponseResult.class)) {
      // 設(shè)置該請(qǐng)求返回體,需要包裝,往下傳遞,在ResponseBodyAdvice接口進(jìn)行判斷
      request.setAttribute(RESPONSE_RESULT_ANNOTATION, clazz.getAnnotation(ResponseResult.class));
     }
    // 判斷是否在方法上加了注解
    else if (method.isAnnotationPresent(ResponseResult.class)) {
      // 設(shè)置該請(qǐng)求返回體,需要包裝,往下傳遞,在ResponseBodyAdvice接口進(jìn)行判斷
      request.setAttribute(RESPONSE_RESULT_ANNOTATION, method.getAnnotation(ResponseResult.class));
     }
   }
  return true;
 }
}

WebMvc配置類攔截器注冊(cè)者添加返回結(jié)果攔截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
 * 添加自定義攔截器
 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
  registry.addInterceptor(new ResponseResultInterceptor()).addPathPatterns("/**");
 }
}

編寫響應(yīng)體處理器

/**
* 統(tǒng)一處理響應(yīng)體,用Result.success靜態(tài)方法包裝,
* 在API接口使用時(shí)就可以直接返回原始類型
*/
@RestControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
/* 使用統(tǒng)一返回體的標(biāo)識(shí) */
private static final String RESPONSE_RESULT_ANNOTATION = "RESPONSE-RESULT-ANNOTATION";

@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
  ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  HttpServletRequest request = Objects.requireNonNull(sra).getRequest();
  ResponseResult responseResult = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ANNOTATION);
  // 判斷返回體是否需要處理
  return responseResult != null;
 }

@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
  // 異常響應(yīng)體則直接返回code+message的消息體
  if (body instanceof ErrorResult) {
    return body;
   }
  // 正常響應(yīng)體則返回Result包裝的code+message+data的消息體
  return Result.success(body);
 }
}

接口調(diào)用

@Api("用戶管理")
@RestController
@RequestMapping("user")
@ResponseResult // 作用于類上,對(duì)所有接口有效
public class UserController {

@Autowired
private UserService userService;

@ResponseResult // 作用于方法上
@ApiOperation("根據(jù)ID查詢用戶")
@GetMapping("one")
public User selectOne(Long id) {
  // 由于在ResponseResultHandler中已經(jīng)統(tǒng)一將返回?cái)?shù)據(jù)用Result.success包裝了,
  // 直接返回原始類型即可,代碼更簡(jiǎn)潔
  return this.userService.queryById(id);
 }

@ResponseResult
@ApiOperation("查詢所有用戶")
@GetMapping("all")
public List<User> selectAll(Page page) {
  // 由于在ResponseResultHandler中已經(jīng)統(tǒng)一將返回?cái)?shù)據(jù)用Result.success包裝了,
  // 直接返回原始類型即可,代碼更簡(jiǎn)潔
  return this.userService.queryAllByLimit(page);
 }
}

測(cè)試結(jié)果

SpringBoot中如何統(tǒng)一接口返回與全局異常處理詳解

SpringBoot中如何統(tǒng)一接口返回與全局異常處理詳解

 

全局異常處理

編寫自定義異常基類

@Data
public class BaseException extends RuntimeException {

private static final int BASE_EXCEPTION_CODE = ResultCode.INTERNAL_ERROR.code();
private static final String BASE_EXCEPTION_MESSAGE = ResultCode.INTERNAL_ERROR.message();

private Integer code;
private String message;

public BaseException() {
  super(BASE_EXCEPTION_MESSAGE);
  this.code = BASE_EXCEPTION_CODE;
  this.message = BASE_EXCEPTION_MESSAGE;
 }

public BaseException(String message) {
  super(message);
  this.code = BASE_EXCEPTION_CODE;
  this.message = message;
 }

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

public BaseException(Throwable cause) {
  super(cause);
  this.code = BASE_EXCEPTION_CODE;
  this.message = BASE_EXCEPTION_MESSAGE;
 }

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

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

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

編寫自定義業(yè)務(wù)異常類

public class BizException extends BaseException {
public BizException(ResultCode resultCode) {
  super(resultCode);
 }
}

定義全局異常處理類

通過(guò)@ExceptionHandler注解來(lái)統(tǒng)一處理某一類異常

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
 * 統(tǒng)一處理自定義基礎(chǔ)異常
 */
@ExceptionHandler(BaseException.class)
public ErrorResult baseException(BaseException e) {
  if (StringUtils.isEmpty(e.getCode())) {
    return ErrorResult.error(e.getMessage());
   }
  return ErrorResult.error(e.getCode(), e.getMessage());
 }

/**
 * 統(tǒng)一處理自定義業(yè)務(wù)異常
 */
@ExceptionHandler(BizException.class)
public ErrorResult bizException(BizException e) {
  if (StringUtils.isEmpty(e.getCode())) {
    return ErrorResult.error(e.getMessage());
   }
  return ErrorResult.error(e.getCode(), e.getMessage());
 }

/**
 * 統(tǒng)一處理非自定義異常外的所有異常
 */
@ExceptionHandler(Exception.class)
public ErrorResult handleException(Exception e) {
  log.error(e.getMessage(), e);
  return ErrorResult.error(e.getMessage());
 }

/**
 * 兼容Validation校驗(yàn)框架:忽略參數(shù)異常處理器
 */
@ExceptionHandler(MissingServletRequestParameterException.class)
public ApiResult<String> parameterMissingExceptionHandler(MissingServletRequestParameterException e) {
  log.error(e.getMessage(), e);
  return ErrorResult.error(PARAMETER_EXCEPTION, "請(qǐng)求參數(shù) " + e.getParameterName() + " 不能為空");
 }

/**
 * 兼容Validation校驗(yàn)框架:缺少請(qǐng)求體異常處理器
 */
@ExceptionHandler(HttpMessageNotReadableException.class)
public ErrorResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
  log.error(e.getMessage(), e);
  return ErrorResult.error(PARAMETER_EXCEPTION, "參數(shù)體不能為空");
 }

/**
 * 兼容Validation校驗(yàn)框架:參數(shù)效驗(yàn)異常處理器
 */
@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResult parameterExceptionHandler(MethodArgumentNotValidException e) {
  log.error(e.getMessage(), e);
  // 獲取異常信息
  BindingResult exceptions = e.getBindingResult();
  // 判斷異常中是否有錯(cuò)誤信息,如果存在就使用異常中的消息,否則使用默認(rèn)消息
  if (exceptions.hasErrors()) {
    List<ObjectError> errors = exceptions.getAllErrors();
    if (!errors.isEmpty()) {
      // 這里列出了全部錯(cuò)誤參數(shù),按正常邏輯,只需要第一條錯(cuò)誤即可
      FieldError fieldError = (FieldError) errors.get(0);
      return ErrorResult.error(PARAMETER_EXCEPTION, fieldError.getDefaultMessage());
     }
   }
  return ErrorResult.error(PARAMETER_EXCEPTION, "請(qǐng)求參數(shù)校驗(yàn)異常");
 }
}

接口調(diào)用

  @ResponseResult
@GetMapping
public User update() {
  // 非自定義的運(yùn)行時(shí)異常
  long id = 10 / 0;
  return userService.queryById(id);
 }

@ResponseResult
@PostMapping
public User insert() {
  // 拋出自定義的基礎(chǔ)異常
  throw new BaseException();
 }

@ResponseResult
@DeleteMapping
public boolean delete() {
  // 拋出自定義的業(yè)務(wù)異常
  throw new BizException(USER_NOT_EXIST_ERROR);
 }

測(cè)試結(jié)果

SpringBoot中如何統(tǒng)一接口返回與全局異常處理詳解

SpringBoot中如何統(tǒng)一接口返回與全局異常處理詳解

SpringBoot中如何統(tǒng)一接口返回與全局異常處理詳解

 

總結(jié)

到此這篇關(guān)于SpringBoot中如何統(tǒng)一接口返回與全局異常處理的文章就介紹到這了,更多相關(guān)SpringBoot統(tǒng)一接口返回內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://juejin.cn/post/7002926832160866318

延伸 · 閱讀

精彩推薦
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java8中Stream使用的一個(gè)注意事項(xiàng)

    Java8中Stream使用的一個(gè)注意事項(xiàng)

    最近在工作中發(fā)現(xiàn)了對(duì)于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程升級(jí)IDEA后Lombok不能使用的解決方法

    升級(jí)IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級(jí),尋思已經(jīng)有好久沒(méi)有升過(guò)級(jí)了。升級(jí)完畢重啟之后,突然發(fā)現(xiàn)好多錯(cuò)誤,本文就來(lái)介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程20個(gè)非常實(shí)用的Java程序代碼片段

    20個(gè)非常實(shí)用的Java程序代碼片段

    這篇文章主要為大家分享了20個(gè)非常實(shí)用的Java程序片段,對(duì)java開(kāi)發(fā)項(xiàng)目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java實(shí)現(xiàn)搶紅包功能

    Java實(shí)現(xiàn)搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)搶紅包功能,采用多線程模擬多人同時(shí)搶紅包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程xml與Java對(duì)象的轉(zhuǎn)換詳解

    xml與Java對(duì)象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對(duì)象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望...

    spcoder14552021-10-18
主站蜘蛛池模板: 国产成人盗拍精品免费视频 | 日本在线视频播放 | 久久热这里面只有精品 | 教室眠催白丝美女校花 | 亚洲成av人片在线观看天堂无码 | 亚洲福利一区二区三区 | 国产一区二区免费不卡在线播放 | 娇妻与老头绿文小说系列 | 亚洲AV无码偷拍在线观看 | 天码毛片一区二区三区入口 | 国产一区二区三区四区波多野结衣 | 顶级尤物极品女神福利视频 | 四虎成人免费 | 欧美精品亚洲精品日韩1818 | 精品综合久久久久久8888 | 日本高清动作片www欧美 | 天天干天天日天天射天天操毛片 | 国产普通话对白露脸流出 | 亚洲天堂精品在线观看 | 99这里只有精品在线 | 456在线观看| 99爱免费视频 | 色色色色网站 | 3d肉浦团在线观看 | h片在线看| 国产激情视频在线 | 日韩一级欧美一级一级国产 | 麻豆夏晴子 | 国产成人精品午夜在线播放 | 色综合天天综合 | 久久亚洲精品中文字幕60分钟 | 免费人成网址在线观看国内 | 亚洲情射 | 女人叉开腿让男人捅 | 日本老妇人乱视频 | 性做久久久久久久久浪潮 | 国产成人精品日本亚洲网址 | 美女主播免费观看 | 亚洲美色综合天天久久综合精品 | 国产在线乱子伦一区二区 | 成人一区二区丝袜美腿 |