I have IHttpContextAccessor injected in a controller. I am reading user name from IHttpContextAccessor and returning it back to the UI.
My question is is very basic. I need to understand how this will work when multiple users will connect and request User Name.
I just want to make sure that the user names will be returned only for the corresponding user.
I don't want to save the user name in session variable after returned to the client. Whenever is needed, I will request a call and get it from the context.
Is this approach valid?
private IHttpContextAccessor ctx
public EmployeeController(IHttpContextAccessor context)
{
ctx=context;
}
public string GetEmployeeName()
{
return ctx.HttpContext.User.UserName;
}
The HttpContextAccessor will be registered as the Singleton service when you
called AddHttpContextAccessor.
According to the source codes, you could find the HttpContextAccessor will check the _httpContextCurrent value when it call get and set method. It will auto set the _httpContextCurrent.Value when the new request come to the application.
Related
I am trying to do something seemingly very simple. All I want to do is get the current Identity User's email address for my Blazor view. I have a UserEmailController in my Server project, with a GetCurrent() method that returns a Task<string>.
public class UserEmailsController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public UserEmailsController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
[HttpGet]
[Route("api/currentUser")]
public async Task<string> GetCurrent()
{
var user = await _userManager.GetUserAsync(User);
return await _userManager.GetEmailAsync(user);
}
}
From the spelunking I've done, it seems that the User property on the Controller base class should have the information loaded into it when a user is logged in, and I should be able to do something like await _userManager.GetUserAsync(User). However, when I break on this line, the Name property of User is null, along with many of the other values. But IsAuthenticated is true. Huh?
So, the result of the _userManager.GetUserAsync(User) call is null.
I've seen a few posts about this, and there are two common themes.
#1 - people try to use the User property in the constructor of a controller and it doesn't work. The suggested solution is to use it in an Action instead, which is of course what I'm doing.
#2 - another common suggestion is to use HttpContext.User, or to inject an IHttpContextAccessor (added via services.AddSingleton... or services.AddHttpContextAccessor()). I've tried all of these approaches and I get the same result.
I found a solution here. Specifically using HttpContext.User.FindFirst(ClaimTypes.NameIdentifier) and then passing that into _userManager.FindByIdAsync(nameId). Hell of a roundabout way of doing it, but it works.
I've read about configuring IHttpContextAccessor as services.AddSingleton scope, but I also read about it is working "async local", and I am also aware of the sophisticated working of async in ASP.Net, I mean for example if a controller action method is async, and it await for an async call, then it may continue with an other thread, but magically some thread bound things with maintained (like HttpContext)
My concrete use case: I have to inject a MyConverter class to my EF Core DbContext which uses it in OnModelCreating. However this model is cached by the DbContext, so any subsequent request, even it will have a brand new instance of DbContext will use this very same model so the very same MyConverter instance. (even it has configured services.AddTransient). This MyConverter has a constructor and an injected IHttpContextAccessor, so based on the very similar reasons, it effectively will be also a singleton for all DbContext/MyConverter usages.
Question
This particular HttpContextAccessor instance which is created in the very first request will serve all the subsequent requests in the Web app lifecycle. Will it work correctly? Is there any (concurrency) trap here?
(Do I suppose correctly that it does not really matter if we use a single or multiple HttpContextAccessor instances, because its implementation of getting HttpContext will use the correct way including async local thread switch traps etc to return with the correct HttpContext?)
Short answer: register as services.AddHttpContextAccessor() and then you can inject IHttpContextAccessor wherever you want and it'll work as long as you're using it in the request's execution context. For instance you can't read HTTP request headers for code that was not initiated by a HTTP request.
You're right that the IHttpContextAccessor should be registered as a singleton. Instead of doing it yourself, the recommendation is to use AddHttpContextAccessor() extension method. See source code here. It internally registers an HttpContextAccessor as a singleton.
The code for HttpContextAccessor can be found here, which I'm also pasting below:
public class HttpContextAccessor : IHttpContextAccessor
{
private static AsyncLocal<HttpContextHolder> _httpContextCurrent = new AsyncLocal<HttpContextHolder>();
public HttpContext HttpContext
{
get
{
return _httpContextCurrent.Value?.Context;
}
set
{
var holder = _httpContextCurrent.Value;
if (holder != null)
{
// Clear current HttpContext trapped in the AsyncLocals, as its done.
holder.Context = null;
}
if (value != null)
{
// Use an object indirection to hold the HttpContext in the AsyncLocal,
// so it can be cleared in all ExecutionContexts when its cleared.
_httpContextCurrent.Value = new HttpContextHolder { Context = value };
}
}
}
private class HttpContextHolder
{
public HttpContext Context;
}
}
Since the HttpContext getter property returns from an async local field, you always get the HttpContext local to the execution context.
The HttpContext field is set in HttpContextFactory.Create() only if IHttpContextAccessor was registered with the DI. Source.
And HttpContextFactory.Create() is invoked from [HostingApplication](https://github.com/aspnet/AspNetCore/blob/v2.2.5/src/Hosting/Hosting/src/Internal/HostingApplication.cs) where the context is setup.
I have a static class User with a static property Username. I set this property in a middleware on each url request. Then i show the Username to user in the header of site.
On every request, this static property is set and then shown in the view. I am assuming that each request will have it's own (correct) value in the property and the value does not get shared between different requests?
So, if request one has value adam and request 2 has value john, adam will see adam and john will see john in the header? This is working okay in my app but just want to make sure that conceptually it is right?
This as you have already been informed is a good candidate for HttpContext.Items with a unique object key to avoid key collisions. As each context is unique for each request they will allow the values to not get shared between the different requests. You are not setting the value as a static value on the middle ware but on the current request context.
public class SampleMiddleware {
public static readonly object SampleKey = new Object();
public async Task Invoke(HttpContext httpContext) {
httpContext.Items[SampleKey] = "some value";
// additional code omitted
}
}
Other code can access the value stored in HttpContext.Items using
the key exposed by the middleware class:
public class HomeController : Controller {
public IActionResult Index() {
string value = HttpContext.Items[SampleMiddleware.SampleKey];
}
}
Reference Introduction to session and application state in ASP.NET Core
So only the key is static but the value set in the items will be unique for each request. That means in your case that if request one has value adam and request 2 has value john, adam will see adam and john will see john in the header
I'm setting a Property on Request.Properties inside a DelegatingHandler after I pluck some data out of a header on an incoming request to a Web API.
This all works fine. I can also access Request.Properties from within the controller as well as in my Action and Exception filters. However, I also need to access this data from outside of the controller (I call a business layer class from the controller). It is data I want to include in some logs in other places,
I can see HttpContext.Current from this class, and I can see the original header from here, so I guess I could pluck it out again, but since I have already done this and put it in the Properties it seems to make more sense to get it from there. However, I don't seem to have access to the Request.Properties from anywhere else.
If this isn't the right way to do this, how else would I pass around this per-request data so that it was accessible from anywhere on the stack in Web API?
I also need to access [Request.Properties] data from outside of the controller (I call a business layer class from the controller). It is data I want to include in some logs in other places... However, I don't seem to have access to the Request.Properties from anywhere else. If this isn't the right way to do this, how else would I pass around this per-request data so that it was accessible from anywhere on the stack in Web API?
You can get it from HttpContext.Current, though it is less than ideal. Keep in mind that if any other non-web applications consume the same business layer, then HttpContext.Current would be null. HttpContext.Current is only non-null when you are running in IIS, and an IIS thread is handling the execution of the request stack. If you ever plan to self-host the web api using OWIN without IIS, there will be no HttpContext.Current.
Personally, if the data really is important enough to be passed into the business layer to be logged, then I would just pass it to the business layer method:
public Task<HttpResponseMessage> SomeAction(SomeModel model) {
... other code
someBusinessLayerObject.SomeMethod(arg1, arg2, Request.Properties["myHeaderKey"]);
}
...If you need other values from Request.Properties, then you can just pass the whole dictionary to the methods that will end up using its values.
A third option if you are using an inversion of control container would be to add some kind of scoped object dependency class and put the data in there. Then constructor inject it into your business layer class:
public interface IHaveRequestData {
IDictionary<string, object> Properties { get; set; }
}
public class RequestData : IHaveRequestData {
public IDictionary<string, object> Properties { get; set; }
}
// ioc registration pseudocode
iocContainer.Register<IHaveRequestData, RequestData>(Lifetime
.WhateverYouNeedSoThatOneOfTheseGetsCreatedForEachWebRequest);
public class SomeController : ApiController {
private readonly IHaveRequestData RequestData;
public SomeController(IHaveRequestData requestData) {
RequestData = requestData;
}
public Task<HttpResponseMessage> SomeAction() {
// you may even be able to do this part in an action filter
RequestData.Properties = Request.Properties;
}
}
public class SomeBusinessLayerComponent {
private readonly IHaveRequestData RequestData;
private readonly ILog Log;
public SomeBusinessLayerComponent(IHaveRequestData requestData, ILog log) {
RequestData = requestData;
Log = log;
}
public Task SomeMethod() {
Log.Info(RequestData["myHeader"]);
}
}
I have a problem with my MVC 4 application which used to work fine, but stopped for some reason, and I cannot find out why. I use simple memebrship provider and code first approach. This is my Index action method in the home controller
[Authorize]
public class HomeController : Controller
{
private IActivityRepository repo;
public HomeController(IActivityRepository activityRepository)
{
repo = activityRepository;
}
//Allow anonymous to allow to create database if there isn't one yet
[AllowAnonymous]
public ActionResult Index()
{
repo.InitializeDatabase(); //!!!!!!!!!!!!!!!!!!!!!
return RedirectToAction("ManageActivities");
}
The whole concept of mine is that if database doesn't exist it gets created in InitializeDatabase Method. Then user is redirected to ManageActivities action method which is decorated with [Authorize] attribute, what in effect takes user to login action method in AccountCotroller (out of the box in MVC4). This controller is decorated with [InitializeSimpleMembership], what fires InitializeSimpleMembershipAttribute filter.
This logic worked fine for me a while ago. Today I wanted to create a new database for testing purposes. When I create data context I call the base class with a custom name for the database like so:
public class ActivityLogContext : DbContext
{
public ActivityLogContext() : base("ActivitiesConnection")
{
}
So I've changed details for my connection string and run the application. Unfortunatelly, for some reason the code hits InitializeSimpleMemebership filter before running Index method from the home controller (even though its decorated with [AllowAnonymous]). In effect simple membership is initialized but database does not yet exist, what runs me into error.
My question is, why InitializeSimpleMemebership filter is getting released on application start if Index method doesn't require authorization?
I would eliminate the use of the InitializeSimpleMembership as discussed in this article. Move initialization to the Global.asax Application_Start method and do your initialization there also, so that it happens in the correct sequence.