Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MultiPartParserError :- Invalid boundary

Im trying to send some data and file using Python requests module to my django rest application but get the below error.

    raise MultiPartParserError('Invalid boundary in multipart: %s' % boundary)
MultiPartParserError: Invalid boundary in multipart: None

Code:-

import requests
payload={'admins':[
                    {'first_name':'john'
                    ,'last_name':'white'
                    ,'job_title':'CEO'
                    ,'email':'[email protected]'
                    },
                    {'first_name':'lisa'
                    ,'last_name':'markel'
                    ,'job_title':'CEO'
                    ,'email':'[email protected]'
                    }
                    ],
        'company-detail':{'description':'We are a renowned engineering company'
                    ,'size':'1-10'
                    ,'industry':'Engineering'
                    ,'url':'http://try.com'
                    ,'logo':''
                    ,'addr1':'1280 wick ter'
                    ,'addr2':'1600'
                    ,'city':'rkville'
                    ,'state':'md'
                    ,'zip_cd':'12000'
                    ,'phone_number_1':'408-393-254'
                    ,'phone_number_2':'408-393-221'
                    ,'company_name':'GOOGLE'}
        }
files = {'upload_file':open('./test.py','rb')}
import json
headers = {'content-type' : 'application/json'}      
headers = {'content-type' : 'multipart/form-data'}      

#r = requests.post('http://127.0.0.1:8080/api/create-company-profile/',data=json.dumps(payload),headers=headers,files=files)
r = requests.post('http://127.0.0.1:8080/api/create-company-profile/',data=payload,headers=headers,files=files)
print r.status_code
print r.text

Django code:-

class CompanyCreateApiView(CreateAPIView):
    parser_classes = (MultiPartParser, FormParser,)
    def post(self, request, *args, **kwargs):
        print 'request ==', request.data
like image 340
user1050619 Avatar asked Dec 17 '15 03:12

user1050619


2 Answers

Okay, I forgot about your headers. According to the spec:

Content-Type   = "Content-Type" ":" media-type

MIME provides for a number of "multipart" types -- encapsulations of one or more entities within a single message-body. All multipart types share a common syntax, ... and MUST include a boundary parameter as part of the media type value.

Here is what a request containing multipart/form-data looks like:

POST /myapp/company/ HTTP/1.1
Host: localhost:8000
Content-Length: 265
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.9.0
Connection: keep-alive
Content-Type: multipart/form-data; boundary=63c5979328c44e2c869349443a94200e   

--63c5979328c44e2c869349443a94200e
Content-Disposition: form-data; name="hello"

world
--63c5979328c44e2c869349443a94200e
Content-Disposition: form-data; name="mydata"; filename="data.txt"

line 1
line 2
line 3
line 4

--63c5979328c44e2c869349443a94200e--

See how the sections of data are separated by the boundary:

--63c5979328c44e2c869349443a94200e--

The idea is to use something for a boundary that is unlikely to appear in the data. Note that the boundary was included in the Content-Type header of the request.

That request was produced by this code:

import requests

myfile = {'mydata': open('data.txt','rb')}

r = requests.post(url, 
        #headers = myheaders
        data = {'hello': 'world'}, 
        files = myfile
) 

It looks like you were paying careful attention to the following note in the django-rest-framework docs:

Note: When developing client applications always remember to make sure you're setting the Content-Type header when sending data in an HTTP request.

If you don't set the content type, most clients will default to using 'application/x-www-form-urlencoded', which may not be what you wanted.

But when you are using requests, if you specify the Content-Type header yourself, then requests assumes that you know what you're doing, and it doesn't overwrite your Content-Type header with the Content-Type header it would have provided.

You didn't provide the boundary in your Content-Type header--as required. How could you? You didn't assemble the body of the request and create a boundary to separate the various pieces of data, so you couldn't possibly know what the boundary is.

When the django-rest-framework note says that you should include a Content-Type header in your request, what that really means is:

You or any programs you use to create the request need to include a Content-Type header.

So @AChampion was exactly right in the comments: let requests provide the Content-Type header, after all the requests docs advertise:

Requests takes all of the work out of Python HTTP/1.1

requests works like this: if you provide a files keyword arg, then requests uses a Content-Type header of multipart/form-data and also specifies a boundary in the header; then requests assembles the body of the request using the boundary. If you provide a data keyword argument then requests uses a Content-Type of application/x-www-form-urlencoded, which just assembles all the keys and values in the dictionary into this format:

x=10&y=20

No boundary required.

And, if you provide both a files keyword arg and a data keyword arg, then requests uses a Content-Type of multipart/form-data.

like image 73
7stud Avatar answered Oct 12 '22 13:10

7stud


When uploading file with parameters:

  1. Don't overwrite the headers

  2. Put other parameters together with upload_file in files dict.

    input ={"md5":"xxxx","key":"xxxxx","sn":"xxxx"}
    files = {"pram1":"abc",
             "pram2":json.dumps(input),
             "upload_file": open('/home/gliu/abc', 'rb')}    
    res = requests.post(url, files=files)
    
like image 37
guichao.liu Avatar answered Oct 12 '22 13:10

guichao.liu