您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“SpringBoot中統(tǒng)一接口返回與全局異常的處理”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
背景
統(tǒng)一接口返回
定義API返回碼枚舉類
定義正常響應(yīng)的API統(tǒng)一返回體
定義異常響應(yīng)的API統(tǒng)一返回體
編寫包裝返回結(jié)果的自定義注解
定義返回結(jié)果攔截器
WebMvc配置類攔截器注冊(cè)者添加返回結(jié)果攔截器
編寫響應(yīng)體處理器
接口調(diào)用
測(cè)試結(jié)果
全局異常處理
編寫自定義異?;?/p>
編寫自定義業(yè)務(wù)異常類
定義全局異常處理類
接口調(diào)用
測(cè)試結(jié)果
在分布式、微服務(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ì)接效率,也可以讓我的代碼看起來更加簡(jiǎn)潔優(yōu)雅。
使用統(tǒng)一返回結(jié)果時(shí),還有一種情況,就是程序的報(bào)錯(cuò)是由于運(yùn)行時(shí)異常導(dǎo)致的結(jié)果,有些異常是我們?cè)跇I(yè)務(wù)中拋出的,有些是無法提前預(yù)知。
因此,我們需要定義一個(gè)統(tǒng)一的全局異常,在Controller捕獲所有異常,并且做適當(dāng)處理,并作為一種結(jié)果返回。
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; } }
@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; } }
@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; } }
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) //作用于方法和類(接口)上 @Documented public @interface ResponseResult { }
@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; } }
@Configuration public class WebMvcConfig implements WebMvcConfigurer { /** * 添加自定義攔截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new ResponseResultInterceptor()).addPathPatterns("/**"); } }
/** * 統(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); } }
@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); } }
@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; } }
public class BizException extends BaseException { public BizException(ResultCode resultCode) { super(resultCode); } }
通過@ExceptionHandler注解來統(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)異常"); } }
@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); }
“SpringBoot中統(tǒng)一接口返回與全局異常的處理”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。