问题一:设置完filter之后 发现莫名其妙的bug:过滤器在controller之前执行没问题,之后执行就不行。

@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //先进接口
        filterChain.doFilter(request, response);
        RequestWrapper requestWrapper = new RequestWrapper(request);
        String bodyString = requestWrapper.getBodyString();
        System.out.println(bodyString);
        JSONObject jsonObject = JSONUtil.parseObj(bodyString);
        String account = jsonObject.getStr("account");
        userService.lambdaUpdate().eq(User::getAccount, account).setSql("count = count + 1").update();

        
    }

报错如下:

2025-02-21T16:59:22.532+08:00 ERROR 25856 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

cn.hutool.json.JSONException: A JSONObject text must begin with '{' at 1 [character 2 line 1]
	at cn.hutool.json.JSONTokener.syntaxError(JSONTokener.java:413) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.JSONParser.parseTo(JSONParser.java:48) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.ObjectMapper.mapFromTokener(ObjectMapper.java:247) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.ObjectMapper.mapFromStr(ObjectMapper.java:223) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.ObjectMapper.map(ObjectMapper.java:98) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.JSONObject.<init>(JSONObject.java:210) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.JSONObject.<init>(JSONObject.java:187) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.JSONObject.<init>(JSONObject.java:142) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.JSONObject.<init>(JSONObject.java:125) ~[hutool-all-5.8.16.jar:5.8.16]
	at cn.hutool.json.JSONUtil.parseObj(JSONUtil.java:88) ~[hutool-all-5.8.16.jar:5.8.16]
	at com.base.app.core.filter.LoginFilter.doFilter(LoginFilter.java:46) ~[classes/:na]
    ........

可以发现,并无执行打印语句,然后我们把它移到后面,先执行filter:

{
  "account": "admin",
  "password": "123456"
}
2025-02-21T17:17:00.984+08:00 ERROR 29064 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

可以发现,执行打印语句,说明request拿到json数据了,但是controller里没数据了,也就是说 给完之后,就不能给下一层了(具体原因未知,我是彩笔)

public class RequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        //保存一份InputStream,将其转换为字节数组
        body = StreamUtils.copyToByteArray(request.getInputStream());
    }

    //转换成String
    public String getBodyString(){
        return new String(body, StandardCharsets.UTF_8);
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    //把保存好的InputStream,传下去
    @Override
    public ServletInputStream getInputStream() {

        final ByteArrayInputStream basis = new ByteArrayInputStream(body);

        return new ServletInputStream() {

            @Override
            public int read() {
                return basis.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}

通过保存一份流,就可实现在过滤器中能拿到JSON参数,同时Controller也不会丢失参数

@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        RequestWrapper requestWrapper = new RequestWrapper(request);
        //先进接口
        filterChain.doFilter(requestWrapper, response);
        String bodyString = requestWrapper.getBodyString();
        System.out.println(bodyString);
        JSONObject jsonObject = JSONUtil.parseObj(bodyString);
        String account = jsonObject.getStr("account");
        User eq = userService.lambdaQuery().eq(User::getAccount, account).one();
        eq.setCount(eq.getCount()+1);
        userService.updateById(eq);


    }

ok 解决

问题二:无法注入问题问题

这个问题我问了ChatGPT:

  • 过滤器的初始化顺序: Filter 是由 Servlet 容器在请求处理之前初始化的,通常它是在 Spring 的容器启动之前创建和调用的。因此,Spring 的依赖注入(例如通过 @Resource 注解)可能会失败,因为过滤器的生命周期并不完全与 Spring 的上下文同步。

  • Filter 的生命周期管理: Spring 管理的普通 bean(如服务层、Controller)是通过 IoC 容器注入的,但是 Filter 是由容器管理的特殊对象,并且通常是由 FilterRegistrationBean 注册的,这使得它不一定按照常规的方式进行依赖注入。

他给的解决方案:


  • 使用 FilterRegistrationBean 注册过滤器时,注入依赖: 如果你想确保 Filter 中的依赖能够正确注入,你可以通过 FilterRegistrationBean 的构造函数传递依赖(像你前面提到的 userRepository)。这种方式可以确保依赖注入在注册过滤器时就能生效。

  • 手动注入依赖: 如果非要在 Filter 中使用 @Resource,你可以手动调用 Spring 的上下文来获取需要的服务实例。例如,通过 SpringContextUtil 类来获取 ApplicationContext,然后从中获取需要的服务。

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<LoginFilter> loggingFilter(UserService userService) {
        FilterRegistrationBean<LoginFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new LoginFilter(userService)); //在这里 把这个service和filter的bean一起注入进去
        registrationBean.setOrder(1);
        registrationBean.addUrlPatterns("/api/*");  // 配置拦截路径
        return registrationBean;
    }
}

private final UserService userService;

    public LoginFilter(UserService userService) {
        this.userService = userService;
    }

然后用的时候用构造器注入即可~