Servlet过滤器和处理程序拦截器的Spring Boot实现

Filters and interceptors are an important part of rest applications, I have already briefed about both of them and gave a comparison between the 2 in my previous article, please check it out.

过滤器和拦截器是其余应用程序的重要组成部分,我已经简要介绍了这两个应用程序,并在上一篇文章中对这两种方法进行了比较,请检查一下。

如何实现过滤器? (How to implement Filters?)

@Component
@Order(1)
public class TransactionFilter implements Filter {


	@Autowired
	private HttpServletReqUtil reqUtil;


	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		final MyHttpServletRequestWrapper wrappedRequest = new MyHttpServletRequestWrapper(
				(HttpServletRequest) request);
		System.out.println("Inside Servlet Filter");
		System.out.println("User IP address: " + reqUtil.getRemoteAddress(wrappedRequest));
		System.out.println("Request Params: " + reqUtil.getRequestParams(wrappedRequest));
		System.out.println("Request Payload: " + reqUtil.getPayLoad(wrappedRequest));
		System.out.println("Exiting Servlet Filter");
		chain.doFilter(wrappedRequest, response);
	}
}

The Servlet Filter implementation is really simple, we implement the javax.servlet.Filter, the @Order specifies the order in which the filters have to be executed. We have specified the order as 1 so this will be the first filter to be executed. The chain.doFilter called in line 18 passes the request and response object to the next filter in chain or to the handler if that’s the last filter.

该Servlet过滤器的实现是非常简单的,我们实行javax.servlet.Filter@Order指定了过滤器必须执行的顺序。 我们将顺序指定为1,因此这将是第一个要执行的过滤器。 在第18行中调用的chain.doFilter将请求和响应对象传递给链中的下一个过滤器,如果是最后一个过滤器,则传递给处理程序。

Inside the doFilter method, I have checked if I can access the user IP address, requestparams from request, payload i.e., the request body from the request object. But the problem we face is the request can be read only once and if we try to access it in filter, it’ll give an error when it is being accessed in the controller. So to prevent that I have created a wrapper that extends HttpServletRequestWrapper to return a copy of the request through it’s constructor.

doFilter方法,我已经检查,如果我可以从请求,有效载荷即,从请求对象的请求体访问用户的IP地址,requestparams。 但是我们面临的问题是该请求只能被读取一次,如果我们尝试在过滤器中访问该请求,则在控制器中对其进行访问时将产生错误。 因此,为防止出现这种情况,我创建了一个包装器,该包装器扩展了HttpServletRequestWrapper以通过其构造函数返回请求的副本。

public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
	private ByteArrayOutputStream cachedBytes;


	public MyHttpServletRequestWrapper(HttpServletRequest request) {
		super(request);
	}


	@Override
	public ServletInputStream getInputStream() throws IOException {
		if (cachedBytes == null)
			cacheInputStream();
		return new CachedServletInputStream();
	}


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


	private void cacheInputStream() throws IOException {
		cachedBytes = new ByteArrayOutputStream();
		IOUtils.copy(super.getInputStream(), cachedBytes);
	}


	/* An inputstream which reads the cached request body */
	public class CachedServletInputStream extends ServletInputStream {
		private ByteArrayInputStream input;


		public CachedServletInputStream() {
			/* create a new input stream from the cached request body */
			input = new ByteArrayInputStream(cachedBytes.toByteArray());
		}


		@Override
		public int read() throws IOException {
			return input.read();
		}


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


		@Override
		public boolean isReady() {
			return false;
		}
		
		@Override
		public void setReadListener(ReadListener listener) {
		}
	}
}

Now that we can access the request object multiple times, I have created a class to return the parameters from the request object which I have used in both Servlet Filter as well as Handler Interceptor for demonstration purpose.

现在我们可以多次访问请求对象,我已经创建了一个类来从请求对象返回参数,该参数已在Servlet Filter和Handler Interceptor中用于演示。

@Service
public class HttpServletReqUtil {


	public String getRemoteAddress(HttpServletRequest request) {
		String ipAddress = request.getHeader("X-FORWARDED-FOR");
		if (ipAddress == null) {
			ipAddress = request.getRemoteAddr();
		}
		return ipAddress;
	}


	public String getPayLoad(HttpServletRequest request) {
		final String method = request.getMethod().toUpperCase();
		if ("POST".equals(method) || "PUT".equals(method)) {
			return extractPostRequestBody(request);
		}
		return "Not a POST or PUT method";
	}


	public String getRequestParams(HttpServletRequest request) {
		final StringBuilder params = new StringBuilder();
		Enumeration<String> parameterNames = request.getParameterNames();
		for (; parameterNames.hasMoreElements();) {
			String paramName = parameterNames.nextElement();
			String paramValue = request.getParameter(paramName);
			if ("password".equalsIgnoreCase(paramName) || "pwd".equalsIgnoreCase(paramName)) {
				paramValue = "*****";
			}
			params.append(paramName).append(": ").append(paramValue).append(System.lineSeparator());
		}
		return params.toString();


	}


	private static String extractPostRequestBody(HttpServletRequest request) {
		if ("POST".equalsIgnoreCase(request.getMethod())) {
			Scanner s = null;
			try {
				s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
			} catch (IOException e) {
				e.printStackTrace();
			}
			return s.hasNext() ? s.next() : "";
		}
		return "";
	}
}

