Skip to content

Instantly share code, notes, and snippets.

@paulosman
Created February 10, 2012 22:01
Show Gist options
  • Save paulosman/1793321 to your computer and use it in GitHub Desktop.
Save paulosman/1793321 to your computer and use it in GitHub Desktop.
Problem posting multipart form data to Rails app.
# Rails 3.0.3 Controller. Just return the parameters as a string.
class HomeController < ApplicationController
protect_from_forgery :except => :upload
def upload
render :text => params.to_s
end
end
#!/usr/bin/env python
import requests
files = dict(avatar=open('avatar.jpg', 'rb'))
data = dict(name='A picture')
r = requests.post('http://localhost:4567/upload', data=data, files=files)
print r.text
# This one monkey patches encode_multipart_formdata to omit the Content-Type header from non-binary parts.
# See below for output from test.py and this one.
import requests
import codecs
try:
from mimetools import choose_boundary
except ImportError:
from requests.packages.urllib3.packages.mimetools_choose_boundary import choose_boundary
from io import BytesIO
from requests.packages.urllib3.packages import six
from requests.packages.urllib3.packages.six import b
from requests.packages.urllib3.filepost import get_content_type
writer = codecs.lookup('utf-8')[3]
def encode_multipart_formdata(fields, boundary=None):
"""Stop rails from doing stupid things by omitting the Content-Type header on non-binary parts."""
body = BytesIO()
if boundary is None:
boundary = choose_boundary()
for fieldname, value in six.iteritems(fields):
body.write(b('--%s\r\n' % (boundary)))
if isinstance(value, tuple):
filename, data = value
writer(body).write('Content-Disposition: form-data; name="%s"; '
'filename="%s"\r\n' % (fieldname, filename))
body.write(b('Content-Type: %s\r\n\r\n' %
(get_content_type(filename))))
else:
data = value
writer(body).write(
'Content-Disposition: form-data; name="%s"\r\n\r\n' % (
fieldname))
if isinstance(data, int):
data = str(data) # Backwards compatibility
if isinstance(data, six.text_type):
writer(body).write(data)
else:
body.write(data)
body.write(b'\r\n')
body.write(b('--%s--\r\n' % (boundary)))
content_type = b('multipart/form-data; boundary=%s' % boundary)
return body.getvalue(), content_type
requests.models.encode_multipart_formdata = encode_multipart_formdata
files = dict(avatar=open('avatar.jpg', 'rb'))
data = dict(name='A picture')
r = requests.post('http://localhost:4567/upload', data=data, files=files)
print r.text
# running it with a recent version of requests / urllib3. Notice that Rails is parsing the non-binary part incorrectly
$ python test.py
{"name"=>#<ActionDispatch::Http::UploadedFile:0x007ff6b30d2670 @original_filename=nil, @content_type="text/plain", @headers="Content-Disposition: form-data; name=\"name\"\r\nContent-Type: text/plain\r\n", @tempfile=#<File:/var/folders/nb/httvyh1d7616jz77m4k1lxp40000gn/T/RackMultipart20120210-20637-6xlzo8>>, "avatar"=>#<ActionDispatch::Http::UploadedFile:0x007ff6b30d2530 @original_filename="avatar.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"avatar\"; filename=\"avatar.jpg\"\r\nContent-Type: image/jpeg\r\n", @tempfile=#<File:/var/folders/nb/httvyh1d7616jz77m4k1lxp40000gn/T/RackMultipart20120210-20637-4vziee>>, "action"=>"upload", "controller"=>"home"}
# running another version with urllib3.filepost.encode_multipart_formdata monkey patched to *not* set a Content-Type header for non-binary parts of the form data (basically just removed https://github.com/shazow/urllib3/blob/master/urllib3/filepost.py#L58 and added the terminal '\r\n' to L57).
$ python test2.py
{"name"=>"A picture", "avatar"=>#<ActionDispatch::Http::UploadedFile:0x007ff6b54884c0 @original_filename="avatar.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"avatar\"; filename=\"avatar.jpg\"\r\nContent-Type: image/jpeg\r\n", @tempfile=#<File:/var/folders/nb/httvyh1d7616jz77m4k1lxp40000gn/T/RackMultipart20120210-20637-rh1v7e>>, "action"=>"upload", "controller"=>"home"}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment