51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 4543|回复: 0
打印 上一主题 下一主题

[转贴] HTTP中POST提交数据的四种方式详解

[复制链接]
  • TA的每日心情
    无聊
    3 天前
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2021-3-8 09:25:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
     前言时刻:
      首先说说为什么写这篇文章,最初学习请求 HTTP 的方式,只了解 POST 和 GET 和两种方式,具体是什么内容也没细看,如同蜻蜓点水一样,根本就没有搞懂?。开始的时候不觉的有啥,但是等到了实际项目的时候,就发现很耽误时间,如无头的苍蝇一样乱找。
      昨天的时候,我抓包一个平台的登录数据,写了一个爬虫模拟登录,但是死活的就是拿不到登录数据。明明表单数据都正确的,我拿 Postman 进行模拟登录也是无果。我想看看网上有解答这种问题的不,关键是我都不知道该怎么搜索。无奈之下我打开Chartless 对比了浏览器官方的请求数据和我用 Postman 发送的数据,对比了下,让我发现了不同,数据是一样的,不同的是数据编码方式不同。
      官方采用的是 raw 中的 Json,而我采用的 form-data 表单,方法错了就算改到胡子白了也成功不了呀??。于是乎网上搜索关于 POST 发送数据 的方式,居然有四种方式。无知的我居然就只知道其中的 form-data,而且还以为urlencode 的方式和 form -data 方式一样。不会就要学习,所以看下面~
      1、HTTP数据传输
      先来看看 HTTP 是如何传输表单数据的。HTTP 是以ASCII 码传输的,建立在TCP/IP 协议之上的应用层规范。HTTP 请求包括:请求行、请求头、消息主体数据。
      形如:
      <method> <url> <version>
      <headers>
      <entity-body>
      # 例如:
      # 请求行
      POST /wp-admin/admin-ajax.php HTTP/1.1
      # 下面都是请求头
      Host: zwjjiaozhu.top
      Content-Length: 69
      Accept: */*
      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
      Content-Type: application/x-www-form-urlencoded; charset=UTF-8
      # 下面是消息主体内容
      action=user_login&username=%E5%8F%91&password=+%E5%8F%91&rememberme=1

      其中这个Content-Type是告诉接受数据的服务端,用什么方式进行解析消息主体。同时发送数据的客户端(浏览器)也是对数据采用同样的编码方式。post 方法中有四种编码方式,详细看2。
      可能你对浏览器如何发送表单也不是很清楚,那么来温故一下。
      form表单属性:
      action:属性定义发送数据要去的地方,如:www.baidu.com
      method:属性定义如何发送数据,常见的方法有:POST,GET,HEAD,PATCH……
      name:属性定义表单的名字
      enctype:定义表单的数据如何编码。如:POST 中对发送数据的四种编码方式。
      target:提交表单后,在那个页面显示响应内容
      _self:默认值,在相同的页面(框架)中显示响应数据,并覆盖原内容
      _blank:在一个新打开的、未命名的窗口打开响应数据
      _parent:在当前内容的父窗口(框架)中打开响应数据。如果 form 表单本身就在顶级框架中,那么等同于self
      例如原生的提交 form 表单html代码:
      <form method="post" enctype="multipart/form-data" name="myForm" action=“http://www.baidu.com”>
        <div>
          <label for="file">Choose a file</label>
          <input type="file" id="file" name="myFile">
        </div>
        <div>
          <button type=“submit”>Send form</button>
        </div>
      </form>

      一旦触发 submit 按钮后,浏览器会对表单内容 myFile:文件内容 以 multipart/form-data 的编码方式,采用POST 的方法向http://www.baidu.com 发送数据。
      以上是原生的表单发送方式。但是现在大都采用更加好用的异步 Ajax 进行数据发送,内容大致相同,就不细说了,有机会在总结。
      2、POST 的四种方式
      POST 方法中对发送数据编码的方式,也就是 Content-Type 有四种方式,其中默认是 application/x-www-form-urlencoded,最方便的是 application/json 。
      四种方式包括:
      · application/x-www-form-urlencoded  (URL encoded)
      · multipart/form-data (键值对型数据)
      · application/json  (Json 类型数据)
      · text/xml  (xml)
      2.1、application/x-www-form-urlencoded
      POST 中很常见的一种编码数据的方式,如果不设置 Content-type 的值,默认就是 urlencoded 。常见的 Ajax 也默认是这种方法。
      POST http://www.example.com HTTP/1.1   
      Content-Type:application/x-www-form-urlencoded;charset=utf-8
      title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

      实际例子:
      # python脚本
      import requests
      url = "http://httpbin.org/post"
      data = {"name":"西园公子","age":"666"}
      headers = {"Content-type":"application/x-www-form-urlencoded"}
      content = requests.post(url=url,data=data,).text
      print(content)
      # 网络请求:
      POST http://httpbin.org/post HTTP/1.1
      Hosthttpbin.org
      User-Agentpython-requests/2.24.0
      Accept-Encodinggzip, deflate
      Accept*/*
      Content-Length49
      Content-Typeapplication/x-www-form-urlencoded
      Connectionkeep-alive
      # 下面是表单内容
      name=%E8%A5%BF%E5%9B%AD%E5%85%AC%E5%AD%90&age=666      # 可以看出汉字是使用utf8编码的
      # 打印
      {
        "args": {},
        "data": "",
        "files": {},
        "form": {           # form表单内容
          "age": "666",
          "name": "\u897f\u56ed\u516c\u5b50"   
        },
        "headers": {
          "Accept": "*/*",
          "Accept-Encoding": "gzip, deflate",
          "Content-Length": "49",
          "Content-Type": "application/x-www-form-urlencoded",   # Content-Type
          "Host": "httpbin.org",
          "User-Agent": "python-requests/2.24.0",     # 这里面如果爬虫不设置user-agent的话,本ban的几率很大
          "X-Amzn-Trace-Id": "Root=1-60010906-7a51fef342a967cd24c32235"
        },
        "json": null,
        "origin": "171.90.37.102",
        "url": "http://httpbin.org/post"
      }

      这里说明一下,requests 是根据传入的键来判断采用那种方法,比如上面的是data= 就说明采用 urlencode 的方法编码数据,其他的方法到最后面一并介绍。
      另外这个 http://httpbin.org/post 链接的作用,就是将你向它发送的数据以及header,原样返回,很是方便。
      2.2、multipart/form-data
      这种编码方式,通常是用在客户端向服务端传送大文件数据,如:图片或者文件。
      首先来解释下什么它的编码方式,首先会生成一个很长的 boundary 字符串分界线,表明下面的都是表单内容,然后紧接着跟的是表单中的第一个键值对中的名称,而后一个换行,跟着值。然后再生成一个boundary 字符串分界线,用于分割不同的键值。之后就重复以上操作,详细的流程请看下方的例子。
      # python脚本
      import requests
      from requests_toolbelt import MultipartEncoder
      m = MultipartEncoder(
          fields={'field0': 'value1', 'field1': 'value2', 'field2': ('filename', open('data.txt', 'rb'), 'text/plain')}
          )
      content = requests.post('http://httpbin.org/post', data=m,
                        headers={'Content-Type': m.content_type}).text
      print(content)
      print(m.content_type)
      # 1、网络请求:
      POST http://httpbin.org/post HTTP/1.1
      Host: httpbin.org
      User-Agent: python-requests/2.24.0
      Accept-Encoding: gzip, deflate
      Accept: */*
      Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962
      Content-Length: 222
      Connection: keep-alive
      # 下面是编码的表单内容
      --e48c73a7a42e403d868095dc3d060962
      Content-Disposition: form-data; name="field0"
      value1
      --e48c73a7a42e403d868095dc3d060962
      Content-Disposition: form-data; name="field1"
      value2
      --e48c73a7a42e403d868095dc3d060962--
      Content-Disposition: form-data; name="field2"; filename="filename"
      Content-Type: text/plain
      ????¥????è¥???-????-????
      --25c88ddc918d40e7a3cd5be0d62476b7--
      # 2、打印
      {
        "args": {},
        "data": "",
        "files": {           # 发送类型是文件的
          "field2": "\u4f60\u597d\uff0c\u897f\u56ed\u516c\u5b50\uff5e"
        },
        "form": {                  # 发送类型是非文件的,有些类似 urlencode 的消息主体
          "field0": "value1",
          "field1": "value2"
        },
        "headers": {
          "Accept": "*/*",
          "Accept-Encoding": "gzip, deflate",
          "Content-Length": "222",
          "Content-Type": "multipart/form-data; boundary=3123ca302f2c4f0dba1050faa8817ab8",
          "Host": "httpbin.org",
          "User-Agent": "python-requests/2.24.0",
          "X-Amzn-Trace-Id": "Root=1-60017280-15e1a08d69c4611737583c87"
        },
        "json": null,
        "origin": "191.80.857.122",
        "url": "http://httpbin.org/post"
      }
      multipart/form-data; boundary=3123ca302f2c4f0dba1050faa8817ab8

      2.3、application/json
      这个是今天的主角,用的超级多,也非常的方便。设置 header 中Content-type,就告诉服务端数据以 Json 字符串的形式存在,相应的就用 Json 的方法解码数据即可。
      Python 脚本例子:
      import requests
      import json
      url="http://httpbin.org/post"
      p_data = {"name": "公子哥", "hobby": "coding"}
      content = requests.post(url, json=json.dumps(p_data),
                        headers={'Content-Type': "application/json"}).text
      print(content)
      # 1、原生网络请求
      POST /post HTTP/1.1
      Host: httpbin.org
      User-Agent: python-requests/2.24.0
      Accept-Encoding: gzip, deflate
      Accept: */*
      Content-Type: application/json
      Content-Length: 62
      Connection: keep-alive
      # 下面是编码成json数据 的表单内容
      "{\"name\": \"\\u516c\\u5b50\\u54e5\", \"hobby\": \"coding\"}"
      # 2、打印数据
      {
        "args": {},
        "data": "\"{\\\"name\\\": \\\"\\\\u516c\\\\u5b50\\\\u54e5\\\", \\\"hobby\\\": \\\"coding\\\"}\"",
        "files": {},
        "form": {},
        "headers": {
          "Accept": "*/*",
          "Accept-Encoding": "gzip, deflate",
          "Content-Length": "62",
          "Content-Type": "application/json",     # 这里指定消息主体编码方式
          "Host": "httpbin.org",
          "User-Agent": "python-requests/2.24.0",
          "X-Amzn-Trace-Id": "Root=1-6001804b-1c8da9b72910a9e1021e02b3"
        },
        "json": "{\"name\": \"\\u516c\\u5b50\\u54e5\", \"hobby\": \"coding\"}",     # 消息主体内容
        "origin": "171.10.87.122",
        "url": "http://httpbin.org/post"
      }

      2.4、text/xml
      这个我还真没咋遇到,不是很熟悉,等后面我仔细研究后在补~
      import requests
      # from requests_toolbelt import MultipartEncoder
      p_data = """
      <?xml version="1.0"?>
      <methodCall>
          <methodName>examples.getStateName</methodName>
          <params>
              <param>
                  <value><i4>41</i4></value>
              </param>
          </params>
      </methodCall>
      """
      content = requests.post(url='http://httpbin.org/post',data=p_data,headers={'Content-Type':'text/xml'}).text
      print(content)
      # 2、打印数据
      {
        "args": {},
        "data": "\n<?xml version=\"1.0\"?>\n<methodCall>\n    <methodName>examples.getStateName</methodName>\n    <params>\n        <param>\n            <value><i4>41</i4></value>\n        </param>\n    </params>\n</methodCall>\n",
        "files": {},
        "form": {},
        "headers": {
          "Accept": "*/*",
          "Accept-Encoding": "gzip, deflate",
          "Content-Length": "200",
          "Content-Type": "text/xml",
          "Host": "httpbin.org",
          "User-Agent": "python-requests/2.24.0",
          "X-Amzn-Trace-Id": "Root=1-60024d51-775180ce108409b413dc68c6"
        },
        "json": null,
        "origin": "106.33.40.219",
        "url": "http://httpbin.org/post"
      }

      3、Reuqests 表示这四种方式
      这里集中将这四种方式说明,否则容易搞混淆。
      p_data = {
      "name": "西园公子",
      }

      1) application/x-www-form-urlencoded:
      这里面是给data=传入参数,参数格式是 Python dict字典。
      requests.post(url="http://httpbin.org/post",data=p_data, headers={"Content-type": "application/x-wwww-form-urlencoded"}).json()

      2) multipart/form-data:
      和上面的一样也是给data=传参,不同的是数据的编码方式不同。
      from requests_toolbelt import MultipartEncoder
      m = MultipartEncoder(
          fields={'field0': 'value1', 'field1': 'value2', 'field2': ('filename', open('data.txt', 'rb'), 'text/plain')}
          )
      requests.post(url="http://httpbin.org/post",data=m, headers={"Content-type": "multipart/form-data"}).json()

      3) application/json:
      这里面是给json=传入参数,参数的格式 Json 字符串,所以需要使用 json.dumps(), 将 Python dict 转 Json 字符串(其实就是 Python 的 str 类型,但是接收方会对字符串进行 Json 解码)。
      import json
      p_data = json.dumps(p_data)
      requests.post(url="http://httpbin.org/post",json=p_data, headers={"Content-type": "application/json"}).json()

      4) text/xml:
      和前面几个的一样也是给data=传参,参数类型是字符串,但是必须按照 xml 的语法写。
      p_data = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Request xmlns="http://tempuri.org/"><jme><JobClassFullName>WeChatJSTicket.JobWS.Job.JobRefreshTicket,WeChatJSTicket.JobWS</JobClassFullName><Action>RUN</Action><Param>1</Param><HostIP>127.0.0.1</HostIP><JobInfo>1</JobInfo><NeedParallel>false</NeedParallel></jme></Request></soap:Body></soap:Envelope>'
      requests.post(url="http://httpbin.org/post",data=p_data, headers={"Content-type": "text/xml"}).json()

      可别忘记在 headers 中的 Conent-type,写入相应的编码方式,否则服务端可不知道怎么解码数据了。
      总结
      在我测试post的几种编码方式的时候,我明明看到官方的 Content-type 是 application/json 的。我用 Postman 也是试成功了,但是我用 Requests 设置 json=json.dumps(p_data) 死活就是不通,最后改成  data=p_data,居然就成功了,我也是服了,也不知是服务端有问题还是我写的有问题。
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-24 07:53 , Processed in 0.063630 second(s), 22 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表