在SpringBoot中,FilterRegistrationBean类用来在Servlet容器执行请求过程中过滤一些特定的请求,并对请求的请求内容和响应结果做一些处理,例如权限拦截验证、访问日志、响应格式化等等。 你可以认为是在服务端接收到请求返回请求结果到调用方这两个过程中间做一些自定义的操作。 下面的案例记录如何在原有请求中增加额外的请求参数。

1 添加请求包装类

先创建一个工具类RequestParameterWrapper继承自HttpServletRequestWrapper,并重写getParametergetParameterMapgetParameterValues方法,用于包装http请求并对请求中的参数进行处理。完整代码如下:

需要重写哪些视情况而定,本例是对参数进行处理,后续请求过滤链处理过程中对调用参数相关的方法需要使用自定义方法,所以需要重写


/**
 * 自定义RequestParameterWrapper包装类,对request参数进行自定义处理
 */
public class RequestParameterWrapper extends HttpServletRequestWrapper {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private Map<String, String[]> params = new HashMap<>();

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     * @throws IllegalArgumentException if the request is null
     */
    public RequestParameterWrapper(HttpServletRequest request) {
        super(request);
        //将现有parameter传递给params
        this.params.putAll(request.getParameterMap());
    }

    /**
     * 重写getParameter,代表参数从当前类中的map获取
     *
     * @param name
     * @return
     */
    @Override
    public String getParameter(String name) {
        this.logger.info(">>>>>>>>>> 调用 RequestParameterWrapper 的方法获取 {} 参数 <<<<<<<<<<", name);
        String[] values = params.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return this.params;
    }

    @Override
    public String[] getParameterValues(String name) {
        return params.get(name);
    }

    /**
     * 添加一些参数到原有参数中
     *
     * @param extraParams
     */
    public void addParameters(Map<String, Object> extraParams) {
        for (Map.Entry<String, Object> entry : extraParams.entrySet()) {
            addParameter(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 添加一个参数到原有参数中
     *
     * @param name
     * @param value
     */
    public void addParameter(String name, Object value) {
        if (value != null) {
            if (value instanceof String[]) {
                params.put(name, (String[]) value);
            } else if (value instanceof String) {
                params.put(name, new String[]{(String) value});
            } else {
                params.put(name, new String[]{String.valueOf(value)});
            }
        }
    }
}

2 实现自定义请求过滤类

添加配置类DemoReqConfig,用于向Servlet容器注册自定义请求过滤类的Bean。完整代码如下:

当然,也可以根据业务划分将Bean的注册过程放到其他组件或配置类中,只要能实现向Servlet容器注册请求过滤Bean即可

@Configuration
public class DemoReqConfig {

    @Bean
    public FilterRegistrationBean getKaFilterRegistrationBean() {
        Filter filter = new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws IOException, ServletException {
                RequestParameterWrapper wrapper = new RequestParameterWrapper((HttpServletRequest) request);
                wrapper.addParameters(new HashMap<String, Object>() {{
                    put("reqType", "demoRequest");
                }});
                //进入当前请求过滤器,并在执行其他请求过滤之前
                chain.doFilter(wrapper, response);
                //执行完其他请求过滤之后
            }

            @Override
            public void destroy() {

            }
        };
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter);
        registrationBean.setOrder(10);
        registrationBean.addUrlPatterns("/demo/*");
        return registrationBean;
    }
}

在配置类DemoReqConfig中,注册了一个FilterRegistrationBean,该Bean的作用是对/demo/路径下的所有请求进行过滤,并向请求的原有参数中添加一个新的参数reqType(值为demoRequest)。

需要注意的点:

  1. FilterdoFilter方法中,我们不能直接向原request添加参数,所以这里通过第一步中RequestParameterWrapper类对原有的请求进行包装。
  2. FilterdoFilter方法中处理完自定义的操作之后,要继续执行其他的请求过滤链chain.doFilter,注意这里的参数要使用包装过的RequestParameterWrapper类对象,不能再使用原有的request,否则起不到预期效果。
  3. Servlet容器中可能有多个过滤操作,如果当前FilterRegistrationBean中的自定义操作依赖其他的Filter或者被其他的Filter依赖,要注意FilterRegistrationBean的顺序。
  • FilterRegistrationBean的执行顺序可通过setOrder方法实现,数值越小越先执行

  • 例如有另外一个AuthFilter向请求中设置了当前登录用户信息,当前Filter中要使用用户信息,那么当前Filter要在AuthFilter执行之后执行,即Order比AuthFilter的Order要大

3 请求模拟

3.1 改造原有请求

对之前<<Spring Boot:Idea从零开始初始化后台项目>> DemoController类中的getRedisValue请求进行修改(请求路径为“/demo/getRedisValue”,满足自定义FilterRegistrationBean的过滤规则),日志打印增加上面步骤FilterRegistrationBean中增加的参数reqType。方法调整后如下:

    @PostMapping("/demo/getRedisValue")
    public HResponse getRedisValue(String key, HttpServletRequest request) {
        String reqType = request.getParameter("reqType");
        this.logger.info("请求获取redis key为:{} 的值,reqType:{}", key, reqType);
        return HResponse.success(new JSONObject().fluentPut("key", key).fluentPut("value", this.redisTemplate.opsForValue().get(key)));
    }

3.2 执行请求

使用Postman执行请求,请求内容及结果如下图。

在这里插入图片描述
在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