【问题标题】:Parsing multipart/form-data in django-rest-framework在 django-rest-framework 中解析 multipart/form-data
【发布时间】:2015-12-17 17:02:51
【问题描述】:

在使用 django-rest-framework 时,我在解析多部分表单数据时遇到了一些困难。我设置了一个最小视图来回显请求数据:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, FormParser


class FileUpload(APIView):
    parser_classes = (MultiPartParser, FormParser, )

    def post(self, request, format=None, *args, **kwargs):
        return Response({'raw': request.data, 'data': request._request.POST,
                         'files': str(request._request.FILES)})

我期待raw(我承认名字有点糟糕)实际上包含与request._request.POSTrequest._request.FILES 相同的数据。

如果我 POSTContent-Type= application/x-www-form-urlencoded 的视图,这将按预期工作:

$ http -v --form POST http://localhost:8000/upload/api/ course=3 name=name

POST /upload/api/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 18
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: localhost:8000
User-Agent: HTTPie/0.9.2

course=3&name=name

HTTP/1.0 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Date: Thu, 17 Dec 2015 16:52:37 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "data": {
        "course": "3",
        "name": "name"
    },
    "files": "<MultiValueDict: {}>",
    "raw": {
        "course": "3",
        "name": "name"
    }
}

但是,如果我使用 Content-Type=multipart/form-data 发帖,我会得到以下信息:

$ http -v --form POST http://localhost:8000/upload/api/ file@~/Projects/lms/manage.py course=3 name=name
POST /upload/api/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 577
Content-Type: multipart/form-data; boundary=634ec7c7e89a487b89c1c07c0d24744c
Host: localhost:8000
User-Agent: HTTPie/0.9.2

--634ec7c7e89a487b89c1c07c0d24744c
Content-Disposition: form-data; name="course"

3
--634ec7c7e89a487b89c1c07c0d24744c
Content-Disposition: form-data; name="name"

name
--634ec7c7e89a487b89c1c07c0d24744c
Content-Disposition: form-data; name="file"; filename="manage.py"

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

--634ec7c7e89a487b89c1c07c0d24744c--

HTTP/1.0 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Date: Thu, 17 Dec 2015 16:55:44 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "data": {
        "course": "3",
        "name": "name"
    },
    "files": "<MultiValueDict: {'file': [<InMemoryUploadedFile: manage.py ()>]}>",
    "raw": {}
}

我在这里遗漏了什么吗?我在这里使用 HTTPIE 生成请求,但 curl 存在相同的行为,所以我很确定这不是问题。我正在使用 djangorestframework==3.3.0 和 Django==1.8.4

编辑:

似乎PUTing 到 url(具有其他相同的请求)达到了预期的结果:

$ http -v --form PUT http://localhost:8000/upload/api/ file@~/Projects/lms/manage.py course=3 name=name
PUT /upload/api/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 577
Content-Type: multipart/form-data; boundary=98feb59a8abe4bfb95a7321f536ed800
Host: localhost:8000
User-Agent: HTTPie/0.9.2

--98feb59a8abe4bfb95a7321f536ed800
Content-Disposition: form-data; name="course"

3
--98feb59a8abe4bfb95a7321f536ed800
Content-Disposition: form-data; name="name"

name
--98feb59a8abe4bfb95a7321f536ed800
Content-Disposition: form-data; name="file"; filename="manage.py"

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

--98feb59a8abe4bfb95a7321f536ed800--

HTTP/1.0 200 OK
Allow: POST, PUT, OPTIONS
Content-Type: application/json
Date: Thu, 17 Dec 2015 18:10:34 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN

{
    "data": {},
    "files": "<MultiValueDict: {}>",
    "raw": "<QueryDict: {'name': ['name'], 'course': ['3'], 'file': [<InMemoryUploadedFile: manage.py ()>]}>"
 }

所以我可以使用PUT。然而,这并不理想,因为客户端不控制文件的名称或文件在服务器上的位置。从这个意义上说,POST 更合适。无论如何,我不明白为什么PUT 有效而POST 无效。

【问题讨论】:

  • 您是否尝试过将内容类型完全排除在帖子之外?根据this answer:如果没有将内容类型传递给 post 方法,它会自动设置 multipart/form-data

标签: python django django-rest-framework


【解决方案1】:

这是您正在使用的版本的known issue。将 django rest 框架升级到最新版本即可解决问题。但是,您可以将请求作为解决方法。

【讨论】:

  • 确实,这也是我最终想通的。感谢您在 Github 上引用这个问题,这意味着这个问题终于有了一个权威的答案!
  • @maxf130 我也有同样的问题,你能帮忙吗??stackoverflow.com/questions/67976100/…你可以检查更新的代码
猜你喜欢
  • 2013-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-04
  • 2015-11-19
  • 2020-07-24
  • 1970-01-01
相关资源
最近更新 更多