views:

85

answers:

2

Does the ASP.NET MVC 2 Default View Model Binding support binding a multi-value cookie to a custom object? Before I write a custom Value Provider, I would like to be sure that the functionality didn't already exist.

So given an action like:

public ActionResult SomeAction(CustomObject foo)

where CustomObject is something like:

public class CustomObject
{
    public string Name { get; set; }
    public int Rank { get; set; }
}

and a cookie that is part of the request like:

foo=Name=John&Rank=10

Could I get the Default View Model Binding to map the cookie to the parameter with some clever tweaks to the naming of the cookie or cookie values like posting "foo.Name=John" and "foo.Rank=10" would do?

A: 

Well, there's one way to do it would be to implement IModelBinder

public class CustomObjectModelBinder : IModelBinder {

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
        HttpCookie c = controllerContext.HttpContext.Request.Cookies["foo"]

        CustomObject value = new CustomObject() {
            foo.Name = c.Values["Name"],
            foo.Rank = c.Values["Rank"]
        }

        return CustomObject
    }

}

Then just add this to your Application_Start()

ModelBinders.Binders.Add(typeof(CustomObject), new CustomObjectModelBinder());

you can add the cookie object to any action as far as i know and it will attempt to bind it for you

BuildStarted
True, and I considered it but was looking for a solution that wouldn't require custom model binding. In many cases this would be the best solution.
ongle
A: 

In the end I created something to do this. Based on the work posted by Mehdi Golchin, I created a value provider that allows this kind of binding to happen.

For those interrested, the following are the custom changes I made to Mehdi's work linked above. See the link for full details on implementation. This doesn't support binding to nested objects (e.g., Foo.Cell.X) because I didn't need that level of complexity, but it would be possible to implement with a bit of recursion.

protected virtual bool ContainsPrefix(string prefix)
{
    try
    {
        var parts = prefix.Split(new char[] { '.' }, 2, StringSplitOptions.RemoveEmptyEntries);

        switch (parts.Length)
        {
            case 0:
                return false;
            case 1:
                return this._context.HttpContext.Request.Cookies.AllKeys.Contains(parts[0]);
            default:
                var cookie = this._context.HttpContext.Request.Cookies[parts[0]];
                if (cookie == null) { return false; }
                return cookie.Values.AllKeys.Contains(parts[1]);
        }
    }
    catch (Exception ex)
    {
        ExceptionPolicy.HandleException(ex, "Controller Policy");
        return false;
    }
}

protected virtual ValueProviderResult GetValue(string key)
{
    try
    {
        var parts = key.Split(new char[] { '.' }, 2, StringSplitOptions.RemoveEmptyEntries);

        if (parts.Length < 2) { return null; }

        var cookie = this._context.HttpContext.Request.Cookies[parts[0]];

        if (cookie == null) { return null; }

        var value = cookie.Values[parts[1]];

        if (value == null) { return null; }

        return new ValueProviderResult(value, value, CultureInfo.CurrentCulture);
    }
    catch (Exception ex)
    {
        ExceptionPolicy.HandleException(ex, "Controller Policy");
        return null;
    }
}
ongle