在开发Web应用时,我们经常需要获取HTTP请求的参数。Spring框架提供了多种方式来获取这些参数,其中一种就是使用拦截器(Interceptor)。本文将详细介绍如何利用拦截器获取HTTP请求参数。
1. 拦截器简介
在Spring框架中,拦截器是实现了HandlerInterceptor
接口的类。拦截器可以在请求被处理之前、之后或者在视图被渲染之前进行拦截,以执行一些自定义的操作。
在我们的项目中,我们定义了一个名为RequestInterceptor
的拦截器,它实现了HandlerInterceptor
接口。在这个拦截器中,我们重写了preHandle
方法,用来在请求被处理之前获取请求参数。
@Slf4j
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
// 如果请求是MultipartHttpServletRequest,那么跳过处理
if (request instanceof MultipartHttpServletRequest) {
return true;
}
//获取请求参数
String queryString = request.getQueryString();
log.info("请求参数:{}", queryString);
//获取请求body
byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());
String body = new String(bodyBytes, request.getCharacterEncoding());
log.info("请求体:{}", body);
// 将请求参数和请求体放入当前线程中
HashMap<String, Object> paramMap = MapUtil.newHashMap(2);
paramMap.put("param", queryString);
paramMap.put("body", body);
RequestParamThreadLocal.set(paramMap);
return true;
}
@Override
public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
// 移除当前线程中的数据
RequestParamThreadLocal.remove();
}
}
我们在WebMvcConfig
类中注册了这个拦截器,使其能够拦截所有的请求。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");
}
@Bean
@Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
return new XinDispatcherServlet();
}
}
2. 获取请求参数
在拦截器中,我们可以通过HttpServletRequest
对象来获取请求参数。HttpServletRequest
提供了多种方法来获取请求参数,例如getQueryString()
、getParameterMap()
和getInputStream()
。
但是,HttpServletRequest
的输入流只能被读取一次。一旦读取完毕,就不能再次读取。为了解决这个问题,我们定义了一个名为CustomerHttpServletRequestWrapper
的类,它继承自HttpServletRequestWrapper
,并重写了getInputStream()
和getReader()
方法,使得输入流可以被多次读取。
public class CustomerHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 缓存下来的HTTP body
*/
private final byte[] body;
public CustomerHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = StreamUtils.copyToByteArray(request.getInputStream());
}
/**
* 重新包装输入流
*
* @return {@link ServletInputStream}
* @throws IOException ioexception
*/
@Override
public ServletInputStream getInputStream() throws IOException {
InputStream bodyStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bodyStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
在CustomerDispatcherServlet
类中,我们重写了doDispatch
方法,将原始的HttpServletRequest
包装成CustomerHttpServletRequestWrapper
,然后再传递给super.doDispatch()
方法。
public class CustomerDispatcherServlet extends DispatcherServlet {
@Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
super.doDispatch(new CustomerHttpServletRequestWrapper(request), response);
}
}
3. 存储请求参数
为了在后续的处理中方便地获取请求参数,我们将请求参数和请求体存储在了ThreadLocal
中。我们定义了一个名为RequestParamThreadLocal
的类,它提供了set
和get
方法来存储和获取数据。
public class RequestParamThreadLocal {
private static final ThreadLocal<Map<String, Object>> REQUEST_PARAM_CONTEXT = new ThreadLocal<>();
public static void set(Map<String, Object> map) {
REQUEST_PARAM_CONTEXT.set(map);
}
public static Map<String, Object> get() {
return REQUEST_PARAM_CONTEXT.get();
}
public static void remove() {
REQUEST_PARAM_CONTEXT.remove();
}
}
在RequestInterceptor
的preHandle
方法中,我们将请求参数和请求体存储在了RequestParamThreadLocal
中。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取请求参数和请求体
// ...
// 将请求参数和请求体存储在ThreadLocal中
RequestParamThreadLocal.set(paramMap);
return true;
}
4. 总结
通过使用Spring框架的拦截器,我们可以方便地获取HTTP请求的参数。我们还可以通过自定义HttpServletRequestWrapper
来解决输入流只能被读取一次的问题。最后,我们可以将请求参数和请求体存储在ThreadLocal
中,以便在后续的处理中使用。