上的HttpContext从StructureMap获得零用户上的HttpContext从Struc

2019-05-12 12:48发布

好吧,我刚才的问题/设置有太多的变数,所以我剥离下来到它的裸露的骨头组成。

下面给出使用StructureMap3代码...

//IoC setup
For<HttpContextBase>().UseSpecial(x => x.ConstructedBy(y => HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) : null ));
For<ICurrentUser>().Use<CurrentUser>();

//Classes used
public class CurrentUser : ICurrentUser
{
    public CurrentUser(HttpContextBase httpContext)
    {
        if (httpContext == null) return;
        if (httpContext.User == null) return;
        var user = httpContext.User;
        if (!user.Identity.IsAuthenticated) return;
        UserId = httpContext.User.GetIdentityId().GetValueOrDefault();
        UserName = httpContext.User.Identity.Name;
    }

    public Guid UserId { get; set; }
    public string UserName { get; set; }
}

public static class ClaimsExtensionMethods
    public static Guid? GetIdentityId(this IPrincipal principal)
    {
        //Account for possible nulls
        var claimsPrincipal = principal as ClaimsPrincipal;
        if (claimsPrincipal == null)
            return null;
        var claimsIdentity = claimsPrincipal.Identity as ClaimsIdentity;
        if (claimsIdentity == null)
            return null;
        var claim = claimsIdentity.FindFirst(x => x.Type == ClaimTypes.NameIdentifier);
        if (claim == null)
            return null;

        //Account for possible invalid value since claim values are strings
        Guid? id = null;
        try
        {
            id = Guid.Parse(claim.Value);
        }
        catch (ArgumentNullException) { }
        catch (FormatException) { }
        return id;
    }
}

这怎么可能在监视窗口?


我有我升级到使用StructureMap 3.X从2.x的Web应用程序,但我发现在特定依赖古怪的行为。

我有一个ISecurityService,我用它来获得核实一些事情,当用户请求一个页面。 该服务依赖一个小界面,我已经叫ICurrentUser上。 类的实现是非常简单的,实际上它可能是一个结构。

public interface ICurrentUser
{
    Guid UserId { get; }
    string UserName { get; }
}

这是通过使用下面的代码依赖注入获得。

For<ICurrentUser>().Use(ctx => getCurrentUser(ctx.GetInstance<HttpContextBase>()));
For<HttpContextBase>().Use(() => getHttpContext());

private HttpContextBase getHttpContext()
{
    return new HttpContextWrapper(HttpContext.Current);
}

private ICurrentUser getCurrentUser(HttpContextBase httpContext)
{
    if (httpContext == null) return null;
    if (httpContext.User == null) return null; // <---
    var user = httpContext.User;
    if (!user.Identity.IsAuthenticated) return null;
    var personId = user.GetIdentityId().GetValueOrDefault();
    return new CurrentUser(personId, ClaimsPrincipal.Current.Identity.Name);
}

当一个请求到来时,我的网站广泛认证首先发生,这取决于ISecurityService 。 出现这种情况OWIN内,似乎之前发生HttpContext.User已被填充,所以它的空,就这样吧。

后来,我有检查一个ActionFilter,通过ISecurityService ,如果当前用户已经同意当前版本的TermsOfUse为网站,如果他们不被重定向到该页面首先同意给他们。

这一切都在structuremap 2.x中工作得很好 对于我的迁移StructureMap3我已经安装了NuGet包StructureMap.MVC5帮助速度东西了我。

当我的代码获取到线在我ActionFilter检查使用的术语我有这个。

var securityService = DependencyResolver.Current.GetService<ISecurityService>();
agreed = securityService.CheckLoginAgreedToTermsOfUse();

里面的CheckLoginAgreedToTermsOfUse()我的情况下CurrentUser为空。 尽管它会hazve成功了,我getCurrentUser的内部断点()似乎从来没有被击中。 它几乎一样,如果它是一个定局,因为它是空的最后一次,即使它已经解决了这个时候。

我有种莫名其妙,为什么getCurrentUser()不会被调用上请求ISecurityService 。 我甚至尝试明确坚持一个.LifecycleIs<UniquePerRequestLifecycle>()我联播处理ICurrentUser没有效果。

更新:好了,所以刚一抬头,我用下面接受的方法开始,虽然它一直很大,到目前为止,它并没有解决我的核心问题。 原来,新StructureMap.MVC5 ,基于StructureMap3 ,使用NestedContainers。 其范围及其对NestedContainer的寿命,而不管默认的是瞬态的请求。 所以,当我要求HttpContextBase首次,它就会返回同一个实例的请求(即使后来在要求的寿命,已经时过境迁的其余部分。你需要或者不使用NestedContainer(其中,因为我了解它的东西ASP.NET vNext)复杂化,或明确设置的生命周期For<>().Use<>()映射给你每次请求一个新的实例。需要注意的是每NestedContainer此作用域会导致控制器问题以及在MVC。虽然StructureMap.MVC5包处理这与ControllerConvention ,它不处理意见,并使用递归的景色或多次可能会导致你的问题很好,我还在寻找的一个永久性的修复查看问题,目前我已经收归DefaultContainer

Answer 1:

我还没有和OWIN工作,但在IIS托管在整合模式的HttpContext未填充之后才HttpApplication.Start事件完成。 在DI而言,这意味着你可以不依赖于任何构造使用的HttpContext的属性。

这是有道理的,如果你仔细想想,因为应用程序应任何个人用户上下文之外进行初始化。

为了解决这个问题,你可以注入一个抽象工厂到您ICurrentUser实施和使用Singleton模式来访问它,它保证的HttpContext将不能访问它填充到。

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

就个人而言,我会做的用户ID和用户名的属性只读,这将简化设计,并确保它们不会在其他地方申请劫持。 我也会使注入到ICurrentUser的构造函数,而不是在一个扩展方法检索要求标识的IClaimsIdentityRetriever服务。 扩展方法违背DI的粮食,一般只适用于保证不会有任何依存关系(如字符串或顺序操作)的任务是有用的。 使得它的松耦合服务也意味着你可以方便地更换或延长实施。

当然,这意味着你不能打电话给你的CurrentUser类的用户ID或用户名的属性在任何构造也是如此。 如果任何其他类依赖于ICurrentUser,您可能还需要为了安全地使用它的ICurrentUserFactory。

抽象工厂是难以注入依赖打交道时的救星和解决的问题,包括这一个主机。



文章来源: Null User on HttpContext obtained from StructureMap