10

I am having a Web API application in which the controller has Services/Repositories etc. injected into it through Dependency Injection (Unity). Let's assume that I have an IStuffService that needs the IPrincipal of the current request (or a wrapper around it).

The problem with Web API seems to be that the only reliable source of the current Request/User is the Request property on the instance of the ApiController. Anything static (be it HttpContext.Current, CallContext.Get/SetData or Thread.Get/SetData) is not guaranteed to be on the same thread due to the sync nature of Web API.

How do I reliably ensure that Request-specific context is passed through dependencies, and more importantly, that the operation retains the correct IPrincipal all the way through the operation?

Two options:

  1. Every method that needs an IPrincipal has it as an argument to the method - that is the most reliable way, but it also requires me to have that thing in every method signature
  2. Inject the IPrincipal into the ctor of the Service, spinning up a new insance of the object graph on every request, using a DependencyOverride in Unity: container.Resolve(opType, new DependencyOverride(typeof(IPrincipal), principal))

Option 2 means that my method signatures are clean, but it also means I need to make sure all dependencies are using the TransientLifetimeManager, not a Singleton or even Per-Thread one.

Is there a better solution than I'm not seeing?

Michael Stum
  • 177,530
  • 117
  • 400
  • 535
  • Ideally, do you want your `IStuffService` implementation to be a singleton across all requests? – Xenolightning Jun 18 '14 at 22:57
  • @Xenolightning But how would the service get the current `IPrincipal` when I'm making a method call? – Michael Stum Jun 18 '14 at 23:05
  • Interesting question, but why would you need an `IPrincipal` for every method in your controller? – DavidG Jun 19 '14 at 00:10
  • @DavidG I need it in my Service (it's available in my Controller). One example is to log which user made a change, or to determine if a service call is legal given the current user. That's a business-logic-level decision that lives in the service and needs to know who's calling to make that decision. – Michael Stum Jun 19 '14 at 00:11
  • @MichaelStum Wouldn't that logic sit better in an `AuthorizeAttribute` for example? – DavidG Jun 19 '14 at 00:15
  • @DavidG The business logic is doing a lot of different checks based on the action. E.g., Manager X might be able to to Action Y to Employee Z, but only some other State X2 is good. On the other hand, Manager X might always be able to to Action Y2 to Employee Z regardless of state. I would have a whole bunch of Authorize Attributes that describe the semantics of the action. So that could live in the Controller. Web API is only one possible consumer of the logic though, there might also be a Console App or a separate MVC site that doesn't talk through the API. – Michael Stum Jun 19 '14 at 00:23
  • And also, despite all the improvements in Unit Testing, Controllers are still much harder to test than Services since I only need to worry about an IPrincipal, not about a RequestContext. From a maintenance standpoint (and experience with issues previously), it makes most sense to move almost all of the business logic/validation into a separate Service-Layer and have the Web API/MVC/Console App be a very small host that just sends stuff down to the service and responses back to the user. – Michael Stum Jun 19 '14 at 00:25
  • @MichaelStum If the `IPrincipal` is part of the logic, I'd probably make it a parameter of the action method. That's the only safe way you'd be able to do unit testing. – DavidG Jun 19 '14 at 00:42
  • @DavidG Indeed, adding it to the method is safe. It's also very verbose as there are dozens of individual service calls :/ – Michael Stum Jun 19 '14 at 01:18
  • @MichaelStum, I believe `HttpContext.User` should be flowed correctly across `async/await` (within the same HTTP request). Is it not for you? – noseratio Jun 19 '14 at 09:15
  • @Noseratio See the other answers - in .net 4.0, it's bound to the current thread and was not properly maintained. It seems that in 4.5, this might be fixed. That said, HttpContext.Current is still not that appropriate in Web API because on self-hosted ones there is no HttpContext.Current. – Michael Stum Jun 20 '14 at 02:11
  • @MichaelStum, my reply couldn't fit into a comment, so I posted it as an [answer](http://stackoverflow.com/a/24319210/1768303). – noseratio Jun 20 '14 at 02:51

4 Answers4

2

The ultimate answer is that our IoC containers need to be changed to support async/await better.

Background:

The behavior of async/await around this changed between .NET 4 and .NET 4.5. In .NET 4.5 the SynchronizationContext was introduced and it will correctly restore HttpContext.Current (see http://msdn.microsoft.com/en-us/magazine/gg598924.aspx). However, it is often a best practice to use .ConfigureAwait(false) (see "Configure Context" in http://msdn.microsoft.com/en-us/magazine/jj991977.aspx) and that specifically requests that the context not be preserved. In that case you would still have the issue you describe.

Answer:

The best answer I have been able to come up with in my own code is to be sure to request the dependency that comes from HttpContext.Current (in your case IPrincipal) early in the web request so that is is loaded into the container.

I don't have any experience with Unity, but in Ninject this would look something like:

kernal.Bind<IPrincipal>().ToMethod(c => HttpContext.Current.User).InRequestScope();

Then I would be sure to load the IPrincipal early in the web request before you have lost the context. Either in BeginRequest or as a dependency of the controller. That will cause the IPrincipal to be loaded into the container for this request.

Note: There are still situations where this may not work. I don't know if Unity has this issue, but I know Ninject does. It actually uses the HttpContext.Current, to determine what request is active. So if you try to resolve something from the container later, like a service locator or factory then it may not be able to resolve.

Community
  • 1
  • 1
Jeff Walker Code Ranger
  • 4,634
  • 1
  • 43
  • 62
  • are you sure this solves the problem when we execute code another thread (asynch, await, or any other methods,...) ? – Khanh TO Jun 19 '14 at 14:54
  • how can the container know that the new thread is `related` with the current thread which has the context? – Khanh TO Jun 19 '14 at 14:57
2

From the comments:

@MichaelStum, I believe HttpContext.User should be flowed correctly across async/await (within the same HTTP request). Is it not for you? – Noseratio 17 hours ago

@Noseratio See the other answers - in .net 4.0, it's bound to the current thread and was not properly maintained. It seems that in 4.5, this might be fixed. That said, HttpContext.Current is still not that appropriate in Web API because on self-hosted ones there is no HttpContext.Current.

AFAIK, there's no proper support for async/await in ASP.NET 4.0 anyway (you probably can use Microsoft.Bcl.Async for the language support, but there is no ASP.NET runtime support, so you'd have to resort to AsyncManager to implement the TAP pattern).

That said, I'm 99% sure Thread.CurrentPrincipal would still be correctly flowed across await continuations in ASP.NET 4.0. That's because it gets flowed as a part of ExecutionContext flow, rather than by synchronization context. As to HtttContext.Current.User, I'm not sure if it would flow correctly in ASP.NET 4.0 (although it certainly does in ASP.NET 4.5).

I've re-read your question, but could find an explicit complaint about Thread.CurrentPrincipal not being correctly flowed. Are you experiencing this issue in existing code (if so, it probably would be a bug in ASP.NET)?

Here's a list of related questions, answered with some great insights by Stephen Cleary:

This blog post by Scott Hanselman is also related, although he speaks about WebForms:

If you're concerned about self-hosting scenarios, I believe Thread.CurrentPrincipal will still be flowed correctly there (once having been set to a correct identity). If you want to flow any other properties (besides those which get automatically flowed with ExecutionContext), you can roll out your own synchronization context. Another option (not so nice, IMO) is to use custom awaiters.

Finally, if you face a situation where you actually require thread affinity across await continuation (much like in a client side UI app), you have such option, too (again, using a custom synchronization context):

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
0

I know this is an old question I have worked with option one (in the question) and, it works.

UPDATE: I deleted my answer as I realized I've posted something that doesn't work. Sorry for any inconvenience.

GenTech
  • 668
  • 6
  • 9
-1

How do I reliably ensure that Request-specific context is passed through dependencies, and more importantly, that the operation retains the correct IPrincipal all the way through the operation?

I don't think you should do this. Your service is a lower layer than your Api controller. Your service should not depend on any classes related to the higher layer, otherwise your service could not be reused, for example: when you need to build a win forms application on top of the existing services.

IPrincipal is not appropriate to be injected into our services as it's web application related . When we pass this information down to lower layers (service), we should pass our neutral-classes or just a userId to decouple our services from the application using it.

You should define your own classes for Users and anything request-related to be used in our services layer as it's more domain-related. With this, your service-layer is application-layer (web, win forms, console,..) agnostic:

public class AppPrincipal : IAppPrincipal
{
   public int UserId { get; set; }
   public string Role { get; set; }
   //other properties
   public AppPrincipal() {

   }
   public AppPrincipal(int userId, string role):this() {
       UserId  = userId;
       Role = role;
   }
}

Then you can register IAppPrincipal as per-request scope in your web application and populate all the properties using your IPrincipal. The will initialize your IAppPrincipal for your entire object graph before any await/async calls. Example code with Unity:

public void RegisterTypes(IUnityContainer container)
{
    container.RegisterType<IAppPrincipal>(
                new PerRequestLifetimeManager(),
                new InjectionFactory(c => CreateAppPrincipal()));
}
public IAppPrincipal CreateAppPrincipal()
{
    var principal = new AppPrincipal();
    principal.UserId = //Retrieve userId from your IPrincipal (HttpContext.Current.User)
    principal.Role = //Retrieve role from your IPrincipal (HttpContext.Current.User)
    return principal;
}

The key here is that we already decouple our service layer from the web. If you need to reuse your service layer to build a windows form or console application, you could register IAppPrincipal as singleton and populate it differently.

We don't need to deal with platform-related problems like async/await

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • 1
    Does not answer the question at all. As he stated, `HttpContext.Current` may not be correct when accessed after an `await`. – Jeff Walker Code Ranger Jun 19 '14 at 13:38
  • @Jeff Walker Code Ranger: it looks like that this is not a problem: http://stackoverflow.com/questions/13557798/preserve-httpcontext-when-going-async-with-webapi-medium-trust – Khanh TO Jun 19 '14 at 13:50
  • HttpContext.Current also doesn't exist if the API is Self-Hosted as far as I know. – Michael Stum Jun 20 '14 at 02:13
  • @Michael Stum: That's what I mean in my first point. Your service layer is `tightly coupled` with your application layer. It cannot work with console, win forms,... because there is no HttpContext. – Khanh TO Jun 20 '14 at 03:26
  • @KhanhTO But that's why I want to use an IPrincipal. The Service takes the IPrincipal, but it's the job of the host to make sure it has a service with that IPrincipal. This is the problem I'm facing: I have an IPrincipal in the Web API Controller and need it in the service through whatever many indirections and async/await blocks (with possible thread switches) are in between. – Michael Stum Jun 20 '14 at 03:57
  • @Michael Stum: I also don't think `IPrincipal` is appropriate to be injected into services as it should just be a normal user in your application (not an external dependency). – Khanh TO Jun 20 '14 at 04:01
  • @Michael Stum: about the threading, this is a general problem. All we're trying to do are just `workarounds` as we should not implement our application this way. There is no solution that covers 100% of the cases, let's say your service code spawn a thread to execute a background task. – Khanh TO Jun 20 '14 at 04:04
  • 1
    @MichaelStum: I have updated the answer, the idea is the same but use a different approach, please have a look and give your opinion on this, thanks. – Khanh TO Dec 03 '17 at 04:16