Null User on HttpContext obtained from StructureMap

前端 未结 1 1938
走了就别回头了
走了就别回头了 2020-12-02 03:01

Ok, my previous question/setup had too many variables, so I\'m stripping this down to it\'s bare bones components.

Given the code below using StructureMap3...

<
相关标签:
1条回答
  • 2020-12-02 04:02

    I haven't worked with OWIN, but when hosting in IIS integrated mode the HttpContext is not populated until after the HttpApplication.Start event is complete. In terms of DI, this means that you cannot rely on using properties of HttpContext in any constructor.

    This makes sense if you think about it because the application should be initialized outside of any individual user context.

    To get around this, you could inject an abstract factory into your ICurrentUser implementation and to use a Singleton pattern to access it, which guarantees HttpContext won't be accessed until it is populated.

    public interface IHttpContextFactory
    {
        HttpContextBase Create();
    }
    
    public class HttpContextFactory
        : IHttpContextFactory
    {
        public virtual HttpContextBase Create()
        {
            return new HttpContextWrapper(HttpContext.Current);
        }
    }
    
    public class CurrentUser // : ICurrentUser
    {
        public CurrentUser(IHttpContextFactory httpContextFactory)
        {
            // Using a guard clause ensures that if the DI container fails
            // to provide the dependency you will get an exception
            if (httpContextFactory == null) throw new ArgumentNullException("httpContextFactory");
    
            this.httpContextFactory = httpContextFactory;
        }
    
        // Using a readonly variable ensures the value can only be set in the constructor
        private readonly IHttpContextFactory httpContextFactory;
        private HttpContextBase httpContext = null;
        private Guid userId = Guid.Empty;
        private string userName = null;
    
        // Singleton pattern to access HTTP context at the right time
        private HttpContextBase HttpContext
        {
            get
            {
                if (this.httpContext == null)
                {
                    this.httpContext = this.httpContextFactory.Create();
                }
                return this.httpContext;
            }
        }
    
        public Guid UserId
        {
            get
            {
                var user = this.HttpContext.User;
                if (this.userId == Guid.Empty && user != null && user.Identity.IsAuthenticated)
                {
                    this.userId = user.GetIdentityId().GetValueOrDefault();
                }
                return this.userId;
            }
            set { this.userId = value; }
        }
    
        public string UserName
        {
            get
            {
                var user = this.HttpContext.User;
                if (this.userName == null && user != null && user.Identity.IsAuthenticated)
                {
                    this.userName = user.Identity.Name;
                }
                return this.userName;
            }
            set { this.userName = value; }
        }
    }
    

    Personally, I would make the UserId and UserName properties readonly, which would simplify the design and ensure they don't get hijacked elsewhere in the application. I would also make an IClaimsIdentityRetriever service that is injected into the constructor of ICurrentUser instead of retrieving the claims Id in an extension method. Extension methods go against the grain of DI and are generally only useful for tasks that are guaranteed not to have any dependencies (such as string or sequence manipulation). The loose coupling of making it a service also means you can easily swap or extend the implementation.

    Of course, this implies that you cannot call the UserId or UserName properties of your CurrentUser class in any constructor as well. If any other class depends on ICurrentUser, you may also need an ICurrentUserFactory in order to safely use it.

    Abstract factory is a lifesaver when dealing with difficult-to-inject dependencies and solves a host of problems including this one.

    0 讨论(0)
提交回复
热议问题