( 十八 )、 SpringMVC 之 ResponseBodyAdvice

( 十八 )、 SpringMVC  之 ResponseBodyAdvice

 

 

1、简介

ResponseBodyAdvice接口属于springMVC 框架基础的底层切面接口;实现这个接口的类,可以修改Controller的返回值,即进行功能增强。

源码如下:

public interface ResponseBodyAdvice<T> {

    /**
     * Whether this component supports the given controller method return type
     * and the selected {@code HttpMessageConverter} type.
     * @param returnType the return type
     * @param converterType the selected converter type
     * @return {@code true} if {@link #beforeBodyWrite} should be invoked;
     * {@code false} otherwise
     */
    boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);

    /**
     * Invoked after an {@code HttpMessageConverter} is selected and just before
     * its write method is invoked.
     * @param body the body to be written
     * @param returnType the return type of the controller method
     * @param selectedContentType the content type selected through content negotiation
     * @param selectedConverterType the converter type selected to write to the response
     * @param request the current request
     * @param response the current response
     * @return the body that was passed in or a modified (possibly new) instance
     */
    @Nullable
    T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request, ServerHttpResponse response);

}
  • supports: 是否支持处理该方法的返回值,返回true 才会执行 beforeBodyWrite。
  • beforeBodyWrite: 在选择HttpMessageConverter之后和调用它的 write 方法之前调用。

 

2、使用示例:

应用场景在Spring项目开发过程中,对controller层返回值进行修改增强处理。比如返回值: "hello-world",需要封装成 {"code":"0",   "data":"hello-world",   "msg":"success"}  格式返回给前端。

2.1、定义统一响应格式:

@Data
public class Result<T> {
    /**
     * 业务错误码
     */
    private Integer code;
    /**
     * 信息描述
     */
    private String message;
    /**
     * 返回参数
     */
    private T data;

    private Result(IResultStatus resultStatus, T data) {
        this.code = resultStatus.code();
        this.message = resultStatus.message();
        this.data = data;
    }

    private Result(Integer code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }


    /**
     * 业务成功返回业务代码和描述信息
     */
    public static Result<Void> success() {
        return new Result<Void>(ResultStatus.SUCCESS, null);
    }

    /**
     * 业务成功返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> success(T data) {
        return new Result<T>(ResultStatus.SUCCESS, data);
    }

    /**
     * 业务成功返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> success(IResultStatus resultStatus, T data) {
        if (resultStatus == null) {
            return success(data);
        }
        return new Result<T>(resultStatus, data);
    }

    /**
     * 业务成功返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> success(Integer code, String message, T data) {
        return new Result<T>(code, message, data);
    }


    /**
     * 业务异常返回业务代码和描述信息
     */
    public static <T> Result<T> fail() {
        return new Result<T>(ResultStatus.BUSINESS_ERR, null);
    }

    /**
     * 业务异常返回业务代码和描述信息
     */
    public static <T> Result<T> fail(String message) {
        return new Result<T>(ResultStatus.BUSINESS_ERR.code(), message, null);
    }

    /**
     * 业务异常返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> fail(IResultStatus resultStatus) {
        return fail(resultStatus, null);
    }

    /**
     * 业务异常返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> fail(IResultStatus resultStatus, String message) {
        if (resultStatus == null) {
            return new Result<T>(ResultStatus.BUSINESS_ERR, null);
        }
        return new Result<T>(resultStatus.code(), message, null);
    }
}

 

2.2、定义状态码接口(方便扩展)、枚举类

public interface IResultStatus {

    /**
     * 业务状态码
     * @return Integer
     */
     Integer code();

    /**
     * 业务信息描述
     * @return String
     */
     String message();
}

 

2.3、状态码枚举

public enum ResultStatus implements IResultStatus {

    /**
     * 成功
     */
    SUCCESS(200, "OK"),
    /**
     * 错误的请求,参数异常
     */
    BAD_REQUEST(400, "Bad Request params exception"),
    /**
     * 服务器异常
     */
    INTERNAL_SERVER_ERROR(500, "Internal Server Error"),

    /**
     * 401 未认证异常
     */
    UNAUTHORIZED(401, "认证异常"),


    /**
     * 业务异常
     */
    BUSINESS_ERR(10000, "业务异常");


    /**
     * 业务状态码
     */
    private Integer code;

    /**
     * 业务信息描述
     */
    private String message;

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

    /**
     * 业务状态码
     *
     * @return Integer
     */
    @Override
    public Integer code() {
        return code;
    }

    /**
     * 业务信息描述
     *
     * @return String
     */
    @Override
    public String message() {
        return message;
    }
}

 

2.3、ResponseBodyAdvice 实现类

@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Logger log = LoggerFactory.getLogger(ResponseResultBodyAdvice.class);/**
     * 判断Controller方法返回值是否支持
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 如果接口返回的类型本身就是Result那就没有必要进行额外的操作,返回false
        if (returnType.getGenericParameterType().equals(Result.class)) {
            return false;
        }
// 对类或者方法上面注解了@RestController 或者 @ResponseBody 的方法统一处理
return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), RestController.class) || returnType.hasMethodAnnotation(ResponseBody.class); } /** * 当类或者方法使用了 @ResponseResultBody 就会调用这个方法 */ @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 防止重复包裹的问题出现 if (body instanceof Result) { return body; } // String类型不能直接包装,所以要进行些特别的处理 if (returnType.getGenericParameterType().equals(String.class)) { ObjectMapper objectMapper = new ObjectMapper(); try { // 将数据包装在Result里后,再转换为json字符串响应给前端 return objectMapper.writeValueAsString(Result.success(body)); } catch (JsonProcessingException e) { throw new BusinessException("返回String类型错误"); } } Result<Object> success = Result.success(body); return success; } }

 

posted @ 2022-01-09 22:18  邓维-java  阅读(973)  评论(0)    收藏  举报