解决HttpServletRequest的输入流只能读取一次的问题Word文件下载.docx
《解决HttpServletRequest的输入流只能读取一次的问题Word文件下载.docx》由会员分享,可在线阅读,更多相关《解决HttpServletRequest的输入流只能读取一次的问题Word文件下载.docx(12页珍藏版)》请在冰点文库上搜索。
从上图中的部分源码可以看到,该类并没有真正去实现HttpServletRequest的方法,而只是在方法内又去调用HttpServletRequest的方法,所以我们可以通过继承该类并实现想要重新定义的方法以达到包装原生HttpServletRequest对象的目的。
首先我们要定义一个容器,将输入流里面的数据存储到这个容器里,这个容器可以是数组或集合。
然后我们重写getInputStream方法,每次都从这个容器里读数据,这样我们的输入流就可以读取任意次了。
具体的实现代码如下:
packagecom.example.wrapperdemo.controller.wrapper;
importlombok.extern.slf4j.Slf4j;
importjavax.servlet.ReadListener;
importjavax.servlet.ServletInputStream;
importjavax.servlet.ServletRequest;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletRequestWrapper;
importjava.io.*;
importjava.nio.charset.Charset;
/**
*@author01
*@programwrapper-demo
*@description包装HttpServletRequest,目的是让其输入流可重复读
*@create2018-12-2420:
48
*@since1.0
**/@Slf4jpublicclassRequestWrapperextendsHttpServletRequestWrapper{
/**
*存储body数据的容器
*/
privatefinalbyte[]body;
publicRequestWrapper(HttpServletRequestrequest)throwsIOException{
super(request);
//将body数据存储起来
StringbodyStr=getBodyString(request);
body=bodyStr.getBytes(Charset.defaultCharset());
}
*获取请求Body
*
*@paramrequestrequest
*@returnString
publicStringgetBodyString(finalServletRequestrequest){
try{
returninputStream2String(request.getInputStream());
}catch(IOExceptione){
log.error("
"
e);
thrownewRuntimeException(e);
*获取请求Bodyhttp:
//www.f-1.cc
publicStringgetBodyString(){
finalInputStreaminputStream=newByteArrayInputStream(body);
returninputStream2String(inputStream);
*将inputStream里的数据读取出来并转换成字符串
*@paraminputStreaminputStream
privateStringinputStream2String(InputStreaminputStream){
StringBuildersb=newStringBuilder();
BufferedReaderreader=null;
reader=newBufferedReader(newInputStreamReader(inputStream,Charset.defaultCharset()));
Stringline;
while((line=reader.readLine())!
=null){
sb.append(line);
}finally{
if(reader!
reader.close();
returnsb.toString();
@Override
publicBufferedReadergetReader()throwsIOException{
returnnewBufferedReader(newInputStreamReader(getInputStream()));
publicServletInputStreamgetInputStream()throwsIOException{
finalByteArrayInputStreaminputStream=newByteArrayInputStream(body);
returnnewServletInputStream(){
publicintread()throwsIOException{
returninputStream.read();
publicbooleanisFinished(){
returnfalse;
publicbooleanisReady(){
publicvoidsetReadListener(ReadListenerreadListener){
};
}
除了要写一个包装器外,我们还需要在过滤器里将原生的HttpServletRequest对象替换成我们的RequestWrapper对象,代码如下:
packagecom.example.wrapperdemo.controller.filter;
importcom.example.wrapperdemo.controller.wrapper.RequestWrapper;
importjavax.servlet.*;
importjava.io.IOException;
*@author01
*@description替换HttpServletRequest
*@create2018-12-2421:
04
**/@Slf4jpublicclassReplaceStreamFilterimplementsFilter{
publicvoidinit(FilterConfigfilterConfig)throwsServletException{
log.info("
StreamFilter初始化..."
);
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{
ServletRequestrequestWrapper=newRequestWrapper((HttpServletRequest)request);
chain.doFilter(requestWrapper,response);
publicvoiddestroy(){
StreamFilter销毁..."
然后我们就可以在拦截器中愉快的获取json数据也不慌controller层会报错了:
packagecom.example.wrapperdemo.controller.interceptor;
importorg.springframework.http.MediaType;
importorg.springframework.web.servlet.HandlerInterceptor;
importorg.springframework.web.servlet.ModelAndView;
importjavax.servlet.http.HttpServletResponse;
*@description签名拦截器
08
**/@Slf4jpublicclassSignatureInterceptorimplementsHandlerInterceptor{
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{
[preHandle]executing...requesturiis{}"
request.getRequestURI());
if(isJson(request)){
//获取json字符串
StringjsonParam=newRequestWrapper(request).getBodyString();
[preHandle]json数据:
{}"
jsonParam);
//验签逻辑...略...
returntrue;
publicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{
publicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{
*判断本次请求的数据类型是否为json
*@returnboolean
privatebooleanisJson(HttpServletRequestrequest){
if(request.getContentType()!
returnrequest.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)||
request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);
编写完以上的代码后,还需要将过滤器和拦截器在配置类中进行注册才会生效,过滤器配置类代码如下:
packagecom.example.wrapperdemo.config;
importcom.example.wrapperdemo.controller.filter.ReplaceStreamFilter;
importorg.springframework.boot.web.servlet.FilterRegistrationBean;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importjavax.servlet.Filter;
*@description过滤器配置类
06
**/@ConfigurationpublicclassFilterConfig{
*注册过滤器
*@returnFilterRegistrationBean
@Bean
publicFilterRegistrationBeansomeFilterRegistration(){
FilterRegistrationBeanregistration=newFilterRegistrationBean();
registration.setFilter(replaceStreamFilter());
registration.addUrlPatterns("
/*"
registration.setName("
streamFilter"
returnregistration;
*实例化StreamFilter
*@returnFilter
@Bean(name="
replaceStreamFilter"
)
publicFilterreplaceStreamFilter(){
returnnewReplaceStreamFilter();
拦截器配置类代码如下:
importcom.example.wrapperdemo.controller.interceptor.SignatureInterceptor;
importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;
importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;
*@description
16
**/@ConfigurationpublicclassInterceptorConfigimplementsWebMvcConfigurer{
publicSignatureInterceptorgetSignatureInterceptor(){
returnnewSignatureInterceptor();
*注册拦截器
*@paramregistryregistry
publicvoidaddInterceptors(InterceptorRegistryregistry){
registry.addInterceptor(getSignatureInterceptor())
.addPathPatterns("
/**"
接下来我们就可以测试一下在拦截器中读取了输入流后在controller层是否还能正常接收数据,首先定义一个实体类,代码如下:
packagecom.example.wrapperdemo.param;
importlombok.AllArgsConstructor;
importlombok.Builder;
importlombok.Data;
importlombok.NoArgsConstructor;
11
**/@Data@Builder@NoArgsConstructor@AllArgsConstructorpublicclassUserParam{
privateStringuserName;
privateStringphone;
privateStringpassword;
然后写一个简单的Controller,代码如下:
packagecom.example.wrapperdemo.controller;
importcom.example.wrapperdemo.param.UserParam;
importorg.springframework.web.bind.annotation.PostMapping;
importorg.springframework.web.bind.annotation.RequestBody;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;
*@programwrapper-demo
47
**/@RestController@RequestMapping("
/user"
)publicclassDemoController{
@PostMapping("
/register"
publicUserParamregister(@RequestBodyUserParam