16

Couchdb only parse application/x-www-form-urlencoded. Is there a FormData() attribute that set the enctype?

xhr.open('put',document.myForm.action,false)
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
xhr.send(new FormData(document.myForm))
Gert Cuykens
  • 6,515
  • 11
  • 47
  • 82
  • 1
    Wouldn't you just set the `Content-Type` header in the AJAX request to `application/x-www-form-urlencoded` ? If you use jQuery, this is the default Content-Type header for $.ajax(). If you use the regular XMLHttpRequest, it's `xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')`. – ampersand Sep 25 '11 at 00:49
  • 2
    In chrome it still sends multipart/form-data anyway. – Gert Cuykens Sep 25 '11 at 01:32
  • What framework, if any, are you using for the AJAX request? – ampersand Sep 25 '11 at 01:35
  • none just doing xhr.open('put',document.user.action,false) xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded') xhr.send(new FormData(document.user)) – Gert Cuykens Sep 25 '11 at 01:38
  • I just tried this in Chrome 14 with a basic form, and it works. Post your form HTML for us to see; it's a far shot, but perhaps there is some issue with it. – ampersand Sep 25 '11 at 02:20
  • http://gert.iriscouch.com/www/user/user.htm if you check the xhr header in chrome inspector you wil see multipart/form-data – Gert Cuykens Sep 25 '11 at 03:07
  • I see application/x-www-form-urlencoded in the Content-Type request header. The response from the server is a 409 Conflict with the following body: `{"error":"conflict","reason":"Document update conflict."}`. I'm not sure what could be happening on your end, but it seems to be working as expected – ampersand Sep 25 '11 at 03:19
  • the data send should not be ------WebKitFormBoundaryZRNvtqo5x6N8YfOn but name1=value1&name2=value2 – Gert Cuykens Sep 25 '11 at 03:32
  • ah, that's an entirely different issue, and one that I cannot answer. Perhaps a new question is in order? In the meantime, maybe you should try using jQuery's $.ajax along with .serialize (http://api.jquery.com/serialize/) to send form data? Perhaps, Chrome's/Safari's FormData() is not ready for prime-time...i don't know. – ampersand Sep 25 '11 at 04:05
  • formURI=function(v){ var t=v.getElementsByTagName('input') var s='' for(i in t)if(t[i].type=='text')s+=encodeURIComponent(t[i].name)+'='+encodeURIComponent(t[i].value)+'&' return s.slice(0,-1) } i use this function for now. – Gert Cuykens Sep 25 '11 at 04:39
  • 1
    ok, I would also recommend that you use jquery.couch.js (found at http://gert.iriscouch.com/_utils/script/jquery.couch.js) to interact w/ your couchdb. The documentation is at http://daleharvey.github.com/jquery.couch.js-docs/symbols/%24.couch.html – ampersand Sep 25 '11 at 05:05

4 Answers4

31

FormData will always be sent as multipart/form-data.

If you want to send FormData as x-www-form-urlencoded, encode the content items:

function urlencodeFormData(fd){
    var s = '';
    function encode(s){ return encodeURIComponent(s).replace(/%20/g,'+'); }
    for(var pair of fd.entries()){
        if(typeof pair[1]=='string'){
            s += (s?'&':'') + encode(pair[0])+'='+encode(pair[1]);
        }
    }
    return s;
}

var form = document.myForm;
xhr.open('POST', form.action, false);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
xhr.send(urlencodeFormData(new FormData(form)));

you can also use URLSearchParams like this:

function urlencodeFormData(fd){
    var params = new URLSearchParams();
    for(var pair of fd.entries()){
        typeof pair[1]=='string' && params.append(pair[0], pair[1]);
    }
    return params.toString();
}

For old browsers which doesn't support URLSearchParams API, you can use one of polyfills:

cuixiping
  • 21,500
  • 6
  • 76
  • 93
  • 1
    MDN says it is not supported by IE and Safari. Also the support for this requires pretty high versions of the other browsers: Chrome 49, Opera 36 and Firefox 29 (for `entries()` Firefox 44 is required): https://developer.mozilla.org/en/docs/Web/API/URLSearchParams – StanE Aug 14 '16 at 08:28
  • There are polyfills for old browsers which doesn't support URLSearchParams API – cuixiping Aug 14 '16 at 11:54
  • I know. This was not a negative criticism from me. I just wanted to point out, that this technique is very handy, but also quite new. :-) – StanE Aug 14 '16 at 15:13
  • I'm glad you seem to have looked at my answer. ;-) – StanE Aug 14 '16 at 15:21
  • Yes I looked your answer. but your answer has some bugs. you cannot handle form elements so easily. there are some different cases (disabled, checked, selected). – cuixiping Aug 14 '16 at 18:21
  • urlencodeFormData fails to correctly format forms containing File uploads. – Steve Owens Dec 16 '17 at 21:27
  • 1
    @SteveOwens You must use multipart/form-data if you want upload file. – cuixiping Dec 18 '17 at 03:32
  • I spent an hour looking for why form data can't be sent as `application/x-www-form-urlencoded` and didn't know that it's always sent as `multipart/form-data`. Thanks a lot! – Anh Tran Jan 04 '21 at 08:50
12

Here's a simpler way to do it that doesn't rely on writing your own conversions:

 const form = document.getElementById('my_form')
 fetch(form.action,
       { headers: {'Content-Type': 'application/x-www-form-urlencoded'},
         body: new URLSearchParams(new FormData(form)) })

It uses the fact that the URLSearchParams constructor can accept a FormData object (anything that will iterate pairs of values, I think) and that fetch knows to convert URLSearchParams into a string. You do have to set the header yourself, though.

cmc
  • 796
  • 7
  • 17
8

No, the XHR2 "send" method is specified to always send FormData objects as multipart/form-data.

As ampersand suggests, one option would be to use the jquery.couch.js plugin that's built into every CouchDB instance within Futon.

If you like a bit more generic HTTP interface, Fermata also has support for URL encoded requests:

fermata.json(document.myForm.action).put({'Content-Type':"application/x-www-form-urlencoded"}, {...form fields...});

Another option would be to send JSON to your update function (which I'm assuming is the 'action' URL of your form) instead.

Of course, the trick with any of these is that you'll have to extract the form fields yourself, since there's no easy DOM-level equivalent of new FormData(document.myForm) that returns an Object instead AFAIK.

natevw
  • 15,097
  • 8
  • 60
  • 83
0

Some time ago I wrote the following function. It collects form values and encodes them url encoded, so they can be sent with content type application/x-www-form-urlencoded:

function getURLencodedForm(form)
{
  var urlEncode = function(data, rfc3986)
  {
    if (typeof rfc3986 === 'undefined') {
      rfc3986 = true;
    }

    // Encode value
    data = encodeURIComponent(data);
    data = data.replace(/%20/g, '+');

    // RFC 3986 compatibility
    if (rfc3986)
    {
      data = data.replace(/[!'()*]/g, function(c) {
        return '%' + c.charCodeAt(0).toString(16);
      });
    }

    return data;
  };

  if (typeof form === 'string') {
    form = document.getElementById(form);
  }

  var url = [];
  for (var i=0; i < form.elements.length; ++i)
  {
    if (form.elements[i].name != '')
    {
      url.push(urlEncode(form.elements[i].name) + '=' + urlEncode(form.elements[i].value));
    }
  }

  return url.join('&');
}

// Example (you can test & execute this here on this page on stackoverflow)
var url = getURLencodedForm('post-form');
alert(url);
StanE
  • 2,475
  • 25
  • 37