So in the TransactionFilter I have autowired this class and passing the wrapped Request object to each of the functions and printing the values returned from them.

因此,在TransactionFilter我已经自动连接了该类,并将包装的Request对象传递给每个函数,并打印从它们返回的值。

如何实现处理程序拦截器? (How to implement Handler Interceptor?)

The Interceptor is very similar to that of a filter, it just has to be added to an InterceptorRegistry.

Interceptor与过滤器非常相似,只需将其添加到InterceptorRegistry中即可。

@Component
public class GeneralInterceptor implements HandlerInterceptor {


	@Autowired
	private HttpServletReqUtil reqUtil;


	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		final MyHttpServletRequestWrapper wrappedRequest = new MyHttpServletRequestWrapper(request);
		System.out.println("Pre handle method has been called");
		System.out.println("User IP address: " + reqUtil.getRemoteAddress(wrappedRequest));
		System.out.println("Request Params: " + reqUtil.getRequestParams(wrappedRequest));
		System.out.println("Request Payload: " + reqUtil.getPayLoad(wrappedRequest));
		System.out.println("Exiting Pre handle method");
		return true;
	}


	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		   System.out.println("Post handle method has been called");
	}


	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			Exception exception) throws Exception {
		   System.out.println("After Completion method has been called");
	}
}

The Interceptor implements HandlerInterceptor interface which has 3 main methods:

拦截器实现HandlerInterceptor接口,该接口具有3个主要方法:

  • prehandle() — called before the actual handler is executed, but the view is not generated yet

    prehandle () —在执行实际的处理程序之前调用,但尚未生成视图

  • postHandle() — called after the handler is executed

    postHandle () —在处理程序执行后调用

  • afterCompletion() — called after the complete request has finished and view was generated

    afterCompletion ()—在完整请求完成并生成视图之后调用

I have printed the elements from the request object in the same way I did in the case of filter. Now that the interceptor is ready, we need to register with InterceptorRegistry.

我已经按照与过滤器相同的方式打印了请求对象中的元素 现在拦截器已准备就绪,我们需要向InterceptorRegistry注册。

@Component
public class InterceptorAppConfig extends WebMvcConfigurationSupport {
	@Autowired
	GeneralInterceptor generalInterceptor;


	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(generalInterceptor);
	}
}

Now that we have finished coding our filters and interceptors, let’s look the hierarchy in which these methods intercept the rest api calls. I have created a controller to add a student and get student by id, let’s make calls through postman and see how the filter and interceptor perform.

现在,我们已经完成了过滤器和拦截器的编码,让我们看一下这些方法拦截其余api调用的层次结构。 我创建了一个控制器来添加学生并通过id来获取学生,让我们通过邮递员打电话,看看过滤器和拦截器的性能如何。

Image for post
RestApi call to add a Student
RestApi呼叫以添加学生
Image for post
Sequence of print statements in filter and interceptor
过滤器和拦截器中的打印语句序列

If we see the sequence of print statements, the HTTP request first goes to servlet filter, where the IP address of user, and payload is printed, since there aren’t any requestparams in the request, it didn’t print any. The request then reached the pre handle method in the handler interceptor where it prints the same data and then it reaches the actual controller where the student object is saved in the database. So the order goes from 1.Filter ➡️ 2. Interceptor ➡️ 3.Controller for a HTTP request.From controller, the response first goes to post handle method and then to after completion method in interceptor. Let’s also check an example for requestparams.

如果我们看到打印语句的顺序,则HTTP请求首先进入Servlet过滤器,在该Servlet过滤器中打印用户的IP地址和有效负载,因为请求中没有任何requestparams,所以它也没有打印任何内容。 然后,该请求到达处理程序拦截器中的pre handle方法,在该方法中,它打印相同的数据,然后到达实际的控制器,在该控制器中,学生对象保存在数据库中。 因此,为了从变为1.Filter ➡️ 2.拦截➡️ 3.Controller用于HTTP request.From控制器,所述响应第一去交手柄方法,然后在拦截器完成方法之后。 让我们还检查一下requestparams的示例。

Image for post
RestApi call to get Student by id sent in requestparams
RestApi调用以通过requestparams中发送的ID获取Student
Image for post
Sequence of print statements in filter and interceptor for api call with requestparams
过滤器和拦截器中用于带requestparams的api调用的打印语句序列

The sequence is same as before, the only difference here is we don’t see the payload from request body since it is a get request, and the id that was sent in requestparams is visible.

序列与之前相同,唯一的区别是我们看不到来自请求主体的有效负载,因为它是一个获取请求,并且在requestparams中发送的id是可见的。

You can view the code in my GitHub-repo, I’ll drop a link below. If you find this article helpful, please drop some claps and feel free to share. This is Vinesh signing-off. Byeee 😋

您可以在我的GitHub存储库中查看代码,我将在下面放置一个链接。 如果您觉得这篇文章对您有帮助,请放手拍一下,随时分享。 这是Vinesh签字。 再见😋

翻译自: https://medium.com/techno101/servlet-filter-and-handler-interceptor-spring-boot-implementation-b58d397d9dbd

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值