The SpringBoot @RestControllerAdvice annotation uniformly encapsulates the return value

1. Demand scenarios

As shown in the figure below, when the background responds to data from the foreground, all data needs to be put into a custom encapsulation Entity before being returned to the foreground. Now I want the method in each Controller to directly return the original data, and then use a certain method to  encapsulate and process it uniformly  .

2. Preliminary preparation

⏹ Interface to get the status code

public interface IStatusCode {
 

    int getCode();

    String getMsg();
}

⏹The enumeration class of the response status code

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;
}

⏹Enumeration class of business status code

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;
}

⏹Custom business exception class

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();
    }
}

⏹Custom annotation, the method of marking the annotation does not enhance the response

Let our method be more  flexible  , you can choose to enhance the encapsulation or not.

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 {
 
}

3. Use @RestControllerAdvice to enhance the response

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);
    }
}

4. Effect

4.1 Return List directly

@Controller
@RequestMapping("/test12")
public class Test12Controller {
 

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

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

⏹List is wrapped and returned to the foreground

4.2 Return List after marking the NotControllerResponseAdvice annotation

⏹  List not wrapped, directly return data to the front desk

4.3 return string directly

4.4 Directly return ResultVo type data

⏹What is returned is the ResultVo type, without packaging, directly returns the data to the front desk

Guess you like

Origin blog.csdn.net/Maxiao1204/article/details/129494589