Getting '400 Bad Request' when using multipart/form-data as Content-Type in XHR

28,974

You didn't set the boundary in your request header, as in:

request.setRequestHeader("Content-Type", "multipart/form-data; boundary=l3iPy71otz");

For more information, see RFC 2045:

5 Content-Type Header Field
[…]
Parameters are modifiers of the media subtype, and as such do not fundamentally affect the nature of the content. The set of meaningful parameters depends on the media type and subtype. Most parameters are associated with a single specific subtype. However, a given top-level media type may define parameters which are applicable to any subtype of that type. Parameters may be required by their defining content type or subtype or they may be optional. MIME implementations must ignore any parameters whose names they do not recognize.

For example, the "charset" parameter is applicable to any subtype of "text", while the "boundary" parameter is required for any subtype of the "multipart" media type.

Update: Another problem I found on the net appears when a charset is added to the Content-type in the request header, but not in the message boundaries in the body (this is also true for your test case). It doesn't seem a likely solution, but perhaps it helps.

In your case, explicitly add a charset to both the request header and in the message boundaries:

data.params += "--" + data.uniqid + "; charset=UTF-8" + data.crlf;
…
request.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + data.uniqid + "; charset=UTF-8");

Update 2: After trying this myself locally, I noticed the leading boundary wasn't recognized as such, but interpreted as the last parameter contents (on my more forgiving server). Perhaps that was causing Apache to throw a 400 Bad Request error.

After some trial and error, I noticed that that was caused because the server expected the charset to be in every boundary, even the last one. To prevent confusion, I decided to explicitly set the charset in the request header before the boundary parameter, so that the boundary would be the last parameter in the Content-type request header. After this, everything seemed to work fine.

data.params = "Content-Type: multipart/form-data; boundary=" + data.uniqid;
…
data.params += "--" + data.uniqid + data.crlf;
…
data.params += "--" + data.uniqid + "--";
…
request.setRequestHeader("Content-Type", "multipart/form-data; charset=UTF-8; boundary=" + data.uniqid);
Share:
28,974
Sorin Buturugeanu
Author by

Sorin Buturugeanu

Updated on December 24, 2020

Comments

  • Sorin Buturugeanu
    Sorin Buturugeanu over 3 years

    I have an AJAX request that sends out some data. The data respects the multipart/form-data specification.

    The problem I'm facing is that the browser sets the Content-Type header to text/plain and it should be multipart/form-data.

    I've tried doing this: request.setRequestHeader("Content-Type", "multipart/form-data"); but this gives out an 400 Bad Request error.

    If I do request.setRequestHeader("Content-Typexxxx", "multipart/form-data"); there is no error, the "Content-Typexxxx" header is set but it obviously is no help to me.

    I guess there is a list of valid Content-Type headers one can set and "multipart/form-data" isn't among them, but I cannot find a sollution to my predicament.

    Sample of the data actually being sent:

    Content-Type: multipart/form-data; boundary=l3iPy71otz
    
    --l3iPy71otz
    Content-Disposition: form-data; name="titluPublic"
    
    Variation_1
    --l3iPy71otz
    Content-Disposition: form-data; name="nr_versiune"
    
    
    --l3iPy71otz--
    

    Thanks!

  • Sorin Buturugeanu
    Sorin Buturugeanu over 13 years
    the actual code is here. just press the first action button (not the delete one :) ) and you'll see page I'm working on. If you submit the form (bottom of the page) you'll get the actual 400 error. The javascript file is here - I've commented near the part that generates the bad request error. Thanks a lot!!!!!
  • Marcel Korpel
    Marcel Korpel over 13 years
    @Sorin: Why do you explicitly set the Content-length to 8000? AFAIK, you really shouldn't do that.
  • Sorin Buturugeanu
    Sorin Buturugeanu over 13 years
    @Marcel Korpel: I was just trying out different solutions. I took it out.
  • Sorin Buturugeanu
    Sorin Buturugeanu over 13 years
    @Marcel: I added the charset, but it's still not working. I'm fairly new to javascript and ajax so you can immagine how frustrating this must be ..
  • Sorin Buturugeanu
    Sorin Buturugeanu over 13 years
    @Marcel: just [23/Jan/2011:01:13:53 +0200] "POST /florin2/request.php?action=produsUpdate&id=24556 HTTP/1.1" 404 - "http://clienti.bsorin.ro/florin2/cautare" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"
  • Marcel Korpel
    Marcel Korpel over 13 years
    @Sorin: Now I see a 404, that's probably related to the unfound ErrorDocument; is there also a 400 error present? BTW, you're doing a POST request and you're sending URL parameters; though not technically wrong, there might be some problem with Apache/PHP. Can you try to omit the URL parameters and see if you get any $_POST variables in your server side script?
  • Sorin Buturugeanu
    Sorin Buturugeanu over 13 years
    @Marcel: 400 error: [Sun Jan 23 16:35:05 2011] [error] File does not exist: /home/soinro/!!clienti/400.shtml, referer: http://clienti.bsorin.ro/florin2/cautare. I also left out the URL parameters, but this doesn't change anything
  • Marcel Korpel
    Marcel Korpel over 13 years
    @Sorin: No, this isn't the line regarding the 400 error, but the line regarding the 404 error. BTW, why are you using multipart/form-data anyway, instead of sticking to the default application/x-www-form-urlencoded for form data, where it is much easier to construct the POST data (only don't forget to use encodeURIComponent).
  • Sorin Buturugeanu
    Sorin Buturugeanu over 13 years
    @Marcel: There are no other references to 400 errors. The script will have to support sending all sorts of data, not just text like it currently does.
  • Marcel Korpel
    Marcel Korpel over 13 years
    @Sorin: In that case, can you post your server side code, so I (and other people here) can test this locally myself.
  • Sorin Buturugeanu
    Sorin Buturugeanu over 13 years
    @Marcel: request.php is just echo '$_SERVER[CONTENT_TYPE]='.$_SERVER['CONTENT_TYPE'].'<br /><br />'; echo '<pre>'.file_get_contents('php://input').'</pre>'; debug($_POST); where debug() is just a nicer var_dump();
  • Sorin Buturugeanu
    Sorin Buturugeanu over 13 years
    @Marcel: Thanks for all the patience and support you've put into this. As it seems we couldn't figure out a solution I'll switch to the x-www-form-urlencode solution for now as I have to finish this job. Maybe at some point I'll get back to figuring this out and if I do I'll be sure to post the solution here. Thanks again!
  • Marcel Korpel
    Marcel Korpel over 13 years
    @Sorin: So my last update (#2) didn't solve your problem? Too bad... Anyway, you're welcome.