• Springframework之ResponseBodyAdvice——响应拦截处理


           补充这篇博文是因为 在SpringBoot之HandlerInterceptor拦截器的使用 ——(一) 中提到 postHandle 方法有机会修改ModelAndView ,但是实际情况却是 无法修改 。原因是 @ResponseBody 注释或者返回 ResponseEntity的方法在先于postHandle方法之前将响应提交给HandlerAdapter(调用handler和Interceptor方法者),所以之后的修改就无效了。那么在这里我将采用实现 ResponseBodyAdvice 接口的方式来 修改ResponseBody

    ResponseBodyAdvice简介

           ResponseBodyAdvice接口,其实是对加了 @RestController (也就是 @Controller + @ResponseBody )注解的处理器将要返回的值进行 增强处理
           可以用于规范化前后端调用的响应格式,也可以用作统一签名等操作。
           ResponseBodyAdvice其实也就是采用了 AOP 的思想,对返回值进行一次修改。

    ResponseBodyAdvice方法介绍

    public interface ResponseBodyAdvice<T> {
    	// 该方法可以用于制定准入规则,只有当该方法返回True时,才会进入beforeBodyWrite方法
        boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);
    
    	// var1 也就是我们的返回值,主要就是针对其进行修改
        @Nullable
        T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    自定义我们的ResponseBodyAdvice

    • 假定我这里去实现一个统一签名,然后返回给接口调用方的处理器
    package com.zhibo.core;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.serializer.SerializerFeature;
    import com.zhibo.common.response.Response;
    import com.zhibo.security.util.MD5Util;
    import com.zhibo.core.annotation.SignAdvice;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    
    import java.lang.reflect.Method;
    import java.util.Objects;
    
    @Slf4j
    @ControllerAdvice("com.zhibo.controller.v3")// 此处可以限制需要拦截的包路径,不填写为全部拦截进入supports 方法验证
    public class MyResponseBodyAdvice implements ResponseBodyAdvice<Response> {
    	//签名KEY
        private final static String SIGN_KEY = "zhibo_lv";
    
        @Override
        public boolean supports(MethodParameter methodParameter, Class aClass) {
            //根据自定义注解判断方法上是否有指定需要拦截,当然这里也可以什么都不写直接返回true,根据自身业务场景做限制校验即可
            Method method = methodParameter.getMethod();
            SignAdvice ra = method.getAnnotation(SignAdvice.class);
            return Objects.nonNull(ra);
        }
    
        @Override
        public Response beforeBodyWrite(Response rsp, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
            if (null == rsp) return null;
            // 打印未加签名前的Response
            log.info("未加签名前的Response:  {}",JSON.toJSONString(rsp));
            SignResponse signRsp = new SignResponse(rsp);
    
            signRsp.setDateTime(System.nanoTime());
            //自己随便弄个加密算法
            StringBuilder sbd = new StringBuilder();
            SignAdvice ra = methodParameter.getMethod().getAnnotation(SignAdvice.class);
            sbd.append(signRsp.getCode())
                    .append(signRsp.getDateTime())
                    .append(signRsp.getMessage())
                    .append(JSON.toJSONString(signRsp.getResult(), SerializerFeature.MapSortField))
                    .append(StringUtils.isBlank(ra.signKey()) ? SIGN_KEY : ra.signKey());
            signRsp.setSign(MD5Util.md5(sbd.toString()));
            log.info("添加签名后的Response:  {}",JSON.toJSONString(signRsp));
            return signRsp;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    自定义注解类

    package com.zhibo.core.annotation;
    
    import javax.ws.rs.NameBinding;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author zhibo.lv
     * Response自动签名
     */
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(value = RetentionPolicy.RUNTIME)
    @NameBinding
    public @interface SignAdvice
    {
    	/**
    	 * 自定义签名KEY
    	 * 如不指定则使用系统默认KEY
    	 */
    	String signKey() default "";
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    Controller测试代码

    package com.zhibo.controller.v3;
    
    import com.zhibo.common.response.Response;
    import com.zhibo.core.annotation.SignAdvice;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @ResponseBody
    @Api(tags = "测试SignResponse", description = "测试SignResponse")
    public class SignResponseController {
    
        @SignAdvice(signKey="signKey:zhibo testDemo")
        @GetMapping(value="/v3/noauth/sign/test")
        @ApiOperation(value = "/v3/noauth/sign/test", notes = "测试SignResponse")
        public Response<String> testDemo() {
            Response<String> response = new Response<>();
            response.setCode(0);
            response.setMessage("Success");
            response.setResult("点赞三连你最帅");
            return response;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    调用接口执行结果在这里插入图片描述

    在这里插入图片描述
    你的点赞就是我创作的最大动力,如果写的不错,来个三连行不行

  • 相关阅读:
    带你快速概览MySQL 整体架构
    普利姆算法(Java)
    web前端期末大作业《中华传统文化题材网页之丝绸之路》 html+css+javascript网页设计实例
    微服务面试题
    【数据结构】选择题快速求解AOE网的关键路径
    SqlServe存储过程中运用事务
    腾讯云幻兽帕鲁服务器中,如何检查并确保所有必要的配置文件(如PalWorldSettings.ini和WorldOption.sav)正确配置?
    springboot学习一:idea社区版本创建springboot项目的三种方式(第三种日后更新,以第二种方法主)
    学生HTML个人网页作业作品----(画家企业8页)
    springboot+jacob实现文字转语音
  • 原文地址:https://blog.csdn.net/zhibo_lv/article/details/126251591