从 StructureMap 获取的 HttpContext 上的空用户

2023-12-26

好吧,我之前的问题/设置有太多变量,所以我将其精简为最基本的组件。

给出使用 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;
    }
}

这怎么可能在“监视”窗口中实现?


I have a web application that I'm upgrading to using StructureMap 3.x from 2.x, but I'm getting odd behavior on specific dependency.

我有一个 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,如果当前用户已同意网站当前版本的使用条款,如果没有,他们将被重定向到首先同意的页面。

这一切在 Structuremap 2.x 中都运行良好。为了迁移到 StructureMap3,我安装了 Nuget 包 StructureMap.MVC5 以帮助加快速度。

当我的代码到达 ActionFilter 中用于检查使用条款的行时,我得到了这个。

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

代替CheckLoginAgreedToTermsOfUse(),我的实例CurrentUser一片空白。尽管它会成功,但我在 getCurrentUser() 内部的断点似乎从未被击中。这几乎就像是一个已成定局的事情,因为上次它是空的,尽管这次它会得到解决。

我有点困惑为什么getCurrentUser()从未被要求请求ISecurityService。我什至尝试明确地坚持.LifecycleIs<UniquePerRequestLifecycle>()在我的连接上进行处理ICurrentUser没有效果。


我没有使用过 OWIN,但是当以 IIS 集成模式托管时,直到 HttpApplication.Start 事件完成后才会填充 HttpContext。就 DI 而言,这意味着您不能依赖在任何构造函数中使用 HttpContext 的属性。

如果您考虑一下,这是有道理的,因为应用程序应该在任何单个用户上下文之外进行初始化。

为了解决这个问题,您可以将抽象工厂注入 ICurrentUser 实现中,并使用单例模式来访问它,这保证了 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; }
    }
}

就个人而言,我会将 UserId 和 UserName 属性设置为只读,这将简化设计并确保它们不会在应用程序的其他位置被劫持。我还会创建一个 IClaimsIdentityRetriever 服务,该服务被注入到 ICurrentUser 的构造函数中,而不是在扩展方法中检索声明 Id。扩展方法违背了 DI 的原则,通常仅适用于保证不具有任何依赖性的任务(例如字符串或序列操作)。使其成为服务的松散耦合还意味着您可以轻松交换或扩展实现。

当然,这意味着您也无法在任何构造函数中调用 CurrentUser 类的 UserId 或 UserName 属性。如果任何其他类依赖于 ICurrentUser,您可能还需要 ICurrentUserFactory 才能安全地使用它。

