Using WWW to simply post a slab of text .. ala JSON

Normally I use WWW and AddField to simply create forms to post, essentially like

POST /page/page

name="steve"
height="181"

however, if you just want to do this for typical modern web services:

POST /page/page

I will take care of
all this myself
just let me pass you the text, pls Unity

there’s actually NO WAY to do that, right???

An intelligent colleague suggested this:

	byte[] dd = Encoding.ASCII.GetBytes(stuff.ToCharArray());
	WebRequest request = new WebRequest();
	request.www = new WWW(happyURL, dd);

but I have Fear. Does anyone have any suggestions on this? Thank you!

1 Answer

1

You don’t have to use key-value pairs as post data. Post data can be any data which is simply appended on the request headers. On the server side it’s up to you to parse the incoming data properly. PHP for example will parse key-value pairs automatically into an associative array.

An HTTP request is actually very simple. It may consists of 3 major parts:

  • The actual request which just contains the method, the path of the requested resource and the used protocol
  • A list of optional header fields
  • Optional post-data

The HTTP header (which consists of the first request line and all header fields) is finished by an empty line. Everything that follows the second CR / LF pair is post data.

The general layout:

   GET /path/to/resource HTTP/1.1
   Header-Field-Name: Value
   Header-Field-Name: Value
   
   Post data .......
   All the way until the transfer is aborted

Unity’s WWW class allows you to define almost everything “manually”.

Let’s take a look at the actual WWW constructor, all others are just using this one:

    new WWW(URL, postData, requestHeaders);

Those are:

  • URL = a complete URL. The domain part is used to determine the IP to which server the request should be sent. The path information goes into the first request line
  • postData = arbitrary data which is simply appended at the end. The WWW class uses this to determine whether to use “GET” or “POST”. If the post data is “null” it will perform a GET if it’s set to an byte array it will send a POST request.
  • requestHeaders = a Hashtable which contains the desired HTTP request headers.

Here are the two limitations of the WWW class:

  • You can’t append any post data on a GET request. It actually doesn’t make much sense since all HTTP servers will ignore it, but technically it’s a limitation.
  • You can only add each HTTP header once. This is actually against the HTTP standard. This limitation comes from the fact that Unity uses a Hashtable for storing the headers. A hashtable can’t have two or more identical keys.

The WWWForm class is just a helper class to create the post data and the request headers for a “Web form” but it’s only useful when you want to send url-encoded data. The headers generated by WWWForm are, well, very simple:

public Hashtable headers
{
    get
    {
        Hashtable hashtable = new Hashtable();
        if (this.containsFiles)
        {
            hashtable["Content-Type"] = "multipart/form-data; boundary=\"" + Encoding.UTF8.GetString(this.boundary) + "\"";
        }
        else
        {
            hashtable["Content-Type"] = "application/x-www-form-urlencoded";
        }
        return hashtable;
    }
}

This is all. The WWWForm just selects the correct Content-Type depending on if you added binary form data or not. But again you don’t have to use WWWForm and you don’t have to use url-encoded data.

So here’s an example how to send a fully customized POST request:

//C#
string ourPostData = "{\"someJSON\":42}";

Hashtable headers = new Hashtable();
headers.Add("Content-Type", "application/json");
headers.Add("Cookie", "Our session cookie");

byte[] pData = Encoding.ASCII.GetBytes(ourPostData.ToCharArray());

WWW www = new WWW("http://our.server.domain.name/path/to/our/resource/file", pData, headers);

This would produce a request like this:

    POST /path/to/our/resource/file HTTP/1.1
    Content-Type: application/json
    Cookie: Our session cookie
    
    {"someJSON":42}

I'm not sure if Unity will add an "Content-Length" header automatically when you add post data, but usually a Content-Length header must be included when you post and additional data since the server doesn't know how much data will follow. It usually just waits until the connection is closed, but it's better to tell the server how much data he has to expect.

Btw to read the posted data in PHP see this <a href="http://stackoverflow.com/questions/3173547/whats-the-difference-between-post-and-raw-post-in-php-at-all">SO question</a>. It's actually quite shocking that ~90% of the PHP users think post data always have to be url encoded data. Unfortunately there's very little information on raw post data.

Thanks for all the upvotes, but don't forget the question ;)

Do you want a GET or POST request? Because Unity determines which one it should use on the fact if there is post data available or not. If there is no post data Unity performs a GET request. Otherwise a POST. If the server doesn't expect any post data but you need to do a POST it doesn't hurt when you append one byte garbage post data ;)

One problem is when the server desires mulitpart/form-data for an image...I think it may be necessary to use a lame old AddBinaryData with a WWWForm.