Prototype Patch

Prototype is JavaScript library, of a similar nature to Neuromancer, that I've been using of late.  It's got some really cool features that Neuromancer doesn't have (particularly when extended by Script.aculo.us), but it's not a complete replacement.  For example, no remoting support.  While working with it, I noticed that all POSTs were sent as urlencoded query strings, which works until you've got an unescaped ampersand lurking.  So I patched it to support multipart POSTs.

To use the new feature, do everything you've been doing to this point, just change your postBody parameter to be a generic object with name/value pairs representing the form fields you want to submit, rather than a query string.  The patch (for src/ajax.js) has been submitted to the Rails team under ticket #4613, and is also available below.  Note that the patch is for the source, not the assembled prototype.js, so you'll either need to get the source, patch it, and the build it (which requires Ruby), or apply the patch to prototype.js manually (which is straightforward, just have to find the right spot in the file).

Index: /home/barneyb/public_html/neuro_preso_2006_04/static/prototype/src/ajax.js
===================================================================
— /home/barneyb/public_html/neuro_preso_2006_04/static/prototype/src/ajax.js (revision 4182)
+++ /home/barneyb/public_html/neuro_preso_2006_04/static/prototype/src/ajax.js (working copy)
@@ -50,6 +50,8 @@
});

Ajax.Base = function() {};
+Ajax.Base.urlencodedContentType = 'application/x-www-form-urlencoded';
+Ajax.Base.multipartContentType = 'multipart/form-data';
Ajax.Base.prototype = {
setOptions: function(options) {
this.options = {
@@ -55,7 +57,7 @@
this.options = {
method: 'post',
asynchronous: true,
- contentType: 'application/x-www-form-urlencoded',
+ contentType: Ajax.Base.urlencodedContentType,
parameters: "
}
Object.extend(this.options, options || {});
@@ -93,6 +95,26 @@
this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;

Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ var body = this.options.postBody ? this.options.postBody : parameters;
+
+ if (this.options.method == 'post' && typeof body != 'string') {
+ // we're POSTing and body is an object, so we need to do multipart encoding
+ var fields = body;
+ body = "";
+ var boundary = "proto" + Math.random();
+
+ for (var i in fields) {
+ body += "–" + boundary + "\nContent-Disposition: form-data;name=\"" + i + "\"\n";
+ body += "\n";
+ body += fields[i] + "\n";
+ body += "\n";
+ }
+ body += "–" + boundary + "–";
+
+ // we also have to update the Content-Type header
+ this.options.contentType = Ajax.Base.multipartContentType + "; boundary=" + boundary;
+ }

this.transport.open(this.options.method, this.url,
this.options.asynchronous);
@@ -103,8 +125,6 @@
}

this.setRequestHeaders();
-
- var body = this.options.postBody ? this.options.postBody : parameters;
this.transport.send(this.options.method == 'post' ? body : null);

} catch (e) {

3 responses to “Prototype Patch”

  1. arung

    How to fill your postBody???
    can use postBody to upload file????

    Is this right?

    new Ajax.Request(url, {
    asynchronous : true,
    method : "post",
    postBody : Form.getInputs(formName),
    onSuccess : function(request)
    { alert("success");},
    onFialure: function(request)
    { alert("fail");}
    });

  2. arung

    How to fill your postBody???
    can use postBody to upload file????

    Is this right?

    new Ajax.Request(url, {
    asynchronous : true,
    method : "post",
    postBody : Form.getInputs(formName),
    onSuccess : function(request)
    { alert("success");},
    onFialure: function(request)
    { alert("fail");}
    });

  3. Barney

    arung,

    No, you cannot use postBody to upload a file, at least not directly. The default postBody behaviour takes a query-string, which Form.serialize will get you. My patch is for if you need to send a multi-part body for some reason. It can also be used to take an arbitrary object and post it's properties without having to serialize, but that's kind of a fringe benefit.

    You could probably use it to upload a file, but you'd have to get the file content into a JS variable, which would be difficult at best. Well, not difficult, but it would entail a lot of limitations (like browsers have to allow native access to JS code, which no one in the right mind would ever do).