SpringBoot @RestControllerAdvice

2022-09-26  本文已影响0人  欧子有话说_

目录

一. 需求场景

如下图所示,后台向前台响应数据的时候,所有的数据都需要放入自定义的封装Entity才返回给前台。现在想要每个Controller中的方法将原数据直接返回,然后通过某种方法 统一封装 处理。

image.png

二. 前期准备

⏹获取状态码的接口

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public interface IStatusCode {

int  getCode();

String  getMsg();

}</pre>

⏹响应状态码的枚举类

<pre class="prettyprint hljs groovy" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ResultCodeEnum implements IStatusCode {

SUCCESS(1000, "请求成功"),
FAILED(1001, "请求失败"),
VALIDATE_ERROR(1002, "参数校验失败"),
RESPONSE_PACK_ERROR(1003, "response返回包装失败");

private int code;
private String msg;

}</pre>

⏹业务状态码的枚举类

<pre class="prettyprint hljs groovy" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum BusinessCodeEnum implements IStatusCode {

APP_ERROR(2000, "业务异常"),
PRICE_ERROR(2001, "价格异常");

private int code;
private String msg;

}</pre>

⏹自定义业务异常类

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException {

private int code;

private String msg;

// 手动设置异常
public BusinessException(IStatusCode codeEnum, String message) { 
    // message用于用户设置抛出错误详情
    super(message);
    // 状态码
    this.code = codeEnum.getCode();
    // 状态码配套的msg
    this.msg = codeEnum.getMsg();
}

// 默认异常使用APP_ERROR状态码
public BusinessException(String message) { 
    super(message);
    this.code = BusinessCodeEnum.APP_ERROR.getCode();
    this.msg = BusinessCodeEnum.APP_ERROR.getMsg();
}

}</pre>

⏹自定义注解,标记该注解的方法不进行响应增强

让我们的方法更加 灵活 ,可以选择增强封装或者不增强。

<pre class="prettyprint hljs css" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {
}</pre>

三. 使用@RestControllerAdvice对响应进行增强

<pre class="prettyprint hljs swift" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Arrays;
import java.util.List;

// 对指定包下面的Controller进行增强
@RestControllerAdvice(basePackages = { "com.example.jmw.controller"})
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {

@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) { 

    List<Boolean> judgeResultList = Arrays.asList(
            // :o:判断相应的类型是否为ResultVo类型
            methodParameter.getParameterType().isAssignableFrom(ResultVo.class),
            // :o:判断响应的方法上是否包含 NotControllerResponseAdvice 注解
            methodParameter.hasMethodAnnotation(NotControllerResponseAdvice.class)
    );

    // 若包含其中一项,则不进行封装
    return !judgeResultList.contains(true);
}

@Override
public Object beforeBodyWrite(Object body
        , MethodParameter returnType
        , MediaType selectedContentType
        , Class<? extends HttpMessageConverter<?>> selectedConverterType
        , ServerHttpRequest request
        , ServerHttpResponse response
) { 

    // String类型不能直接包装
    if (returnType.getGenericParameterType().equals(String.class)) { 
        ObjectMapper objectMapper = new ObjectMapper();
        try { 
            // 将数据包装在ResultVo里后转换为json串进行返回
            return objectMapper.writeValueAsString(ResultVo.build(body));
        } catch (JsonProcessingException e) { 
            // 抛出自定义的业务异常
            throw new BusinessException(ResultCodeEnum.RESPONSE_PACK_ERROR, e.getMessage());
        }
    }

    // 否则直接包装成ResultVo返回
    return ResultVo.build(body);
}

}</pre>

四. 效果

4.1 直接返回List

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Controller
@RequestMapping("/test12")
public class Test12Controller {

@PostMapping("/test")
@ResponseBody
public List<String> test() { 

    return Arrays.asList("1", "2", "3");
}

}</pre>

⏹List被包装之后返回给前台

image.png

4.2 标记NotControllerResponseAdvice注解后返回List

List 未被包装,直接返回数据给前台

image.png

4.3 直接返回字符串

image.png

4.4 直接返回ResultVo类型数据

⏹返回的就是ResultVo类型,无需包装,直接返回数据给前台

image.png
上一篇 下一篇

猜你喜欢

热点阅读