Reputation: 339
I'm currently trying to get a POST request using multipart/form-data running to the Django REST framework. I've successfully run through some test requests via the interactive API screens, which work fine. I've then tried to convert these over to using a non-Session based auth strategy, and I've consistently got errors. The requests I've sent are of the form:
POST /api/logs/ HTTP/1.1
Host: host:8080
Connection: keep-alive
Content-Length: 258
Accept: application/json
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryTOhRsMbL8ak9EMQB
Authorization: Token -token-
------WebKitFormBoundaryx6ThtBDZxZNUCkKl
Content-Disposition: form-data; name="SubmittedAt"
2014-01-23T10:39:00
------WebKitFormBoundaryx6ThtBDZxZNUCkKl
Content-Disposition: form-data; name="Device"
CheeseDevice
------WebKitFormBoundaryx6ThtBDZxZNUCkKl--
Sadly, the result has been (for all the requests I've run):
{"Device": ["This field is required."], "SubmittedAt": ["This field is required."], "LogFile": ["This field is required."]}
Interestingly, I've been able to send chunks of JSON through to the endpoint, and they're accepted as expected, eg:
POST /api/logs/ HTTP/1.1
Content-Type: application/json
Host: host:8080
Connection: keep-alive
Content-Length: 35
Accept: application/json
Authorization: Token -token-
{
"Device": "CheeseDevice"
}
Returns:
{"SubmittedAt": ["This field is required."], "LogFile": ["This field is required."]}
As expected - it actually accepts the Device argument and only raises errors on the missing items. I'd switch to using JSON, but sadly cannot upload files with it...
Thanks in advance for any help!
Edit:
Further investigation (ie: writing a view method that returns the request data shows that request.DATA isn't getting populated, for some reason. Method I'm using to debug follows:
def test_create(self, request, pk=None):
return Response(request.DATA)
Edit 2:
Even further investigation (and dropping code chunks into the framework for debugging) indicates that the requests are getting caught up in _perform_form_overloading and never hitting the MultiPartParser. Not sure why this is occurring but I'll try and trace it further.
Upvotes: 2
Views: 1477
Reputation: 339
After delving down every level I could find...
Looks like the problem stems from the line endings - ie: the libs and request senders I've been using send the content through with "\n" (LF) endings, while the HTTP spec requires "\r\n" endings (CR,LF)
This hinges on the following code in the Django core, within http/multipartparser.py - in parse_boundary_stream:
header_end = chunk.find(b'\r\n\r\n')
For dev purposes (and because it's going to be way easier to patch at the Django end than in the clients...) I've switched the above line to:
header_end = chunk.replace("\r\n","\n").find(b'\n\n')
This updated code follows the recommendations in Section 19.3 of the HTTP/1.1 spec regarding Tolerant Applications and accepting LF instead of just CRLF - I'll try and get around to seeing if this is suitable for inclusion in the Django core.
Edit:
For reference, the patch is up on GitHub: https://github.com/tr00st/django/commit/9cf6075c113dd27e3743626ab0e18c6616488bd9
Upvotes: 1
Reputation: 33901
This could be due to malformed multipart post data.
Also possible that you don't have MultiPartParser
installed, but I don't think that'll be it as you'd normally expect to see a 415 Unsupported Media Type
response in that case.
Upvotes: 0