views:

224

answers:

2

Here's my scenario:

I've successfully created a custom IIdentity that I pass to a GenericPrincipal. When I access that IIdentity in my controller I have to cast the IIdentity in order to use the custom properties. example:

public ActionResult Test()
{
    MyCustomIdentity identity = (MyCustomIdentity)User.Identity;
    int userID = identity.UserID;
    ...etc...
}

Since I need to do this casting for nearly every action I would like to wrap this functionality in an ActionFilterAttribute. I can't do it in the controller's constructor because the context isn't initialized yet. My thought would be to have the ActionFilterAttribute populate a private property on the controller that I can use in each action method. example:

public class TestController : Controller
{
    private MyCustomIdentity identity;

    [CastCustomIdentity]
    public ActionResult()
    {
        int userID = identity.UserID;
        ...etc...
    }
}

Question: Is this possible and how? Is there a better solution? I've racked my brain trying to figure out how to pass public properties that are populated in an attribute to the controller and I can't get it.

+1  A: 

All you have to do is access the ActionExecutingContext of an overloaded OnActionExecuting() method and make identity public instead of private so your actionfilter can access it.

public class CastCustomIdentity : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        ((TestController) filterContext.Controller).Identity = (MyCustomIdentity)filterContext.HttpContext.User;



        base.OnActionExecuting(filterContext);
    }
}

This could be even easier by using a custom base controller class that all of your controllers would inherit from:

public class MyCustomController
{
    protected MyCustomIdentity Identity { get{ return (MyCustomIdentity)User.Identity; } }
}

and then:

public class TestController : MyCustomController
{
    public ActionResult()
    {
        int userID = Identity.UserId
        ...etc...
    }
}
jfar
+1  A: 

You could use a custom model binder...

I can't remember why I used this method over the base controller method @jfar mentions (which is also a good option), but it works well for me and I actually kinda like it because my actions are more self describing through their parameters.

MyCustomIdentityModelBinder.cs

public class MyCustomIdentityModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.Model != null)
            throw new InvalidOperationException("Cannot update instances");

        //If the user isn't logged in, return null
        if (!controllerContext.HttpContext.User.Identity.IsAuthenticated)
            return null;

        return controllerContext.HttpContext.User as MyCustomIdentity;
    }
}

Inside your application start event in Global.asax.cs

System.Web.Mvc.ModelBinders.Binders.Add(typeof(MyCustomIdentity), new MyCustomIdentityModelBinder());

Then whenever you have a type of MyCustomIdentity as an action parameter, it'll automatically use the MyCustomIdentityModelBinder.

Eg.

public class TestController : Controller
{
    public ActionResult Index(MyCustomIdentity identity)
    {
        int userID = identity.UserID;
        ...etc...
    }
}

HTHs,
Charles

Charlino