不用aop,获取 request 对象中所有形式的参数,包含 get参数,post body参数,restful格式的所有参数。put all parameters into 1 single map

12 篇文章 0 订阅
2 篇文章 0 订阅

需求场景

request请求的参数有大概 3种,怎样一次性 都获取出来呢 ?

  1. get 请求 ?param1=hello&param2=world ,直接用request.getParameter()获取单个参数,

    或者 request.getParameterMap() 获取 多个参数为map格式

  2. restful 请求 ,借助 spring实现 (Map)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE)

  3. post 请求的参数,读取 stream 流 ,request.getInputStream() 获取 。 该方法只允许调用1次,因为 stream的流指针没有复位,导致后续执行会报错。因此,我们需要 提前 封装一下它的内容 到 buffer 中,曲线救国实现 多次读取 。

为什么不用aop?
实现方案 : filter + interceptor + request 参数,最干净的原始编程
  • filter 封装request对象,使其 内部的 stream 流可以 多次 读
  • interceptor 拦截每次 指定的 请求, 获取 request 对象中的 所有parameters ,并且最终 放入 1 个map 中 返回
废话少说,搞起:
  1. WrapRequestFilter 过滤器,封装request 对象,替换 原有的request,支持 多次 执行 request.getInputStream()

    /**
     * enable the ability to read request-params multiple times
     * by copying the request stream into a byte[] buffer.
     * getInputStream() & getReader() are both override to read this buffer
     *
     * @author stormfeng
     * @date 2021-08-12  10:30
     * <p>
     * reference :https://blog.csdn.net/dark_horse_lk/article/details/82344692
     */
    @WebFilter(urlPatterns = "/api/*")
    @Order(2)
    public class WrapRequestFilter extends OncePerRequestFilter {
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
         ServletRequest requestWrapper = new BufferedServletRequestWrapper(request);
         chain.doFilter(requestWrapper, response);
     }
    
     private static class BufferedServletRequestWrapper extends HttpServletRequestWrapper {
         private byte[] buffer;
    
         public BufferedServletRequestWrapper(HttpServletRequest request) throws IOException {
             super(request);
             InputStream is = request.getInputStream();
             this.buffer = StreamUtils.copyToByteArray(is);
         }
    
         // 外部 读取字节流 的方法
         @Override
         public ServletInputStream getInputStream() throws IOException {
             InputStream bodyStream = new ByteArrayInputStream(this.buffer);
             // https://www.cnblogs.com/keeya/p/13634015.html
             return new ServletInputStream() {
                 @Override
                 public int read() throws IOException {
                     return bodyStream.read();
                 }
    
                 @Override
                 public boolean isFinished() {
                     return false;
                 }
    
                 @Override
                 public boolean isReady() {
                     return false;
                 }
    
                 @Override
                 public void setReadListener(ReadListener listener) {
                 }
             };
         }
    
         // 外部 读取字符流 的方法
         @Override
         public BufferedReader getReader() throws IOException {
             return new BufferedReader(new InputStreamReader(getInputStream()));
         }
     }
    }
    

    注意: 在 springboot 中, @WebFilter 这个注解要想生效,需要在 boot的启动 main类上 加上 @ServletComponentScan ,开启扫描servlet容器组件的功能。(本身filter不是spring的全家桶,所以需要额外配置下)

  2. InterceptorUtils 自定义的工具类,获取 所有参数

    /**
     * @author stormfeng
     * @date 2021-08-12  19:19
     */
    @Slf4j
    public class InterceptorUtils {
    
        /**
         * 获取所有参数,集成到 1个map 中展示给前端
         */
        public static Map getParamMap(HttpServletRequest request) throws IOException {
            Map<String, Object> resultMap = new HashMap<>();
            // get 请求
            Map<String, String[]> parameterMap = request.getParameterMap();
            resultMap.putAll(parameterMap);
    
            // restful请求
            Object attribute = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            if (attribute instanceof Map) {
                resultMap.putAll((Map) attribute);
            }
    
            // post body 参数
            Object postBody = getPostBody(request);
            if (null != postBody) {
                resultMap.put("posytBody", postBody);
            }
    
            // 因为请求可能是多个key,value为数组,导致直接 toString 会 很丑,
            // 所以需要 jackson 提前序列化一下values ,不至于太丑看不懂 [java... 到底是什么 
            for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
                entry.setValue(JacksonUtils.writeValueAsString(entry.getValue()));
            }
            return resultMap;
        }
         public static Object getPostParam(HttpServletRequest request, String requiredParam) throws IOException {
             Object postBody = getPostBody(request);
     
             if (null == postBody) {
                 return null;
             }
             if (postBody instanceof Map) {
                 return ((Map) postBody).get(requiredParam);
             } else if (postBody instanceof List) {
                 for (Map<String, Object> map : (List<Map<String, Object>>) postBody) {
                     if (map.get(requiredParam) != null) {
                         return map.get(requiredParam);
                     }
                 }
             }
     
             return null;
         }
    
        /**
    	 * @return 有可能 返回 Map 格式,也可能返回 List<Map> 格式 .所以用 Object 接参
         */
        public static Object getPostBody(HttpServletRequest request) throws IOException {
            int contentLength = request.getContentLength();
            if (contentLength <= 0) {
                return null;
            }
    
            String charEncoding = request.getCharacterEncoding();
            if (charEncoding == null) {
                charEncoding = "UTF-8";
            }
    
            String body = new String(StreamUtils.copyToByteArray(request.getInputStream()), charEncoding);
            return JacksonUtils.readValue(body, Object.class);
        }
    }
    

    注意: 上面代码,最后一步中的 JacksonUtils 是一个极其简单的封装, 其实就是 单例的

    new ObjectMapper().writeValueAsString(..)new ObjectMapper().readValue(..) 而已 ,

    ​ 不明白的小伙伴可以在下面留言。

  3. 赠送 一个 重载的方法,判断 request中 是否包含 某个 参数 requiredParam

    /**
     * try to get a requiredParam by 3 ways below:
     * 1)query string,like ?name=hello&id=1
     * 2)uri pattern params,like /{name}/1 --> /myname/1
     * 3)if post ,check post body
     * @return null,则代表不包含 参数的 requiredParam ,或者该参数的值 value 不存在 
     */
    public static Object getParam(HttpServletRequest request, String requiredParam) throws IOException {
        if (null != request.getParameter(requiredParam)) {
            return request.getParameter(requiredParam);
        }
    
        Object uriParamMap = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        if (uriParamMap instanceof Map) {
            Object o = ((Map) uriParamMap).get(requiredParam);
            if (null != o) {
                return o;
            }
        }
    
        if (HttpMethod.POST.matches(request.getMethod())) {
            return getPostParam(request,requiredParam);
        }
    
        return null;
    }
    
  4. 总结:步骤 2和3 都是工具类 InterceptorUtils 中 的2个方法,可以在你的项目中 随意使用。比如,在 拦截器 interceptor中,或者 aop 中使用。

    aop 中,再也不不需要笨重的 pjp.getArgs() 等一系列的复杂操作 来判断参数了 。

    从此,人们过上了幸福舒心的生活。

总结

我的使用场景是,在拦截器中 ,通过判断 某个特定参数的值,来进行权限控制。

并且,不使用 笨重的 aop ,直接 用 interceptor 完全能胜任。

拦截器中,还可以引入 spring 其他Bean 组件,比如 redissonClient + @Cacheable,实现缓存 某些权限的判断结果,非常好用。过滤器链和 拦截器链,各司其职,整体的代码结构,看起来比 aop 也清晰很多。

推荐1个大神的博客,源码解析 interceptor和aop

全局终,如有疑问,欢迎留言和私信。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值