在Springboot HandlerInterceptor中获取GET和POST请求参数
创始人
2024-01-12 23:30:45

  说一个最近工作中遇到的事,我们系统因为业务发展的问题,针对不同用户群体做了两套系统(B系统和S系统),底层基础功能一样,但偏上层的业务有差异,最近我们想将底层基础功能提供一个统一的入口,所以就新起了一个类似业务网关的服务,把两个系统的接口封装一层,提供一个统一的接口出去,然后B业务的请求转发的B系统,S业务的请求转发到S系统。

  但这里就有个很重要的问题了,一个请求进来之后,我们如何判定这个请求应该转发到B系统还是S系统? 当然上游请求的时候可以在请求参数里带上他们的业务来源,我们直接根据业务来源路由即可,实际上最开始我们也是这么做的。 但有个问题时有些接口业务方很难判定自己的请求是属于哪个业务方的,而且未来B和S两个系统也是要做融合的。长期的话,使用者对业务标识字段有很大的理解成本,而且未来融合的时候业务标识会变动,到时候推动上游改造就很难很难了。

  幸运的是,我们自己其实可以通过请求中的id信息来判断出是哪个业务的。但是目前已经6-7接口了,未来肯定会继续增加接口,难道每个接口都需要加判断逻辑? 这显然很不程序猿! 上面已经说到了,每个接口都需要传id信息,那是不是只需要写个interceptor把id信息解析出来,然后统一做处理就行了! 实际实现的时候,我发现还是稍微有一点点复杂,特此记录下。

  首先如果是将参数放在params里的请求,就很简单了,只需要在Interceptor直接读出来就行了。 形如String orderId = request.getParameter("orderId"); 但从请求body里获取参数就比较复杂了,得从HttpServletRequest里的inputStream里获取到。 但是,java中inputStream的特性,从里面读出内容后,之后就没法读了,也就是说你在Inteceptor里取到了Body,后面的流程就再也取不到了,总之这次的http请求就费了。当然解决方法也很简单,通过我查阅资料,只需要用HttpServletRequestWrapper将Request请求包装一层就行了,作用就是让body能重复读取,具体代码如下:

  先来看下RequestWrapper的实现:

public class RequestWrapper extends HttpServletRequestWrapper {private String body;/*** Wrapper的构造方法,主要是将body里的内容取出来,然后存储到对象中的body变量中,方便* 后续复用** @param request The request to wrap* @throws IllegalArgumentException if the request is null*/public RequestWrapper(HttpServletRequest request) throws IOException {super(request);StringBuilder stringBuilder = new StringBuilder();BufferedReader bufferedReader = null;try {InputStream inputStream = request.getInputStream();if (inputStream != null) {bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));char[] charBuffer = new char[128];int bytesRead = -1;while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {stringBuilder.append(charBuffer, 0, bytesRead);}}} catch (IOException ex) {throw ex;} finally {if (bufferedReader != null) {try {bufferedReader.close();} catch (IOException ex) {throw ex;}}}body = stringBuilder.toString();}/*** 这里才是关键,这里将getInputStream重新,让它能重复获取到body里的内容,这样才不会影响后续的流程* @return* @throws IOException*/@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("UTF-8"));ServletInputStream servletInputStream = new ServletInputStream() {@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) { }@Overridepublic int read() throws IOException {return byteArrayInputStream.read();}};return servletInputStream;}/*** 重写获取 字符流的方式* @return* @throws IOException*/@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(this.getInputStream(), Charsets.UTF_8));}/*** 获取body* @return*/public String getBody() {return this.body;}
}

  上面RequestWrapper的代码我查阅资料的时候在多篇博文中看到了,但是单有RequestWrapper还不足以完成整个请求,而且我看很多网络上的博客都是只在Interceptor中Wapper,但实际这样是不对的,而且也完全不需要,因为必须要替换掉整个请求链路中的Request才行。这里我们只需要在Filter中将普通的Request替换成我们自己的RequestWrapper ,具体代码如下:

@Component
@WebFilter(urlPatterns = "/*", filterName = "wapperRequestFilter")
public class WapperRequestFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {ServletRequest requestWrapper = null;if(servletRequest instanceof HttpServletRequest) {requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);}if(requestWrapper == null) {filterChain.doFilter(servletRequest, servletResponse);} else {// 将请求封装并传递下去filterChain.doFilter(requestWrapper, servletResponse);}}@Overridepublic void destroy() {}
}

  接下来我们就可以直接在Inteceptor里使用RequestWrapper来获取Body里的内容,并且不会影响到后续的请求处理,具体代码如下:

@Component
@Slf4j
public class WebInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String orderId = request.getParameter("orderId");String body = StringUtils.EMPTY;if (request instanceof RequestWrapper) {body = ((RequestWrapper) request).getBody();}if (StringUtils.isNotBlank(body)) {JSONObject jsonObject = JSONObject.parseObject(body);orderId = jsonObject.getString("orderId");}LOGGER.info("orderId:{}", orderId);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

   在上面的代码中WebInterceptor可以拿到我们替换后的WapperRequest,这个里面可以直接获取到body,而且也不影响后续流程继续获取body。 今天的内容就到这里了,虽然只是自己解决问题的一个小笔记,但也希望能帮助到大家。

相关内容

热门资讯

demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...