spring boot拦截器中获取request post请求中的参数
最近有一个需要从拦截器中获取post请求的参数的需求,这里记录一下处理过程中出现的问题。
首先想到的就是request.getparameter(string )方法,但是这个方法只能在get请求中取到参数,post是不行的,后来想到了使用流的方式,调用request.getinputstream()获取流,然后从流中读取参数,如下代码所示:
string body = ""; stringbuilder stringbuilder = new stringbuilder(); bufferedreader bufferedreader = null; inputstream inputstream = null; try { inputstream = request.getinputstream(); if (inputstream != null) { bufferedreader = new bufferedreader(new inputstreamreader(inputstream)); char[] charbuffer = new char[128]; int bytesread = -1; while ((bytesread = bufferedreader.read(charbuffer)) > 0) { stringbuilder.append(charbuffer, 0, bytesread); } } else { stringbuilder.append(""); } } catch (ioexception ex) { e.printstacktrace(); } finally { if (inputstream != null) { try { inputstream.close(); } catch (ioexception e) { e.printstacktrace(); } } if (bufferedreader != null) { try { bufferedreader.close(); } catch (ioexception e) { e.printstacktrace(); } } } body = stringbuilder.tostring();
代码中的body就是request中的参数,我这里传的是json数据:{"page": 1, "pagesize": 10},那么body就是:body = "{"page": 1, "pagesize": 10}",一个json字符串。这样是可以成功获取到post请求的body,但是,经过拦截器后,参数经过@requestbody注解赋值给controller中的方法的时候,却抛出了一个这样的异常:
org.springframework.http.converter.httpmessagenotreadableexception: required request body is missing
在网上查找资料后发现,request的输入流只能读取一次,那么这是为什么呢?下面是答案:
那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaapi中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的io读取流都可以调用该方法!servletinputstream是不能调用reset方法,这就导致了只能调用一次getinputstream()。
摘自:
那么有什么办法可以用户解决呢?上面这篇博客中提到了解决方案,就是重写httpservletrequestwrapper把request保存下来,然后通过过滤器把保存下来的request再填充进去,这样就可以多次读取request了。步骤如下所示:
①写一个类,继承httpservletrequestwrapper
import javax.servlet.readlistener; import javax.servlet.servletinputstream; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletrequestwrapper; import java.io.*; public class requestwrapper extends httpservletrequestwrapper { private final string body; public requestwrapper(httpservletrequest request) { super(request); stringbuilder stringbuilder = new stringbuilder(); bufferedreader bufferedreader = null; inputstream inputstream = null; try { inputstream = request.getinputstream(); if (inputstream != null) { bufferedreader = new bufferedreader(new inputstreamreader(inputstream)); char[] charbuffer = new char[128]; int bytesread = -1; while ((bytesread = bufferedreader.read(charbuffer)) > 0) { stringbuilder.append(charbuffer, 0, bytesread); } } else { stringbuilder.append(""); } } catch (ioexception ex) { } finally { if (inputstream != null) { try { inputstream.close(); } catch (ioexception e) { e.printstacktrace(); } } if (bufferedreader != null) { try { bufferedreader.close(); } catch (ioexception e) { e.printstacktrace(); } } } body = stringbuilder.tostring(); } @override public servletinputstream getinputstream() throws ioexception { final bytearrayinputstream bytearrayinputstream = new bytearrayinputstream(body.getbytes()); servletinputstream servletinputstream = new servletinputstream() { @override public boolean isfinished() { return false; } @override public boolean isready() { return false; } @override public void setreadlistener(readlistener readlistener) { } @override public int read() throws ioexception { return bytearrayinputstream.read(); } }; return servletinputstream; } @override public bufferedreader getreader() throws ioexception { return new bufferedreader(new inputstreamreader(this.getinputstream())); } public string getbody() { return this.body; } }
②拦截器层面
import com.alibaba.fastjson.json; import com.miniprogram.api.douyin.user.req.dyuserreq; import com.miniprogram.common.auth.visitlimitcount; import com.miniprogram.common.cache.rediscache; import com.miniprogram.common.config.interceptorconfigmap; import com.miniprogram.common.config.interceptorurlconfig; import com.miniprogram.common.douyin.searchenginemapconstants; import com.miniprogram.common.response.response; import com.miniprogram.common.session.*; import com.miniprogram.common.utils.dateutil; import com.miniprogram.dao.common.userloginentity.users; import com.miniprogram.service.douyin.users.usersservice; import com.miniprogram.web.douyin.config.requestwrapper; import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.beans.beanutils; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.component; import org.springframework.web.servlet.modelandview; import org.springframework.web.servlet.handler.handlerinterceptoradapter; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import java.io.bufferedreader; import java.io.inputstreamreader; import java.util.arraylist; import java.util.hashmap; import java.util.list; import java.util.map; @component("authsecurityinterceptor") public class authsecurityinterceptor extends handlerinterceptoradapter { private logger logger = loggerfactory.getlogger(authsecurityinterceptor.class); @autowired private rediscache rediscache; @autowired private visitlimitcount visitlimitcount; @override public boolean prehandle(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, object o) throws exception { try { requestwrapper requestwrapper = new requestwrapper(httpservletrequest); string body = requestwrapper.getbody(); system.out.println(body); return true; }catch (exception e){ logger.error("权限判断出错",e); } return false; } @override public void posthandle(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, object o, modelandview modelandview) throws exception { } @override public void aftercompletion(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, object o, exception e) throws exception { } }
③过滤器filter,用来把request传递下去
import javax.servlet.*; import javax.servlet.annotation.webfilter; import javax.servlet.http.httpservletrequest; import java.io.ioexception; @webfilter(urlpatterns = "/*",filtername = "channelfilter") public class channelfilter implements filter { @override public void init(filterconfig filterconfig) throws servletexception { } @override public 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); } } @override public void destroy() { } }
④在启动类中注册拦截器
import org.springframework.boot.springapplication; import org.springframework.boot.autoconfigure.springbootapplication; import org.springframework.boot.web.servlet.multipartconfigfactory; import org.springframework.boot.web.servlet.servletcomponentscan; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.componentscan; import org.springframework.context.annotation.configuration; @springbootapplication // @servletcomponentscan //注册过滤器注解 @configuration public class webapplication { public static void main(string[] args) { springapplication.run(webapplication.class, args); } }
经测试,问题解决
上一篇: 实现AutoMapper(1.0版本)
下一篇: 如何使内容不变成别人伪原创的题材