3

I am using the recently released MVC 4 Beta (4.0.20126.16343) and am working on getting around a known problem with deserialization/model binding not working with arrays (see Stack Overflow here)

I am having difficulty getting my explicit custom binding to hook up. I have registered a custome IModelBinder (or attempted to) but when my post action is called my custom binder isn't hit and I just get the default serialization (with the null arrays - even though wireshark shows me the incoming complex object contains array elements).

I feel like I am missing something and would greatly appreciate any solution or insight.

Thanks.

from global.asax.cs:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

protected void Application_Start()
{
    ModelBinders.Binders.Add(typeof(DocuSignEnvelopeInformation), new DocusignModelBinder());
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    BundleTable.Bundles.RegisterTemplateBundles();
}

and my custom binder:

public object BindModel(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext)
{
    var value = bindingContext.ValueProvider.GetValue("envelope");

    var model = new DocuSignEnvelopeInformation();

    //build out the complex type here

    return model;
}

and my controller is just:

public void Post(DocuSignEnvelopeInformation envelope)
{
    Debug.WriteLine(envelope);
}
Community
  • 1
  • 1
reuben
  • 173
  • 6

2 Answers2

2

Generally we register our Model Binders via DI container and it works. Register a IModelBinderProvider with DI container used by DependencyResolver and return your ModelBinder from there in the GetBinder method.

Mirko
  • 4,284
  • 1
  • 22
  • 19
  • Thanks for your response. For reasons that aren't worth going into we're not going down the DI path, so this option wasn't available to me. Certainly worth the upvote, but I'll hold off on marking as the answer because I am really after a "sample code" style answer. I suspect my real answer is waiting for the next (RC?) release of MVC 4 which is said to contain the fix for automated binding. – reuben May 22 '12 at 12:57
1

This is what I ended up doing (thanks to Jimmy Bogard in Model binding XML in ASP.NET MVC 3)

I wound my solution back to MVC 3. (Burned by pre-release anxiety once again)

Added a ModelBinderProvider:

public class XmlModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(Type modelType)
    {
        var contentType = HttpContext.Current.Request.ContentType;

        if (string.Compare(contentType, @"text/xml",
            StringComparison.OrdinalIgnoreCase) != 0)
        {
            return null;
        }

        return new XmlModelBinder();
    }
}

and a ModelBinder

public class XmlModelBinder : IModelBinder
{
    public object BindModel(
        ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
        var modelType = bindingContext.ModelType;
        var serializer = new XmlSerializer(modelType);

        var inputStream = controllerContext.HttpContext.Request.InputStream;

        return serializer.Deserialize(inputStream);
    }
}

and added this to Application_Start():

    ModelBinderProviders.BinderProviders
    .Add(new XmlModelBinderProvider());

My controller stayed exactly the same as in the question.

Works like a treat. Will be great when the new 'no strings' approach arrives properly with MVC 4, but this manual binding approach to deserialization isn't exactly onerous.

Community
  • 1
  • 1
reuben
  • 173
  • 6
  • I am going to go ahead and mark my own answer as accepted - since it's what I did and it worked. Please feel free to add anything if there's something that occurs to you as relevant. – reuben May 22 '12 at 21:06