前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个由request.getParameter与request.getInputStream引发的bug

一个由request.getParameter与request.getInputStream引发的bug

作者头像
山行AI
发布2019-06-28 16:25:44
2.5K0
发布2019-06-28 16:25:44
举报
文章被收录于专栏:山行AI山行AI

1. 问题

先看下图:

在图中标注部分,会有取不到值的情况。

其实出现这个的原因是Servlet在socket上的读取只能一次。request.getParameter与request.getInputStream这两个系列的方法 能不能取到值, 就完全取决于你调用的先后顺序了。

2. 原因介绍

除了request.getInputStream()及其他多个关联方法不能多次调用外,还有另外一个容易引起bug的地方:

  • 先调用request.getParameter()家族方法,可能导致request.getInputStream()返回空。
  • 先调用request.getInputStream()及其关联方法会导致request.getInputStream()家族方法返回空(或者缺少值)。 不是所有request.getParameter()都会导致读取request.body,在Servlet3.1规范->参数->HTTP协议参数->当参数可用时单节: Servlet规范规定parameter是可以在POST的body中取的,但是需要满足一些条件: 如果 ContentType=application/x-www-form-urlencoded, 并且method=POST时getParameterXxx()方法不但从url中获取参数,也会从body中获取参数,Servlet规范允许把POST body中的内容当成parameter。 如果body刚好是 k1=v1&k2=v2时,body里的东西就会被当做参数被getParameterXxx提走了。同理,先调用 getInputStream()也会导致getParameterXXxx()返回值变少甚至没有。 本质原因:既然是x-www-form-urlencoded,那么POST body就会认为是url的一部分,这是http协议中用来解决url长度受限的问题的解决方案,所以Servlet也尊重这一点。 在Servlet实现时,当调用满足上面条件的getParameter()时,会认为此时的POST body是URL的一部分,会消耗掉getInputStream()中的内容。 所以会导致一种错觉:为什么我POST body中的内容无法从getInputStream()中获取到?还有就是会产生一些误会。在编程过程中应谨防其他代码无意或刻意调用了getParameter()家族方法。

3. 相关问题

1. spring中出现过类似问题

在某个版本的springboot开始,会默认注入org.springframework.web.filter.HiddenHttpMethodFilter,这个Filter就会调用getParameter(),会导致用户代码中的getInputStream()返回空: https://github.com/spring-projects/spring-boot/issues/4782 https://github.com/spring-projects/spring-framework/issues/21439

一般来说,Trace当前使用的具体Servlet容器对应的方法,就能找到谁调用了getParameter()系列方法。 比如undertow实现是:io.undertow.servlet.spec.HttpServletRequestImpl#getParameter tomcat实现是:org.apache.catalina.connector.Request#parseParameters 有兴趣的可以去看看其他容器的实现。

2. go中类似问题

在高可用架构Learning as we Go第五期的page 33有这样的介绍: 在读取了 http.Request.Body 之后, Body 就被排空了,而随后的读取就会返回 []byte{} —— 一个空的主文。 这是因为当你在读取http. Request.Body 的字节时,读取器处于这些字节的结尾,需要重置才能再次读取。但是, http.Request.Body 是一个 io.ReadWriter 并且没有Peek或Seek这样可以帮上忙的方法。一个解决这个问题的方法是先把主文复制进存储空间,然后在读取之后把原来的回拨回去。如果你的请求都很庞大这么做的成本很高。这绝对是一个让人恍然大悟,而且时不时还会让人措手不及的程序。 这个的原因和Servlet一样。

1. 执行go doc http.Request.Body
代码语言:javascript
复制
 type Request struct {     // Body is the request's body.     //     // For client requests a nil body means the request has no     // body, such as a GET request. The HTTP Client's Transport     // is responsible for calling the Close method.     //     // For server requests the Request Body is always non-nil     // but will return EOF immediately when no body is present.     // The Server will close the request body. The ServeHTTP     // Handler does not need to.     Body io.ReadCloser
     // ... other fields elided ... }
2. 执行go doc httputil.DumpRequest
代码语言:javascript
复制
 package httputil // import "net/http/httputil"
 func DumpRequest(req *http.Request, body bool) ([]byte, error)     DumpRequest returns the given request in its HTTP/1.x wire representation.     It should only be used by servers to debug client requests. The returned     representation is an approximation only; some details of the initial request     are lost while parsing it into an http.Request. In particular, the order and     case of header field names are lost. The order of values in multi-valued     headers is kept intact. HTTP/2 requests are dumped in HTTP/1.x form, not in     their original binary representations.
     If body is true, DumpRequest also returns the body. To do so, it consumes     req.Body and then replaces it with a new io.ReadCloser that yields the same     bytes. If DumpRequest returns an error, the state of req is undefined.
     The documentation for http.Request.Write details which fields of req are     included in the dump.

If body is true, DumpRequest also returns the body. To do so, it consumes req

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 问题
    • 2. 原因介绍
      • 3. 相关问题
        • 1. spring中出现过类似问题
        • 2. go中类似问题
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档