认识JavaWeb过滤器
认识JavaWeb过滤器
记录在一次java过滤器作业中遇到的一些问题
过滤器原理
当我们使用过滤器时,过滤器会对游览器的请求进行过滤,过滤器可以动态的分为3个部分1.放行之前的代码,2.放行,3.放行后的代码,这3个部分分别会发挥不同作用。
对浏览器请求进行第一次过滤,然后继续执行
将浏览器请求放行,如果还有过滤器,那么就继续交给下一个过滤器
对返回的Web资源再次进行过滤处理
不止请求会经过过滤器,响应也会经过过滤器
执行过程
使用过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package com.servlet.filter;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
public class test implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
// filterConfig读取配置参数 filterConfig.getInitParameterNames获取键值对
System.out.println("创建");
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行");
//下面这行代码就是放行
filterChain.doFilter(servletRequest,servletResponse)
}
public void destroy() {
System.out.println("销毁");
}
}
现实案例
确保所有通过该过滤器的 HTTP 请求和响应都使用 UTF-8 编码,过滤器拦截所有进入应用的请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class EncodeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filter) throws IOException, ServletException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
filter.doFilter(req, resp);
}
@Override
public void destroy() {}
}
过滤器放行静态资源
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 import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AuthFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI();
// 排除静态资源请求 req.getContextPath()得到上下文路径 如:http://localhost:8080/
if (uri.startsWith(req.getContextPath() + "/static/")) {
chain.doFilter(request, response); // 直接放行
return;
}
// 检查用户是否已登录
Object user = req.getSession().getAttribute("user");
if (user == null) {
// 用户未登录,重定向到登录页面
res.sendRedirect(req.getContextPath() + "/login.jsp");
} else {
// 用户已登录,继续处理请求
chain.doFilter(request, response);
}
}
public void init(FilterConfig filterConfig) throws ServletException {}
public void destroy() {}
}注解中就是过滤器过滤的资源
多个过滤器之间可以形成过滤器链
出现的问题
- maven导入坐标时没加
provided
1
2
3
4
5
6 <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>为什么?
当我们运行程序到Tomcat容器中时,容器本身已经提供了servlet-api的实现,如果不指定
<scope>provided</scope>
,Maven会将javax.servlet-api
包含在最终的WAR包中,会导致冲突问题,加这个相当于表示在编译和测试中是可用的,但在运行时不要包含在最终构建的产物中
在运行过程中浏览器出现
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
报错,因为之前调用过sendError()这个函数,我以为是方法调用出现的问题,在过滤器中将该代码注释后,再次运行发现还是报相同的错误,后面以为是tomcat有缓存的问题😡,因为之前也有过类似的问题,后面我又清理缓存又换tomcat的版本,后面发现他其实是多次response导致,Servlet容器会在响应结束时自动关闭输出流,这个时候再次发送response就会出现报错,进到我的代码中去看,我重写了HttpServletf分发资源的方式,按照方法名分发资源访问路径
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 package com.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 修改分发的方式
*/
public class BaseServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI();
int index = uri.lastIndexOf("/");
String methodName = uri.substring(index + 1);
Class<? extends BaseServlet> cls = this.getClass();
try {
Method method = cls.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
try {
method.invoke(this, req, resp);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
// super.service(req, resp);
}
}就是在注释的地方,调用这个方法的时候会在调用父类中的service方法,这就产生了多次响应的问题