4

django.request 获取 requsts.post 提交的数据

 1 year ago
source link: https://zhiqiang.org/coding/django-reqest-get-data-from-requests-post.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1、问题: requests.post 提交的 json 数据在 django.requests.POST 里找不到

在 Python 里,我们可以用requests.post提交json数据:

requests.post("http://example.com/foo?dog=true", json={ "cat": False})

但在 django.requests.POST 里找不到 cat 这个数据:

def foo_callback(request):
    print(request.POST["Cat"]) # raise KeyError

为什么会出现这个问题,需要从 http 协议如何 post 传输数据说起。

2、http 协议如何 post 数据

http 请求是传输了一个字节流,分为三个部分:状态行请求行、请求头、消息主体:

method request-URL version
headers

entity-body

对于 post 请求, method 即 POST , request-URL 为请求的地址, version 一般都是 HTTP/1.1。headers 里定义了客户端信息(比如浏览器版本、是否接受压缩数据等),以及一个更重要的 Content-Type 信息,它告诉服务器,后面提交的 entity-body 的数据格式。理论上 entity-body 可以以任何格式编码,但在实际应用中,一般约定的有 5 种数据格式。

2.1、application/x-www-form-urlencoded

application/x-www-form-urlencoded 是最常见的 POST 提交数据方式。浏览器原生的 form 表单如果不设置 enctype 属性,最终就会以这种方式进行提交。类似于:

POST http://example.com/foo?dog=true HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=ahweg&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

Content-Type 被指定为 application/x-www-form-urlencoded ;其次,提交的数据按照 key1=val1&key2=val2 的方式进行编码, key 和 val 都进行了 URL 转码。

2.2、multipart/form-data

另一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 form 表单的 enctype 等于 multipart/form-data。例如:

POST http://example.com/foo?dog=true HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="ahweg"

title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="ahweg.png"
Content-Type: image/png

PNG ... content of ahweg.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复, boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary-- 标示结束。

上传文件等二进制数据一般采取 multipart/form-data 这种数据格式。

2.3、application/json

由于 JSON 规范的流行,现在越来越多的人把 application/json 作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。例如:

POST http://example.com/foo?dog=true HTTP/1.1 
Content-Type: application/json;charset=utf-8

{"title":"ahweg","sub":[1,2,3,4,5]}

这种方案,可以方便的提交复杂的结构化数据,特别适合 RESTful 的接口。各大抓包工具如 Chrome 自带的开发者工具、Firebug、Fiddler ,都会以树形结构展示 JSON 数据,非常友好。

2.4、text/xml

XML-RPC ( XML Remote Procedure Call )是一种使用 HTTP 作为传输协议, XML 作为编码方式的远程调用规范。典型的 XML-RPC 请求是这样的:

POST http://example.com/foo?dog=true HTTP/1.1 
Content-Type: text/xml

<?xml version="1.0"?>
<methodCall>
    <methodName>examples.getStateName</methodName>
    <params>
        <param>
            <value><i4>125</i4></value>
        </param>
    </params>
</methodCall>

XML-RPC 协议简单、功能够用,各种语言的实现都有。它的使用也很广泛,如 WordPress 的 XML-RPC Api ,搜索引擎的 ping 服务等等。

2.5、text/plain

plain 文本即没有任何预设格式的文本。

POST http://example.com/foo?dog=true HTTP/1.1 
Content-Type: text/xml

plain readable text 

3、为什么 django.requests.POST 里找不到 requests.post 提交的 json 数据

通过request.post提交时, django.requests 并不会解析所有的数据,它只会解析:

  • application/x-www-form-urlencoded ,数据会保存在 django.requests.POST 里面。
  • multipart/form-data , key-value 对会保存在 django.request.POST 里,文件会保存在 django.request.FILES 里。

requests.post提交一个 json 数据时,事实上是提交了一个 application/json 数据,此时 django.request 并不会解析该数据,因此无法通过django.requet.POST来访问。此时需要自己来解析,主要 POST 的原始的 entity-body 会保存在 django.request.body 里:

import json 

def foo_callback(request):
    body = json.loads(request.body)
    print(body["cat"])

如果提交 xml 或 plain 文本数据时,也可以通过django.request.body来解析数据。

4、requests.post 如何选择提交数据的格式

reqiest.post 有很多参数,决定提交数据的格式:

  • data :为字典,以 application/x-www-form-urlencoded 格式发送, django 处理程序可从 request.POST 里获取。
  • json: 以 application/json 格式发送, django 处理程序可用 json.loads(request.body) 还原。
  • files: 上传的文件,以 multipart/form-data 格式发送, django 处理程序可从 request.FILES 里获取。
  • params :作为查询字符串参数发送,影响的是 request-URL , django 处理程序可从 request.GET 里获取。

用户也可以通过 headers 参数直接设置数据格式:

import requests

url = "http://example.com/foo?dog=true"
data = "<data>...</data>"
headers = {"Content-Type": "application/xml"}

response = requests.post(url, data=data, headers=headers)

Q. E. D.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK