Question

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.

Was it helpful?

Solution

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...
    }
}

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top