如果還有接口test2、test3、test4…..,即每個接口都必須返回Result對象,這也是目前大多數(shù)項目正在使用的方式

優(yōu)化一

1.繼承@ResponseBody

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

2.實現(xiàn)ResponseBodyAdvice

ResponseBodyAdvice 接口允許在執(zhí)行 @ResponseBody 或 ResponseEntity 控制器方法之后,但在使用 HttpMessageConverter 寫入響應(yīng)體之前自定義響應(yīng),進(jìn)行功能增強。通常用于加密,簽名,統(tǒng)一數(shù)據(jù)格式等

相應(yīng)的也有RequestBodyAdvice接口,針對所有以@RequestBody的參數(shù),在讀取請求body之前或者在body轉(zhuǎn)換成對象之前可以做相應(yīng)的增強

@Slf4j
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;

    /**
    * 1、選擇是否執(zhí)行 beforeBodyWrite 方法,返回 true 執(zhí)行,false 不執(zhí)行.
    * 2、通過supports方法,可以選擇對哪些類或方法的 Response 進(jìn)行處理,其余的則不處理。
    * @param returnType:返回類型
    * @param converterType:轉(zhuǎn)換器
    * @return :返回 true 則下面的 beforeBodyWrite  執(zhí)行,否則不執(zhí)行
    * /
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) || returnType.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    /**
    * 對 Response 處理的具體執(zhí)行方法
    * @param body:響應(yīng)對象(response)中的響應(yīng)體
    * @param returnType:控制器方法的返回類型
    * @param selectedContentType:通過內(nèi)容協(xié)商選擇的內(nèi)容類型
    * @param selectedConverterType:選擇寫入響應(yīng)的轉(zhuǎn)換器類型
    * @param request:當(dāng)前請求
    * @param response:當(dāng)前響應(yīng)
    * @return :返回傳入的主體或修改過的(可能是新的)主體
   * /
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return convert(convert(body), selectedConverterType);
    }

    private Result<?> convert(Object body) {
        //兼容返回是Result類型
        if (body instanceof Result) {
            return (Result<?>) body;
        }
        return Result.success(body);
    }

    private Object convert(Result<?> result, Class<? extends HttpMessageConverter<?>> selectedConverterType) {
        //兼容返回是String類型
        if (selectedConverterType == StringHttpMessageConverter.class && result.getData() instanceof String) {
            return "{\"code\":\"" + result.getCode() + "\",\"message\":\"" + result.getMessage() + "\",\"data\":\"" + result.getData() + "\"}";
        }
        return result;
    }
}

3.控制層

@RestController
@ResponseResultBody
public class HelloController {

    @GetMapping("/test")
    public Result hello() {
        HashMap<String, Object> info = new HashMap<>();
        info.put("name","一安未來");
        info.put("addr","北京");
        return Result.success(ResultCode.SUCCESS,info);
    }

    @GetMapping("/test2")
    public Object hello2() {
        HashMap<String, Object> info = new HashMap<>();
        info.put("name","一安未來");
        info.put("addr","北京");
        return info;
    }
}

4.接口測試

至此,即使返回Object也可以統(tǒng)一JSON格式了, 不用每個返回都返回Result對象

優(yōu)化二

使用@ExceptionHandler可以用來統(tǒng)一處理方法拋出的異常返回JSON格式

自定義異常處理類

@Data
public class ResultException extends Exception{

    ResultCode resultCode;

    public ResultException() {
        this(ResultCode.FAIL);
    }

    public ResultException(ResultCode resultCode) {
        super(resultCode.getMessage());
        this.resultCode = resultCode;
    }
}

改造ResponseResultBodyAdvice

ResponseBodyAdvice 接口允許在執(zhí)行 @ResponseBody 或 ResponseEntity 控制器方法之后,但在使用 HttpMessageConverter 寫入響應(yīng)體之前自定義響應(yīng),進(jìn)行功能增強。通常用于加密,簽名,統(tǒng)一數(shù)據(jù)格式等

相應(yīng)的也有RequestBodyAdvice接口,針對所有以@RequestBody的參數(shù),在讀取請求body之前或者在body轉(zhuǎn)換成對象之前可以做相應(yīng)的增強

@Slf4j
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;

    /**
    * 1、選擇是否執(zhí)行 beforeBodyWrite 方法,返回 true 執(zhí)行,false 不執(zhí)行.
    * 2、通過supports方法,可以選擇對哪些類或方法的 Response 進(jìn)行處理,其余的則不處理。
    * @param returnType:返回類型
    * @param converterType:轉(zhuǎn)換器
    * @return :返回 true 則下面的 beforeBodyWrite執(zhí)行,否則不執(zhí)行
    * /
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) || returnType.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    /**
    * 對 Response 處理的具體執(zhí)行方法
    * @param body:響應(yīng)對象(response)中的響應(yīng)體
    * @param returnType:控制器方法的返回類型
    * @param selectedContentType:通過內(nèi)容協(xié)商選擇的內(nèi)容類型
    * @param selectedConverterType:選擇寫入響應(yīng)的轉(zhuǎn)換器類型
    * @param request:當(dāng)前請求
    * @param response:當(dāng)前響應(yīng)
    * @return :返回傳入的主體或修改過的(可能是新的)主體
   * /
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return convert(convert(body), selectedConverterType);
    }

    private Result<?> convert(Object body) {
        if (body instanceof Result) {
            return (Result<?>) body;
        }
        return Result.success(body);
    }

    private Object convert(Result<?> result, Class<? extends HttpMessageConverter<?>> selectedConverterType) {
        if (selectedConverterType == StringHttpMessageConverter.class && result.getData() instanceof String) {
            return "{\"code\":\"" + result.getCode() + "\",\"message\":\"" + result.getMessage() + "\",\"data\":\"" + result.getData() + "\"}";
        }
        return result;
    }


    /**
     * 提供對標(biāo)準(zhǔn)Spring MVC異常的處理
     */
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Result<?>> exceptionHandler(Exception ex, WebRequest request) {
        log.error("ExceptionHandler: {}", ex.getMessage());
        HttpHeaders headers = new HttpHeaders();
        if (ex instanceof ResultException) {
            return this.handleResultException((ResultException) ex, headers, request);
        }
        // TODO:  這里可以自定義其他的異常攔截
        return this.handleException(ex, headers, request);
    }

    /**
     * 對ResultException類返回返回結(jié)果的處理
     */
    protected ResponseEntity<Result<?>> handleResultException(ResultException ex, HttpHeaders headers, WebRequest request) {
        Result<?> body = Result.failure(ex.getResultCode());
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /**
     * 異常類的統(tǒng)一處理
     */
    protected ResponseEntity<Result<?>> handleException(Exception ex, HttpHeaders headers, WebRequest request) {
        Result<?> body = Result.failure();
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    protected ResponseEntity<Result<?>> handleExceptionInternal(
            Exception ex, Result<?> body, HttpHeaders headers, HttpStatus status, WebRequest request) {

        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
        }
        return new ResponseEntity<>(body, headers, status);
    }
}

文章轉(zhuǎn)自微信公眾號@一安未來

上一篇:

Spring Boot 2.X 實戰(zhàn)--RESTful API 全局異常處理

下一篇:

API協(xié)議設(shè)計的10種技術(shù)
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費