request.getInputStream()取值为空的问题

今天在项目中获取request的请求数据为空,消耗了一天的时间
百度了两篇文章解决了这个问题:原因 解决方案

  1. 阐述下问题:
    项目是记录请求数据及响应数据,但在获取请求数据时使用request.getInputStream()为空,而使用
	Enumeration enu=request.getParameterNames();  
	while(enu.hasMoreElements()){  
		String paraName=(String)enu.nextElement();  
		System.out.println(paraName+": "+request.getParameter(paraName));  
	}  

但是获取的值是不完整的,它将原数据前面部分作为参数key,后面部分作为参数value。

  1. 分析原因
    经过一顿操作,发现客户端上送的Content-Type的值为 这里可以看下

application/x-www-form-urlencoded;charset=UTF-8

而不是

text/xml
text/plain
application/json

所以请求数据的值是以key/value形式存储的。
若在使用request.getInputStream()前已经使用过getParameter或者@requestParam注解方式,则request.getInputStream()获取为空。

3 解决方案
1 将post请求的Content-Type 设置为text/xml
2 通过遍历获取所有参数,再获取参数值
3 避免在request.getInputStream()之前使用getParameter或者@requestParam注解方式
4 对httprequest进行修饰
定义InputStreamHttpServletRequestWrapper类继承于HttpServletRequestWrapper对请求数据进行处理


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.web.bind.annotation.RequestMethod;

public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper{

    private final byte[] streamBody;
    private static final int BUFFER_SIZE = 4096;
    
	//对请求数据判断,getInputStream()为空的数据对key和value值进行拼接
    public InputStreamHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        byte[] bytes = inputStream2Byte(request.getInputStream());
        if (bytes.length == 0 && RequestMethod.POST.name().equals(request.getMethod())) {
            //从ParameterMap获取参数,并保存以便多次获取
            bytes = request.getParameterMap().entrySet().stream()
                    .map(entry -> {
                        String result;
                        String[] value = entry.getValue();
                        if (value != null && value.length > 1) {
                            result = Arrays.stream(value).map(s -> entry.getKey() + "=" + s)
                                    .collect(Collectors.joining("&"));
                        } else {
                            result = entry.getKey() + "=" + value[0];
                        }

                        return result;
                    }).collect(Collectors.joining("&")).getBytes();
        }
        	//System.err.println(new String(bytes));
        streamBody = bytes;
    }

    private byte[] inputStream2Byte(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[BUFFER_SIZE];
        int length;
        while ((length = inputStream.read(bytes, 0, BUFFER_SIZE)) != -1) {
            outputStream.write(bytes, 0, length);
        }

        return outputStream.toByteArray();
    }


    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(streamBody);

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

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

            @Override
            public void setReadListener(ReadListener listener) {

            }

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

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


写doFilterInternal类对InputStreamHttpServletRequestWrapper 进行调用即过滤器对请求的处理,该类继承于OncePerRequestFilter,一次请求只调用一次

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;


public class InputStreamWrapperFilter extends OncePerRequestFilter{

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
     HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        ServletRequest servletRequest = new InputStreamHttpServletRequestWrapper(httpServletRequest);
        filterChain.doFilter(servletRequest, httpServletResponse);
    }
}

最后确定过滤器调用的顺序
在application类中

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import com.springboot.SpringBootUtill.InputStreamWrapperFilter;

@SpringBootApplication
@Slf4j
public class Application
{
  public static void main(String[] args)
  {
    log.info("=================开始成功=================");
    SpringApplication.run(Application.class, args);
    log.info("=================启动成功=================");
  }
	@Bean
  @Order(1)
  public FilterRegistrationBean inputStreamWrapperFilterRegistration() {
      FilterRegistrationBean registrationBean = new FilterRegistrationBean();
      registrationBean.setFilter(new InputStreamWrapperFilter());
      registrationBean.setName("inputStreamWrapperFilter");
      registrationBean.addUrlPatterns("/*");
      return registrationBean;
  }
}

到此为止该问题已经解决了,可以获取到参数了

//获取请求body
	String getRequestMsg(HttpServletRequest request) throws IOException {
		BufferedReader br = request.getReader();
	    String str, wholeStr = "";   
	    while((str = br.readLine()) != null){
	        wholeStr += str;
	}
		return wholeStr;  
	}

现在还有个疑问,我并没有在request.getInputStream()前使用过getParameter或者@requestParam注解方式。是不是springboot在嵌入tomcat后,请求自动调用了getParameter方法呢。

猜你喜欢

转载自blog.csdn.net/qq_31941773/article/details/83016141
今日推荐