浅析@ResponseBodyAdvice的理解以及实际应用

一、@ResponseBodyAdvice的理解

1、ResponseBodyAdvice接口类  ——  其描述大致解释下:

  允许在执行{@code@ResponseBody}或{@code ResponseEntity}控制器方法之后,但在使用{@code HttpMessageConverter}编写主体之前自定义响应。

  实现可以直接用{@code RequestMappingHandlerAdapter}和{@code exceptionhandleexceptionresolver}注册,或者更可能用{@code@ControllerAdvice}注释,在这种情况下,它们将被两者自动检测。

/**
 * Allows customizing the response after the execution of an {@code @ResponseBody}
 * or a {@code ResponseEntity} controller method but before the body is written
 * with an {@code HttpMessageConverter}.
 *
 * Implementations may be registered directly with
 * {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}
 * or more likely annotated with {@code @ControllerAdvice} in which case they
 * will be auto-detected by both.*/
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
     */
    T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request, ServerHttpResponse response);

}

2、个人理解:

  ResponseBodyAdvice 接口是在 Controller 执行 return 之后,在 response 返回给客户端之前,执行的对 response 的一些处理,可以实现对 response 数据的一些统一封装或者加密等操作。

  该接口一共有两个方法:

(1)supports  —— 判断是否要执行beforeBodyWrite方法,true为执行,false不执行  ——  通过supports方法,我们可以选择哪些类或哪些方法要对response进行处理,其余的则不处理。

(2)beforeBodyWrite  ——  对 response 处理的具体执行方法。

二、实例

  有一个Controller类,返回参数为OutputObject,我们通过ResponseBodyAdvice,对该类的所有方法返回的OutputObject中的部分数据进行统一加密处理。

// 对响应报文统一处理,对bean内容进行加密
@Component
//声明该类要处理的包路径
@ControllerAdvice("com.cmos.edcreg.web.controller")
public class ResponseAdvice implements ResponseBodyAdvice {
    
    private final Logger logger = LoggerFactory.getLogger(ResponseAdvice.class);
    
    // 对response处理的具体方法
    @Override
    public Object beforeBodyWrite(Object arg0, MethodParameter arg1,
            MediaType arg2, Class arg3, ServerHttpRequest arg4,
            ServerHttpResponse arg5) {
        OutputObject out = new OutputObject();
        try {
            //arg0转换为OutputObject类型
            ObjectMapper objectMapper=new ObjectMapper();
            out = objectMapper.readValue(org.json.JSONObject.valueToString(arg0), OutputObject.class);
            //获取加密密钥
            String oldEncryptKey = out.getBean().get("oldEncryptKey");
            //获取加密字符串
            DesSpecial des = new DesSpecial();
            String encryptData = des.strEnc(JSON.toJSONString(out.getBean()), oldEncryptKey, null, null);
            //封装数据(清除原来数据,放入加密数据)
            out.getBean().clear();
            out.getBean().put("data", encryptData);
            return out;
        } catch (Exception e) {
            logger.error("返回报文处理出错", e);
            out.setReturnCode(ReturnInfoEnums.PROCESS_ERROR.getCode());
            out.setReturnMessage(ReturnInfoEnums.PROCESS_ERROR.getMessage());
            return out;
        }
    }

    /* 选择哪些类,或哪些方法需要走beforeBodyWrite
     * 从arg0中可以获取方法名和类名
     * arg0.getMethod().getDeclaringClass().getName()为获取方法名
     */
    @Override
    public boolean supports(MethodParameter arg0, Class arg1) {
        return "com.cmos.edcreg.web.controller.GdH5AppointmentActiveVideoNewController".equals(arg0.getMethod().getDeclaringClass().getName());
    }
}

三、在spring项目开发过程中的应用场景

  对controller层返回值进行修改增强处理。比如返回值5,需要封装成:{"code":"0","data":5,,"msg":"success"} 格式返回前端

1、controller层业务代码

@RestController //此注解包含@ResponseBody注解
@RequestMapping("/mp")
public class ResponseBodyAdviceController {
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public int hello() {
        return 5;
    } 
}

2、实现ResponseBodyAdvice接口的切面类

/**
*此注解针对controller层的类做增强功能,即对加了@RestController注解的类进行处理
*/
@ControllerAdvice(annotations = RestController.class)
public class RestResultWrapper implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }
 
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
 
            //定义一个统一的返回类
            RestResult responseResult = new RestResult( "0", body, "success");
             //如果handler处理类的返回类型是String(即控制层的返回值类型),为了保证一致性,这里需要将ResponseResult转回去
            if(body instanceof String) {
                return JSON.toJSONString(responseResult);
            }
           //封装后的数据返回到前端页面
            return JSONObject.toJSON(responseResult);       
    } 
}

3、返回公共类的创建

// 统一返回Rest风格的数据结构
public class RestResult<T>  implements Serializable {
    private String code = "2000";
    // 成功时返回的数据,失败时返回具体的异常信息
    private T data;
    // 请求失败返回的提示信息,给前端进行页面展示的信息
    private String message ; 

    public RestResult() {
    }
 
    @Override
    public String toString() {
        return "RestResult{" +
                "code='" + code + '\'' +
                ", data=" + data +
                ", message=" + message +
                '}';
    }
 
    public RestResult(String code, T data, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
    }
}
posted @ 2021-07-22 21:46  古兰精  阅读(13565)  评论(0编辑  收藏  举报