为什么 Django HttpRequest (POST) 似乎将 request.data 设置为 request.body 中 "data" 键的值?
Why does a Django HttpRequest (POST) appear to set request.data to the value of the "data" key from request.body?
背景
我有一个 Django wsgi 应用程序,它也使用了 django-rest-framework。向我的应用程序发出 POST 请求,该请求具有包含密钥 "data" 的 JSON 负载,例如。
{'meta': {'some': 'stuff'}, 'data': {'other': 'stuff'}}
我注意到 WSGIRequest 对象(一旦它被初始化,即还不是 django-rest-framework 请求对象)有 request.body 包含完整的有效负载,但是 request.data包含 request.body['data'] 的值。我的应用程序的编写取决于 request.data.
中仅存在 "data" 键值这一事实
我最近将 Django 从 1.6 升级到 1.11,并将 django-rest-framework 从 2.3.8 升级到 3.8.2。较新版本的 DRF 实现了它自己的 request.data 而之前它只是将它代理到底层的 Django 请求对象。新实现将 request.data 设置为已解析的 request.body。这进一步破坏了我的应用程序。
问题
我在源代码或其他地方找不到任何迹象表明 request.data 应该不同于 request.body。相反,有几个问题似乎理所当然地认为它们是相同的。谁能帮我弄清楚我是否发现了错误或者这是否是预期的行为?我的应用程序是否做出了不必要的假设?
谢谢
更新
这是一个邮递员请求示例:
POST /api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/execute/?hierarchy=1c0ffee2c0deab00da595101&nowait=true&format=json HTTP/1.1
Host: localhost
Content-Type: application/json
Accept: application/json
Authorization: Basic c3lzYWRtaW46c3lzYWRtaW4=
Cache-Control: no-cache
Postman-Token: 5140e8db-6ffa-e2d2-9791-698030fe6caf
{"data":{"name":"_test","parameters":{"parallel":"false","max_workers":"1"},"rollback_disabled":false,"workflow":[{"method":"add","entity_type":"model","entity":"data/Countries","templates":[{"template":"_test"}],"advanced_find_search_direction":"full_tree"}]},"request_meta":{},"meta":{"references":{"form_href":"/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101"}}}
如果我在我的 wsgi.py 中进行调试,则会看到问题:
>>> request.body
'{"data":{"name":"_test","parameters":{"parallel":"false","max_workers":"1"},"rollback_disabled":false,"workflow":[{"method":"add","entity_ty
pe":"model","entity":"data/Countries","templates":[{"template":"_test"}],"advanced_find_search_direction":"full_tree"}]},"request_meta":{},"m
eta":{"references":{"form_href":"/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101"}}}'
>>> request.data
{u'rollback_disabled': False, u'name': u'_test', u'parameters': {u'max_workers': u'1', u'parallel': u'false'}, u'workflow': [{u'templates':
[{u'template': u'_test'}], u'entity': u'data/Countries', u'method': u'add', u'advanced_find_search_direction': u'full_tree', u'entity_type':
u'model'}]}
我可以看到底层的请求对象,这里的行为又很清楚了:
>>> request._request.data
{u'rollback_disabled': False, u'name': u'_test', u'parameters': {u'max_workers': u'1', u'parallel': u'false'}, u'workflow': [{u'templates':
[{u'template': u'_test'}], u'entity': u'data/Countries', u'method': u'add', u'advanced_find_search_direction': u'full_tree', u'entity_type':
u'model'}]}
>>> request.data
{u'request_meta': {}, u'meta': {u'references': {u'form_href':
u'/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101'}}, u'data': {u'rollback_disabled': False,
u'name': u'_test', u'parameters': {u'max_workers': u'1', u'parallel': u'false'}, u'workflow': [{u'templates': [{u'template': u'_test'}],
u'entity': u'data/Countries', u'method': u'add', u'advanced_find_search_direction': u'full_tree', u'entity_type': u'model'}]}}
>>> request.body
'{"data":{"name":"_test","parameters":{"parallel":"false","max_workers":"1"},"rollback_disabled":false,"workflow":[{"method":"add","entity_ty
pe":"model","entity":"data/Countries","templates":[{"template":"_test"}],"advanced_find_search_direction":"full_tree"}]},"request_meta":{},"m
eta":{"references":{"form_href":"/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101"}}}'
>>> request._request.body
'{"data":{"name":"_test","parameters":{"parallel":"false","max_workers":"1"},"rollback_disabled":false,"workflow":[{"method":"add","entity_ty
pe":"model","entity":"data/Countries","templates":[{"template":"_test"}],"advanced_find_search_direction":"full_tree"}]},"request_meta":{},"m
eta":{"references":{"form_href":"/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101"}}}'
我已经回答了我自己的问题。我发现了一个将 request.data 设置为 request.body['data'] 的中间件。新版本的DRF在这里有命名空间冲突,因为它也设置了request.data,但这是在Django中间件生效之后发生的。为了解决这个问题,我覆盖了 initialize_request 并在访问数据 属性 后手动设置了 _full_data。不幸的是,更改我的应用程序使用 request.data.
的所有情况是不切实际的
背景
我有一个 Django wsgi 应用程序,它也使用了 django-rest-framework。向我的应用程序发出 POST 请求,该请求具有包含密钥 "data" 的 JSON 负载,例如。
{'meta': {'some': 'stuff'}, 'data': {'other': 'stuff'}}
我注意到 WSGIRequest 对象(一旦它被初始化,即还不是 django-rest-framework 请求对象)有 request.body 包含完整的有效负载,但是 request.data包含 request.body['data'] 的值。我的应用程序的编写取决于 request.data.
中仅存在 "data" 键值这一事实我最近将 Django 从 1.6 升级到 1.11,并将 django-rest-framework 从 2.3.8 升级到 3.8.2。较新版本的 DRF 实现了它自己的 request.data 而之前它只是将它代理到底层的 Django 请求对象。新实现将 request.data 设置为已解析的 request.body。这进一步破坏了我的应用程序。
问题
我在源代码或其他地方找不到任何迹象表明 request.data 应该不同于 request.body。相反,有几个问题似乎理所当然地认为它们是相同的。谁能帮我弄清楚我是否发现了错误或者这是否是预期的行为?我的应用程序是否做出了不必要的假设?
谢谢
更新
这是一个邮递员请求示例:
POST /api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/execute/?hierarchy=1c0ffee2c0deab00da595101&nowait=true&format=json HTTP/1.1
Host: localhost
Content-Type: application/json
Accept: application/json
Authorization: Basic c3lzYWRtaW46c3lzYWRtaW4=
Cache-Control: no-cache
Postman-Token: 5140e8db-6ffa-e2d2-9791-698030fe6caf
{"data":{"name":"_test","parameters":{"parallel":"false","max_workers":"1"},"rollback_disabled":false,"workflow":[{"method":"add","entity_type":"model","entity":"data/Countries","templates":[{"template":"_test"}],"advanced_find_search_direction":"full_tree"}]},"request_meta":{},"meta":{"references":{"form_href":"/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101"}}}
如果我在我的 wsgi.py 中进行调试,则会看到问题:
>>> request.body
'{"data":{"name":"_test","parameters":{"parallel":"false","max_workers":"1"},"rollback_disabled":false,"workflow":[{"method":"add","entity_ty
pe":"model","entity":"data/Countries","templates":[{"template":"_test"}],"advanced_find_search_direction":"full_tree"}]},"request_meta":{},"m
eta":{"references":{"form_href":"/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101"}}}'
>>> request.data
{u'rollback_disabled': False, u'name': u'_test', u'parameters': {u'max_workers': u'1', u'parallel': u'false'}, u'workflow': [{u'templates':
[{u'template': u'_test'}], u'entity': u'data/Countries', u'method': u'add', u'advanced_find_search_direction': u'full_tree', u'entity_type':
u'model'}]}
我可以看到底层的请求对象,这里的行为又很清楚了:
>>> request._request.data
{u'rollback_disabled': False, u'name': u'_test', u'parameters': {u'max_workers': u'1', u'parallel': u'false'}, u'workflow': [{u'templates':
[{u'template': u'_test'}], u'entity': u'data/Countries', u'method': u'add', u'advanced_find_search_direction': u'full_tree', u'entity_type':
u'model'}]}
>>> request.data
{u'request_meta': {}, u'meta': {u'references': {u'form_href':
u'/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101'}}, u'data': {u'rollback_disabled': False,
u'name': u'_test', u'parameters': {u'max_workers': u'1', u'parallel': u'false'}, u'workflow': [{u'templates': [{u'template': u'_test'}],
u'entity': u'data/Countries', u'method': u'add', u'advanced_find_search_direction': u'full_tree', u'entity_type': u'model'}]}}
>>> request.body
'{"data":{"name":"_test","parameters":{"parallel":"false","max_workers":"1"},"rollback_disabled":false,"workflow":[{"method":"add","entity_ty
pe":"model","entity":"data/Countries","templates":[{"template":"_test"}],"advanced_find_search_direction":"full_tree"}]},"request_meta":{},"m
eta":{"references":{"form_href":"/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101"}}}'
>>> request._request.body
'{"data":{"name":"_test","parameters":{"parallel":"false","max_workers":"1"},"rollback_disabled":false,"workflow":[{"method":"add","entity_ty
pe":"model","entity":"data/Countries","templates":[{"template":"_test"}],"advanced_find_search_direction":"full_tree"}]},"request_meta":{},"m
eta":{"references":{"form_href":"/api/data/ProvisioningWorkflow/5aeaf9caa9d1cb000887184f/?hierarchy=1c0ffee2c0deab00da595101"}}}'
我已经回答了我自己的问题。我发现了一个将 request.data 设置为 request.body['data'] 的中间件。新版本的DRF在这里有命名空间冲突,因为它也设置了request.data,但这是在Django中间件生效之后发生的。为了解决这个问题,我覆盖了 initialize_request 并在访问数据 属性 后手动设置了 _full_data。不幸的是,更改我的应用程序使用 request.data.
的所有情况是不切实际的