0

I used to have an action processing some complex JSON data:

[HttpPost, ValidateAntiForgeryToken]
public ActionResult GetAdditionals(string pageIdentifier,
    IEnumerable<HtmlPlaceHolder> htmlPlaceHolders,
    IEnumerable<DataSource> dataSourceRequests)

that was called from client side like this:

var requestData = {
    pageIdentifier: 'test',
    htmlPlaceHolders: getHtmlPlaceHolders(),
    dataSourceRequests: getDataSourceRequests(),
    __RequestVerificationToken: token
};
$.post(url, $.toDictionary(requestData), resultHandler, "json");

Where htmlPlaceHolders and dataSourceRequests would contain arrays of objects matching the classes shown in the signature of the action method.

$.toDictionary() is used to get the complex object to the server in a proper manner.

That was all working fine. But now I want to do some processing of the same post data in an ActionFilter. Therefore, in the OnActionExecuting methode, I want to parse the data in the NameValueCollection contained in filterContext.HttpContext.Request.Form where the names of the name/value pairs look like:

pageIdentifier
htmlPlaceHolders[0].ControlId
htmlPlaceHolders[0].FunctionName
htmlPlaceHolders[0].Parameter
...
...
dataSourceRequests[0].Id
dataSourceRequests[0].Src

Now I need to know how I can reconstruct the IEnumerable<HtmlPlaceHolder> and IEnumerable<DataSource> from this NameValueCollection like the default model binder did for my action. So far, I cannot figure out a nice way to accomplish this.

1 Answer 1

1

For this kind of scenarios it is better to use model binder instead of action filter. Consider this as an simple example:

public class MyComplexBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // check type of the object you are going to bind with 
        // bindingContext.ModelType
        // if you could generate this kind of object use
        // controllerContext.HttpContext.Request.Form
        // or anything else and return generated object
        return GenerateMyComplexObject(controllerContext); 
    }
}

Then you could use this binder like this:

public string MyAction([ModelBinder(typeof(MyComplexBinder))]MyComplexType complexParameter)
{
}

If you think this looks ugly write your custom model binder attribute:

public class ComplexAttribute : CustomModelBinderAttribute
{
    public override IModelBinder GetBinder()
    {
        return new MyComplexBinder();
    }
}

Then you could write:

public string MyAction([Complex]MyComplexType complexParameter)
{
}

You could also register your model binder as a global binder in Global.asax.cs:

protected void Application_Start()
{
    // some codes here

    ModelBinders.Binders.Add(typeof(MyComplexType),new MyComplexBinder());
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your elaborate reply Sam, but the purpose of moving the functionality from an action to an actionfilter was to remove the action. The whole context: GetAdditionals was called after almost every ajax post to get additional info from the server that wasn't provided in the original answer from the server. A very silly solution. Now the model changes plus the extra stuff from GetAdditionals are sent in the same ajax call. I standardized the whole process, but I don't want the signature of every action cluttered with the parameters that were in GetAdditionals, thus the ActionFilter.
BTW, I now created a c# class that holds all the parameters of the GetAdditionals call, use JSON.stringify to send the object to the server and reconstruct an object of that class using JsonConvert.DeserializeObject<MyClass>()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.