Our repositories and services are currently being injected into our controllers via a Unity container (using the Web API MVC bootstrapper).

public class AnnouncementsController : BaseApiController
{
    protected IAnnouncementRepository AnnouncementRepository{ get; set; }

    public AnnouncementsController (IAnnouncementRepository announcementRepository)
        : base()
    {
        this.AnnouncementRepository = announcementRepository;
    }

    public HttpResponseMessage Post([FromBody]GetAnnouncementsModel model)
    {
        var announcements = AnnouncementRepository.GetByType(model.AnnouncementType);
        // ... 
    }
}

A new requirement has arisen: All input models (e.g. GetAnnouncementsModel) now need to have an AccessToken.

Why? So that results from repositories are filtered according to data rights. Clients are restriction on what data they can consume.

Bad Solution - Pass in token as a method parameter

One solution is to include an AccessToken parameter to every repository or service call. This is not a good solution. We have to implement this in hundreds of methods. An example of this parameter:

public HttpResponseMessage Post([FromBody]GetAnnouncementsModel model)
{
    var announcements = AnnouncementRepository.GetByType(model.AccessToken, model.AnnouncementType);
    // ... 
}

Better Solution - Inject token during resolution

A better solution would be to provide the AccessToken in the repository constructors and have some base implementation that does the filtering logic implicitly.

But how could I do this with dependency injection? The constructor is resolved and called by the Unity container. How could I inject the property value of an input model into this process?

container.RegisterType<IAnnouncementRepository, AnnouncementRepository>(
    new InjectionConstructor(
        new InjectionParameter<Guid>(AccessToken)
    )
);
有帮助吗?

解决方案

You can define a custom interface, call it for example IAccessTokenProvider:

interface IAccessTokenProvider
{
    Guid Token { get; }
}

Now you can make an implementation like this:

public class HttpContextAccessTokenProvider
{
    public Guid Token
    {
      get { return (Guid)HttpContext.Current.Items["AccessToken"]; }
    }

    public static void SetToken(Guid token) 
    {
        HttpContext.Current.Items["AccessToken"] = token;
    }
}

Now you should be able to implement a filter to read the token from the request:

public class TokenFilter : ActionFilterAttribute
{
     public override void OnActionExecuting(ActionExecutingContext filterContext)
     { 
         string tokenString = filterContext.HttpContext.Request.QueryString["token"];

         ActionExecutingContext.SetToken(Guid.Parse(tokenString));
     }
}

You can also read the token from other sources or store it in other containers (sessions, cookies, whatever). You can also directly access it in your controller or repositories.

You have 2 options to use the token in your repository:

  1. Inject IAccessTokenProvider to your repository and get the token directly from the provider.
  2. Inject IAccessTokenProvider to your controller and pass the token
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top