好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

SpringBoot通过请求对象获取输入流无数据

请求对象获取输入流无数据问题

昨天下午在开发的时候遇到了奇怪的事情,在SpringBoot的Controller里面直接使用HttpServletRequest的getInputStream()方法的时候获得的输入流无数据,通过getContentLength()获得内容长度的时候又是有值的,由于昨天比较晚了就没有研究,今天花了点时间查一下原因。

出现这种情况,首先怀疑输入流已经被使用了,由于请求输入流是不带缓存的,使用一次后流就无效了,通常触发解析输入流就是调用了getParameter()等方法,经过检查代码确认没有做过相关处理,所以怀疑SpringBoot底层做了处理。

查了一下SpringBoot的自动装配配置,在WebMvcAutoConfiguration中初始化了一个OrderedHiddenHttpMethodFilter,默认这个过滤器是生效的,相关代码如下:

?

1

2

3

4

5

6

@Bean

@ConditionalOnMissingBean (HiddenHttpMethodFilter. class )

@ConditionalOnProperty (prefix = "spring.mvc.hiddenmethod.filter" , name = "enabled" , matchIfMissing = true )

public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {

    return new OrderedHiddenHttpMethodFilter();

}

OrderedHiddenHttpMethodFilter继承了OrderedHiddenHttpMethodFilter,而OrderedHiddenHttpMethodFilter又继承了HiddenHttpMethodFilter,在该类的doFilterInternal()方法中发现有对参数做处理,相关代码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@Override

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)

    throws ServletException, IOException {

    HttpServletRequest requestToUse = request;

    if ( "POST" .equals(request.getMethod()) && 

    request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null ) {

        String paramValue = request.getParameter( this .methodParam);

        if (StringUtils.hasLength(paramValue)) {

            String method = paramValue.toUpperCase(Locale.ENGLISH);

            if (ALLOWED_METHODS.contains(method)) {

                requestToUse = new HttpMethodRequestWrapper(request, method);

            }

        }

    }

    filterChain.doFilter(requestToUse, response);

}

至此就可以定位问题的所在了,找到了问题下面就来看看如何解决。

方案一:禁用默认的过滤器

SpringBoot在自动装配的时候注入了OrderedHiddenHttpMethodFilter,如果我们不需要该功能,在配置文件中显示的将其设置为false。配置如下:

?

1

spring.mvc.hiddenmethod.filter.enabled= false

方案二:使用@RequestBody注解

在需要获取请求输入流的方法上添加字节数组的参数,并添加@RequestBody注解,示例代码如下:

?

1

2

3

4

@RequestMapping ( "/**" )

public 返回值 方法名( @RequestBody byte [] body) {

    // ...

}

方案三:自定义HiddenHttpMethodFilter过滤器

参考代码如下:

?

1

2

3

4

5

6

7

8

9

10

@Bean

