POST Payloads


Thu Nov 15 17:04:00 -0800 2007

I often find myself needing to make some sort of an HTTP request (ajax or rest) in which the data being passed by the call is a single uniform block of data. For those situations I’ve become fond of just passing the data as the payload of the POST, and bypassing the CGI protocol altogether.

For example, if you dig around in the javascript for the Heroku console, you’ll see that it sends the command with this code:

new Ajax.Request('/console/command',
  { method: 'post', postBody: command, ... } })

The fun part here is postBody: command. This just sends the command (which is presumably some Ruby code) as the raw POST data. Then in the controller, we can pick it up like this:

class ConsoleController < ApplicationController
  def command
    cmd = request.raw_post

Clean, easy, and simple. I’ve found that CGI encoders/decoder can to get confused if you have too many layers of nested quotes and other characters, which is common when you’re passing chunks of code around. (Part of this may be caused by different implementations of the escaping - in an ajax call, the encoding is done by Javascript, and the decoding by Ruby.) Passing it straight through as a block avoids this problem entirely.

Plus, you can test it at the command line really easily:

curl http://localhost:3000/console/command -d "Widget.count"

I get the impression that this method may violate some W3C standard, or least some unwritten netiquette law - but so far I haven’t found anyone expressing an opinion one way or the other. If you know of or can think of a problem with this method, please leave a comment.

Your first thought might be - what if I need to pass some additional data, such as an id? Well, you can put the CGI parameters into the URL, and leave the POST payload as your uniform data block. To take a completely made-up example:

curl "http://localhost:3000/posts/create?author_id=1&publish=true" -d "Here's the payload."

One other thing you might want to do when transferring binary data is set the content-type of the POST, which is not only more correct, and also keeps Rails from logging the payload (not pretty with a large binary file). Try this:

curl http://localhost:3000/image/upload --data-binary @public/images/rails.png -H 'Content-type: image/png'