在处理难以注入的依赖项时,抽象工厂是一个救星,它解决了包括这个在内的一系列问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 StructureMap 获取的 HttpContext 上的空用户 的相关文章

  • 单元测试验证失败

    我正在运行我的单元测试PostMyModel路线 然而 在PostMyModel 我用的是线Validate
  • 在路由mvc 4中添加公司名称

    我一直在尝试为 Facebook 等用户提供在 URL 中添加公司名称的选项 http localhost 50753 MyCompany Login 我尝试过不同的网址 但没有成功 routes MapRoute name Default
  • 删除是如何工作的? [复制]

    这个问题在这里已经有答案了 可能的重复 C 编程 free 如何知道要释放多少 https stackoverflow com questions 1518711 c programming how does free know how m
  • 检测到堆栈崩溃

    我正在执行我的 a out 文件 执行后 程序运行一段时间 然后退出并显示消息 stack smashing detected a out terminated Backtrace lib tls i686 cmov libc so 6 f
  • 将表(行)与 OpenXML SDK 2.5 保持在一起

    我想在 Word 文档中生成多个表 每行 2 行 但我想将这两行保留在一起 如果可能的话 new KeepNext 第一行不起作用 new KeepNext 第一行的最后一段不起作用 new CantSplit 放在桌子上不起作用 在所有情
  • 获取 boost Spirit 语法中的当前行

    我正在尝试使用 boostspirit 获取正在解析的文件的当前行 我创建了一个语法类和结构来解析我的命令 我还想跟踪在哪一行找到命令并将其解析到我的结构中 我将 istream 文件迭代器包装在 multi pass 迭代器中 然后将其包
  • 访问 ascx 文件中的母版页控件

    我有一个母版页文件 其中包含 2 个面板控件中的 2 个菜单 我还使用控件来检查用户是否登录并获取用户类型 根据我想要显示 隐藏面板的类型 控件本身不在母版页中引用 而是通过 CMS 系统动态引用 我想在用户控件中使用findcontrol
  • 如何在 Asp.NET MVC 中使用 RedirectToAction() 传递多个对象?

    我想使用redirectToAction 方法传递多个对象 下面是我重定向到的操作结果 public ActionResult GetEmployees Models Department department Models Categor
  • 将接口转换为其具体实现对象,反之亦然?

    在 C 中 当我有一个接口和几个具体实现时 我可以将接口强制转换为具体类型 还是将具体类型强制转换为接口 这种情况下的规则是什么 Java 和 C 中都允许这两个方向 向下转型需要显式转型 如果对象类型不正确 可能会抛出异常 然而 向上转换
  • 析构函数中的异步操作

    尝试在类析构函数中运行异步操作失败 这是代码 public class Executor public static void Main var c1 new Class1 c1 DoSomething public class Class
  • 从BackgroundWorker线程更新图像UI属性

    在我正在编写的 WPF 应用程序中 我有一个 TransformedBitmap 属性 该属性绑定到 UI 上的 Image 对象 每当我更改此属性时 图像就会更新 因此显示在屏幕上的图像也会更新 为了防止在检索下一张图像时 UI 冻结或变
  • 如何从 Rx Subscribe 回调异步函数?

    我想回调 Rx 订阅中的异步函数 例如 像那样 public class Consumer private readonly Service service new Service public ReplaySubject
  • Linux mremap 不释放旧映射?

    我需要一种方法将页面从一个虚拟地址范围复制到另一个虚拟地址范围 而无需实际复制数据 范围很大 延迟很重要 mremap 可以做到这一点 但问题是它也会删除旧的映射 由于我需要在多线程环境中执行此操作 因此我需要旧映射能够同时使用 因此稍后当
  • .NET 4 的条件编译[重复]

    这个问题在这里已经有答案了 可能的重复 条件编译和框架目标 https stackoverflow com questions 2923210 c sharp conditional compilation and framework ta
  • CUDA 8 编译错误 -std=gnu++11

    我正在尝试转换一些代码以使用 CUDA 并且我认为我遇到了兼容性问题 我们使用CMake 这些是我使用的 gcc 和 CUDA 版本 gcc version gcc Ubuntu 5 4 0 6ubuntu1 16 04 5 5 4 0 2
  • 如何调试 .NET 运行时中的内部错误?

    我正在尝试调试一些处理大文件的工作 代码本身works 但 NET 运行时本身会报告零星错误 对于上下文 这里的处理是一个 1 5GB 文件 仅加载到内存中一次 在循环中处理和释放 故意尝试重现此否则不可预测的错误 我的测试片段基本上是 t
  • LINQ 中的“from..where”或“FirstOrDefault”

    传统上 当我尝试从数据库中获取用户的数据时 我使用了以下方法 在某种程度上 DbUsers curUser context DbUsers FirstOrDefault x gt x u LoginName id string name c
  • 如何得知客户端从服务器的下载速度?

    根据客户的下载速度 我想以低质量或高质量显示视频 任何 Javascript 或 C 解决方案都是可以接受的 Thanks 没有任何办法可以确定 您只能测量向客户端发送数据的速度 如果没有来自客户端的任何类型的输入来表明其获取信息的速度 您
  • 来自 3rd 方库的链接器错误 LNK2019

    我正在将旧的 vc 6 0 应用程序移植到 vs2005 我收到以下链接器错误 我花了几天时间试图找到解决方案 错误LNK2019 无法解析的外部符号 imp 创建AwnService 52 在函数 public int thiscall
  • DataContractSerializer 事件/委托字段问题

    在我的 WPF 应用程序中 我正在使用DataContractSerializer序列化对象 我发现它无法序列化具有事件或委托声明的类型 考虑以下失败的代码 Serializable public abstract class BaseCl

