post 提交 x-www-form-urlencoded 方式 inputStream 为空

3,504 阅读2分钟

提前阅读

四种常见post提交数据方式
理解http之Content-Type

情况描述

  • content-type 为 x-www-form-urlencoded,但body非key1=value1&key2=value2的格式
  • 服务端使用是jfinal 2.X
  • Postman 模拟参数为
POST /api/rest/service/test HTTP/1.1
Host: v2.ynpay.cc
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: 01e4d8f6-6e8c-c5a3-0672-80da11cdf322

<OrderProcessRequest xsi="http://www.w3.org/2001/XMLSchema-instance">
 <Authentication>
   <TimeStamp>2017-09-19 12:18:35</TimeStamp>
     <ServiceName>web.order.lockOrder</ServiceName>
   <MessageIdentity>testtest</MessageIdentity>
 </Authentication>
 <OrderService>
   <OrderInfo>
     <Number>1001</Number>
    </OrderInfo>
   </OrderService>
 </OrderProcessRequest>
  • 使用request.inputStream 从流里获取数据转化为String 为空,在控制台查看 request.inputStream.request.parseFromData里能看到数据。
  • 使用request.parameterMap 可以获取到数据,但数据格式有问题。

问题原因

根据selvet规范当遇到content-type为x-www-form-urlencoded,各个容器遵循了规范把post数据解析为parameter

自己的理解

  1. delete,get请求没有body。
  2. put,post请求可以有但不一定非要有body
  3. 一般来说,body里的数据都可以通过原始输入流(inputstream)读取再转化为对应的对象(java中字符串属于对象)。
  4. content-type 只是规定了浏览器和服务器解析请求的方式,和传输的内容无关,但是和传输内容的格式有关,浏览器和服务器根据content-type的标准格式,去解析内容,如果指定错content-type,则不一定能够正确解析数据。
  5. content-type为x-www-form-urlencoded,如果严格按照标准格式,body里面提交的参数也应该是key1=value1&key2=value2的形式。

解决方案

  • 请求方的content-type 和body 内容的格式不匹配,但是在服务端接收到之前,servlet已经按照content-type 的标准方式去读取了数据,所以想要在接收时强制改变content-type 也是不可行的。只能把解析出错的数据,想办法解析回原始传递的数据。
    // kotlin 语法
    var str =  IOUtils.toString(request.inputStream)
          if (str.isNullOrBlank()) {
              str  =request.parameterMap.map { entity ->
                  (entity.value as Array<*>).map { "${entity.key}=$it" }.joinToString("&")
              }.joinToString("&")
          }