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

作者: , 共 4209 字 , 共阅读 0

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

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

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

但在 django 的 request.POST 里找不到这个数据:

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://www.example.com 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 转码(空格、&、=等特殊符号变成%开头带十六进制 ascii 值,比如空格变成%20 )。

2.2、multipart/form-data

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

POST http://www.example.com 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://www.example.com 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://www.example.com 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://www.example.com HTTP/1.1 
Content-Type: text/xml

plain readable text 

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

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

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

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

类似文章:
编程 » django, requests, python
这里的 requests 是指 Python 的 requests 包。
编程 » django, html, http, axios
我们用访问网页动态数据时,经常会提交一些参数,比如用axios.get
IT » django
在 django 的setting.py里可以设置 debug 和 production 模式:
安装 selenium ,使用 requestium 来调用 selenium 程序更为简单,因此可一起安装:
IT » apt, pip, python, ubuntu
正常而言,大家都是用 pip 来安装 python 的包。但有时候无意中(通常是为安装某个特定的软件,根据软件的安装提示),会使用 apt 安装 python 包。而且其实很多包都可以通过 apt 来安装的,名字就是包名再加python3-的前缀。安装后的库以及依赖项位于/usr/lib/python3/dist-packages目录下。比如 apt 安装 requests 包:
IT » openai, chatgpt, llm
chatgpt 的开发应用需要使用 API key。申请地址是:https://platform.openai.com/account/api-keys
编程 » xml, python
在升级 django-wiki 后, Python 的 markdown 库里爆出来一个错误:
IT » GIT
分为三步。最简单的:
命令行参数的初步说明,请参考argparse 模块用法实例详解,写的很清晰而详细。
编程 » Python库
Python 的官方自带库json库处理 JSON 很方便,但它只支持标准的 JSON 格式。rapidjson 库来自于 json 的 C++库,速度比json库快五倍,而且支持一些非标准的 JSON 字符串。
户外 » 浆板
具体下水点在东湖湾西区东门,往河边走就有台阶下水口。车违停路边。
户外 » 戏水, 浆板, 昆玉河
周六晚上在颐和园南如意门外昆玉河,也是京密引水渠上,划桨板和游泳。车停在昆明湖东路的红绿灯路口附近(无正式车位,停路边空位),走到河边大约 100 米。