随机推荐

  • 从 numpy 数组中随机选择

    我有两个相关的 numpy 数组 X and y 我需要选择n随机行来自X并将其存储在数组中 对应的y值并附加随机选择的点的索引 我有另一个数组index它存储了我不想采样的索引列表 我怎样才能做到这一点 样本数据 index 2 3 X
  • 如何在 VBA 中打印大型用户表单

    我在 VBA 中有一个大型用户表单 表单上已经有一个 打印 选项 可以打印一半的表单 请告诉我如何在两页内打印横向和整个表格 Thanks Userform PrintForm 方法不提供任何选项 另一方面 工作表提供了无数的选项 I wo
  • 定时器精度:c Clock( ) 与 WinAPI 的 QPC 或 timeGetTime( )

    我想表征软件计时器的准确性 我不太关心它的准确性 但确实需要知道准确性是多少 我研究了c函数clock 以及WinAPI的函数QPC和timeGetTime 我知道它们都依赖于硬件 我正在测量一个可能需要大约 5 10 秒的过程 我的要求很
  • 如何在 Linux 中避免通过本地堆栈进行路由

    我有以下环境 2 台主机 每台主机都有 2 个相互连接的以太网接口 如下图所示 1 2 host1 host2 3 4 我想编写客户端 服务器套接字工具 它将在 host1 上打开客户端和服务器套接字 我希望客户端通过接口 1 发
  • 在 XCode 5 中使用 iOS 5 模拟器?

    XCode 5 似乎仅附带 iOS 6 和 7 模拟器 有没有办法将 iOS 5 模拟器与 XCode 5 一起使用 如果您在 OS X v10 8 下使用 Xcode 5 那么如果您转到 首选项 gt 下载 您应该会在 组件 下看到可用的
  • 如何锁定文件

    我有一个write应该将数据安全写入文件的方法 The current file I am writing to FileOutputStream file null Synchronized version private void wr
  • 如何使用 Rspec 测试 Sinatra 中的辅助块?

    我正在编写一个 sinatra 应用程序并使用 rspec 和rack test 对其进行测试 如 sinatrarb com 上所述 到目前为止 一切都很棒 直到我将一些相当程序化的代码从我的域对象移到 西纳特拉的帮手 从那时起 我一直试
  • 使用 Firebase OAuth 保护 RESTful API?

    我在 iOS 和 Web 应用程序中使用 Firebase 来处理用户身份验证 我需要确保用户已登录 然后才能向我的 API 发出任何请求 我如何使用 Firebase 完成这样的事情 我正在考虑使用 Kong https getkong
  • R 中的大型固定效应二项式回归

    我需要在一个相对较大的数据框架上运行逻辑回归 该数据框架包含 480 000 个条目和 3 个固定效应变量 固定效应 var A 有 3233 个级别 var B 有 2326 个级别 var C 有 811 个级别 所以总共我有 6370
  • RSS 项目描述中的图像

    我开始为我的新网站进行 RSS 开发 但我想知道是否有任何方法可以将图像放在项目的描述中 如下所示
  • 当视图已经加载时,如何使用出现的键盘打开视图?

    我有一个要求 在视图中有一个文本字段 当我想通过切换选项卡 基于选项卡的应用程序 打开视图时 第一次加载视图时会出现键盘 因为我调用了 loadview 方法 但是当我切换到 tab2 并再次切换到 tab1 时 不会调用加载视图 我希望每
  • 在银河系坐标中绘制热图

    EDIT 好吧 我想出了一个糟糕的方法来做到这一点 涉及scipy interpolate griddata 我有一些二维数组 130x360 形式的地图 数组中的每个值对应于该点的测量值 这些地图以赤经 ra 和赤纬 dec 形式提供 范
  • 如何获取字符串的第一个单词并将其转换为 int? jQuery

    我需要获取字符串中的第一个单词 并将其转换为整数 如何使用 jQuery 做到这一点 example 223 洛雷姆 伊普苏姆 多洛尔 I need 223 并且它必须转换为integer 任何帮助 将不胜感激 您可以根据任何字符 例如空格
  • iPad 上禁用嵌入式 Youtube 播放器中的全屏按钮

    在研究了 stackoverflow 上至少十个类似的问题后 我似乎偶然发现了一些新东西 最近 不确定具体什么时候停止工作 嵌入式 YouTube 视频上的全屏按钮在 iPad 上被禁用 如果我尝试单击它 则会弹出 您的浏览器不支持全屏 的
  • 使用 lerna 将自定义参数发送到 npm (yarn) 脚本

    我在尝试将参数传递给npm脚本与lerna 我有一个节点脚本 我想在工作区的每个包中运行它 lerna文档建议如下 scripts my script lerna exec node LERNA ROOT PATH scripts my s
  • Swift 一次删除多个对象 Parse 服务器

    我向服务器查询如下 let query PFQuery className posts query whereKey uuid equalTo Ncell uuidLbl text query findObjectsInBackground
  • 从远程 SQLite 数据库复制表?

    有没有办法将数据从一个远程 SQLite 数据库复制到另一个 我在两台服务器上完成了文件复制 但是 一些更改会记录在每个服务器本地的 SQLite 数据库中 为了使文件复制正常工作 我需要复制一个表的内容并将其输入到另一系统上的表中 我知道
  • 如何保护database.yml?

    在 Ruby on Rails 应用程序中 database yml 是一个存储数据库凭据的纯文本文件 当我部署 Rails 应用程序时 我的 Capistrano 中有一个部署后回调 在应用程序的 config 目录中创建到 databa
  • 如何在Ubuntu中生成核心转储文件[重复]

    这个问题在这里已经有答案了 我想知道如何在 Ubuntu 中生成核心转储文件 我使用的是 Ubuntu 8 04 1 和 gcc 编译器 4 2 3 我编写了一个简单的 C 程序来生成核心转储 我已经编译了该程序 如 gcc g badpo
  • 从 StructureMap 获取的 HttpContext 上的空用户

    好吧 我之前的问题 设置有太多变量 所以我将其精简为最基本的组件 给出使用 StructureMap3 的以下代码 IoC setup For