public HiddenHttpMethodFilter hiddenHttpMethodFilter() {

    return new OrderedHiddenHttpMethodFilter(){

        @Override

        protected void doFilterInternal(HttpServletRequest request,

            HttpServletResponse response, FilterChain filterChain)

            throws ServletException, IOException {

            filterChain.doFilter(request, response);

        }

    };

request输入流重复可读

自定义类继承 HttpServletRequestWrapper

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

/**

  * @describe 目的是让其输入流可重复读

  * @author czx

  * @date 2020年5月15日22:53:35

  */

@Slf4j

public class RequestWrapper extends HttpServletRequestWrapper {

    /**

      * 存储body数据的容器

      */

    private final byte [] body;

 

    public RequestWrapper(HttpServletRequest request) throws IOException {

        super (request);

 

        // 将body数据存储起来

        String bodyStr = getBodyString(request);

        body = bodyStr.getBytes(Charset.defaultCharset());

    }

 

    /**

      * 获取请求Body

      *

      * @param request request

      * @return String

      */

    public String getBodyString( final ServletRequest request) {

        try {

            return inputStream2String(request.getInputStream());

        } catch (IOException e) {

            log.error( "" , e);

            throw new RuntimeException(e);

        }

    }

 

    /**

      * 获取请求Body

      *

      * @return String

      */

    public String getBodyString() {

        final InputStream inputStream = new ByteArrayInputStream(body); 

        return inputStream2String(inputStream);

    }

 

    /**

      * 将inputStream里的数据读取出来并转换成字符串

      *

      * @param inputStream inputStream

      * @return String

      */

    private String inputStream2String(InputStream inputStream) {

        StringBuilder sb = new StringBuilder();

        BufferedReader reader = null ;

 

        try {

            reader = new BufferedReader( new InputStreamReader(inputStream, Charset.defaultCharset()));

            String line;

            while ((line = reader.readLine()) != null ) {

                sb.append(line);

            }

        } catch (IOException e) {

            log.error( "" , e);

            throw new RuntimeException(e);

        } finally {

            if (reader != null ) {

                try {

                    reader.close();

                } catch (IOException e) {

                    log.error( "" , e);

                }

            }

        }

 

        return sb.toString();

    }

 

    @Override

    public BufferedReader getReader() throws IOException {

        return new BufferedReader( new InputStreamReader(getInputStream()));

    }

 

    @Override

    public ServletInputStream getInputStream() throws IOException {

 

        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body); 

        return new ServletInputStream() {

            @Override

            public int read() throws IOException {

                return inputStream.read();

            }

 

            @Override

            public boolean isFinished() {

                return false ;

            }

 

            @Override

            public boolean isReady() {

                return false ;

            }

 

            @Override

            public void setReadListener(ReadListener readListener) {

            }

        };

    } 

}

定义一个过滤器 Filter

?

1

2

3

4

5

6

7

8

9

@Slf4j

public class ReplaceStreamFilter implements Filter {

 

    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);

        chain.doFilter(requestWrapper, response);

    }

}

创建过滤器配置类 FilterConfig

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

@Configuration

public class FilterConfig {

 

    /**

      * 注册过滤器

      *

      * @return FilterRegistrationBean

      */

    @Bean

    public FilterRegistrationBean someFilterRegistration() {

        FilterRegistrationBean registration = new FilterRegistrationBean();

        registration.setFilter(replaceStreamFilter());

        registration.addUrlPatterns( "/*" );

        registration.setName( "streamFilter" );

        return registration;

    }

 

    /**

      * 实例化StreamFilter

      * @return Filter

      */

    @Bean (name = "replaceStreamFilter" )

    public Filter replaceStreamFilter() {

        return new ReplaceStreamFilter();

    } 

}

完成以上步骤即可在拦截器中读取request中的body数据 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

@Slf4j

@Component

public class APIInterceptor implements HandlerInterceptor { 

    /**

      * 预处理回调方法,实现处理器的预处理

      * 返回值:true表示继续流程;false表示流程中断,不会继续调用其他的拦截器或处理器

      */

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{

        log.info( "开始拦截请求" );

 

         if (isJson(request)){

             String jsonParam = new RequestWrapper(request).getBodyString();

             JSONObject params = JSONObject.parseObject(jsonParam);

             ......

             return true ;

         }        

         return false ;

    }   

 

    /**

      * 返回json数据给前端

      * @param response

      * @param json

      */

    protected void returnJson(ServletResponse response, JSONObject json){

        PrintWriter writer = null ;

        response.setCharacterEncoding( "UTF-8" );

        response.setContentType( "application/json; charset=utf-8" );

        try {

            writer = response.getWriter();

            writer.print(json.toJSONString());

 

        } catch (IOException e) {

            log.error( "response error" ,e);

        } finally {

            if (writer != null )

                writer.close();

        }

    }  

 

    /**

      * 判断本次请求的数据类型是否为json

      * @param request request

      * @return boolean

      */

    private boolean isJson(HttpServletRequest request) {

        if (request.getContentType() != null ) {

            return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) ||

                    request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);

        } 

        return false ;

    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://blog.csdn.net/jianggujin/article/details/86644914

查看更多关于SpringBoot通过请求对象获取输入流无数据的详细内容...

  阅读:50次