views:

183

answers:

1

Hi,

How do I manually create nested POST parameters for a http web request? I have a .NET C# client for which I'm creating a HTTP request to a Rails page. Everything is fine so far, however I've noted that the parameters I'm creating for the request (key/value pairs) are expected to be nested. I'm actually also having a hard time trying to work out in a controller before_filter how to do a "puts" on the raw request content to see how a successful request formats it.

RAILS BACKEND EXPECTATION (a successful login file, when I called from browser (not .net))

 action_controller.request.request_parameters: !map:HashWithIndifferentAccess
   commit: Save
   webfile: !map:HashWithIndifferentAccess
     path: winter
     file: &id005 !ruby/object:File
       content_type: image/jpeg
       original_path: Winter.jpg

C# Parameter Creation:

    var form = new NameValueCollection();
    form["path"] = "winter";  ==> THIS DOESN'T WORK BECAUSE I THINK IT MAY HAVE TO BE NESTED WITHIN THE "webfile" HASH

C# Routine:

    public static HttpWebResponse Upload(HttpWebRequest req, UploadFile[] files, NameValueCollection form)
    {
        List<MimePart> mimeParts = new List<MimePart>();

        try
        {
            foreach (string key in form.AllKeys)
            {
                StringMimePart part = new StringMimePart();

                part.Headers["Content-Disposition"] = "form-data; name=\"" + key + "\"";
                part.StringData = form[key];

                mimeParts.Add(part);
            }

            int nameIndex = 0;

            foreach (UploadFile file in files)
            {
                StreamMimePart part = new StreamMimePart();

                if (string.IsNullOrEmpty(file.FieldName))
                    file.FieldName = "file" + nameIndex++;

                part.Headers["Content-Disposition"] = "form-data; name=\"" + file.FieldName + "\"; filename=\"" + file.FileName + "\"";
                part.Headers["Content-Type"] = file.ContentType;

                part.SetStream(file.Data);

                mimeParts.Add(part);
            }

            string boundary = "----------" + DateTime.Now.Ticks.ToString("x");

            req.ContentType = "multipart/form-data; boundary=" + boundary;
            req.Method = "POST";

            long contentLength = 0;

            byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");

            foreach (MimePart part in mimeParts)
            {
                contentLength += part.GenerateHeaderFooterData(boundary);
            }

            req.ContentLength = contentLength + _footer.Length;

            byte[] buffer = new byte[8192];
            byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
            int read;

            using (Stream s = req.GetRequestStream())
            {
                foreach (MimePart part in mimeParts)
                {
                    s.Write(part.Header, 0, part.Header.Length);

                    while ((read = part.Data.Read(buffer, 0, buffer.Length)) > 0)
                        s.Write(buffer, 0, read);

                    part.Data.Dispose();

                    s.Write(afterFile, 0, afterFile.Length);
                }

                s.Write(_footer, 0, _footer.Length);
            }

            return (HttpWebResponse)req.GetResponse();
        }
        catch
        {
            foreach (MimePart part in mimeParts)
                if (part.Data != null)
                    part.Data.Dispose();

            throw;
        }
    }

Thanks

PS. In particular I think what I'm after is: * how does Rails serialize a nested form parameter/hash into an actual HTTP Request body, and/or * a pointer to a specific class in the Rails code base that does this (so I can have a look and then emulate within my .net client)

+2  A: 

You're not exactly clear with what you're trying to do here. So I don't think we can correct the issue for you.

Best I can suggest is that you submit the form via the web using a tool that monitors requests, such as Firebug. It will tell you exactly what's in the HTTP request, and not what Rails interprets. You can use that information to craft the HTTP request you want.

FYI Rails uses pairs of square brackets in keys to denote nesting and arrays in parameters. Empty square brackets indicate lists, while filled square brackets indicates another level of nesting. A hash with indifferent access is a hash that has an implicit to_s call on strings and symbols for all keys used to access the hash.

Example:

When you create the form like this:

var form = new NameValueCollection();
form["user[name]"] = "EmFi";
form["user[phone_number]" = "555-555-1234";
form["user[friend_ids][]" = "7";
form["user[friend_ids][]" = "8";
form["user[address][street_number]" = "75";
form["user[address][street_name]" = "Any St.";
form["user[address][province]" = "Ontario";
form["user[address][country]" = "Candad";

then pass it to the subroutine posted in the question, this is the params hash rails will provide the controller:

params = {
  "user" => {
    "name" => "EmFi,
    "phone_number" => "555-555-1234",
    "friend_ids" => ["7","8"],
    "address" => {
      "street_number" => "75",
      "street_name" => "any St.",
      "province" => "Ontario",
      "country" => "Canada",
    }
  }
}
EmFi
thanks EmFi - so if I'd understood correctly you've highlighted what the nested hashes/parameters look like in Rails in term of before and after, however what I need to work out is how Rails serializes these nested hashes under the bonnet when it puts it into HTTP requests...I will start digging here....If anyone else knows how Rails persists/serializes nested parameters into a HTTP Request I'd love to hear....or perhaps there's a specific class in the Rails source I should look for?
Greg
got it - it was actually as simple as creating the name for the key/value pairs in .net client to look like: "webfile[path]" :) Thanks
Greg