views:

27

answers:

1

I am uploading files using SWFUpload. To get the data I need passed along with the file, I am adding post params to swfupload. The problem is that the custom model binder I have written is not called when the controller method is called by swfupload. Some of the properties of my model are set, but not the properties controlled by my custom binder, which means that it is using the default model binder.

When I use the following JQuery method, everything works as intended. So the problem is not that my custom model binder is set up incorrectly.

$.post('AsyncUpload', $('#uploadform').serialize());

The following is the code I am using to set up the post params and start the upload process. Note that I am aware of the setPostParams function, but for some reason it was not working for me, hence the work around. Also note that I verified that the post params are being set correctly in the Request variable on the server side.

$('#uploadbutton').click(function (e) {
    e.preventDefault();
    var data = $('#uploadform').serialize();
    var res = $.parseQuery(data);

    for (key in res) {
            swfu.addPostParam(key, res[key]);
    }

    swfu.startUpload();
});

The pertinent swfupload variables are set as follows.

upload_url: '<%= Url.Action("AsyncUpload")%>',  //This becomes /Home/AsyncUpload
use_query_string: true,  // I have also tried setting this to false to no effect

This is my controller method, such as it is.

[HttpPost]
public bool AsyncUpload(EditModel model)
{
    if (Request.Files.Count <= 0)
        return false;

    return true;
}

Any ideas why the custom model binder is not being invoked?

A: 

It turns out that the cause of the problem was character encoding. My custom model binder is built to handle Dictionary properties.

My model looks a bit like this:

public class EditModel
{
    public string Name { get; set; }
    public Dictionary<int, string> Type { get; set; }
}

As stated in my question, the controller method looks like the following:

[HttpPost]
public bool AsyncUpload(EditModel model)
{
    if (Request.Files.Count <= 0)
        return false;

    return true;
}

On the client side I have textbox controls and checkboxes. The textboxes are mapped to the string properties. The checkboxes will are mapped to the dictionary properties and would have a name like "Type[0]", "Type[7]", etc. that I parse with my custom model binder to extract the key from the brackets.

Now, everything works as expected when calling the jQuery function

$.get('AsyncUpload', $('#uploadform').serialize());

Because the query string sent to the controller is

http://127.0.0.1/Home/AsyncUpload?Name=John&amp;Type%5B0%5D

Type%5B0%5D is decoded into Type[0]. MVC recognizes that as a dictionary and we're off to the races.

Passing an already encoded string to the SWFUpload addPostParam function was the issue. It was taking the value I was setting, which was 'Type%5B0%5D' and encoding the '%' to %25. So the query string from SWFUpload was:

http://127.0.0.1/Home/AsyncUpload?Name=John&amp;Type%255B0%255D

Type was then decoded as Type%5B0%5D which is unrecognized as a dictionary by MVC. Name would be set correctly, because the default model binder knows how to deal with a simple string property.

To fix the issue, I made the following changes to my javascript. It will replace the bracket encoding with actual brackets. They will then be encoded back on the call to the server and all will be well with the universe.

for (key in res) {
    var value = res[key];
    var fixedKey = key;
    fixedKey = fixedKey.replace('%5B', '[');
    fixedKey = fixedKey.replace('%5D', ']');
    swfu.addPostParam(fixedKey, value);
}

Many thanks to Matthew Abbott who commented on my post, which caused me to have another look at the names being passed to the server. You saved my sanity.

Jaaromy Zierse