如何从 .Net Core 中的单独类库中获取经过身份验证的用户

Mat*_*hew 5 c# asp.net-mvc asp.net-core-mvc .net-core asp.net-core

我有一个引用 .NET Core 类库的 MVC 核心应用程序。所有的数据访问和业务逻辑都在类库中。如何从类库访问经过身份验证的用户?

过去使用 .NET Framework 您可以使用

string UserName = System.Web.HttpContext.Current.User.Identity.Name
Run Code Online (Sandbox Code Playgroud)

从类库中的方法内部获取用户名。在 .NET Core 中,似乎HttpContext不再具有CurrentorUser属性。

这是一个简单的用例。假设我有一个数据实体和服务,它在将实体保存到数据库之前用日期和用户名“标记”实体。

这些将在外部类库中:

public interface IAuditable{
    DateTime CreateDate{get;set;}
    string UserName{get;set;}
}

public class MyEntity:IAuditable{
    public int ID{get;set;}
    public string Name{get;set;}
    public string Information{get;set;}
}

public static class Auditor{
    public static IAuditable Stamp(IAuditable model){            
        model.CreateDate=DateTime.UtcNow;
        model.CreatedBy=System.Web.HttpContext.Current.User.Identity.Name;
        return model;
    }
}

public sealed class MyService:IDisposable{
    MyDb db=new MyDb();
    public async Task<int> Create(MyEntity model){
        Auditor.Stamp(model);
        db.MyEntities.Add(model);
        return await db.SaveAsync();
    }
    public void Dispose(){
        db.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的 MVC 控制器中,我有一个调用服务的 post 操作:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(MyEntity model)
{
    await service.Create(model);
    return RedirectToAction("Index")
}
Run Code Online (Sandbox Code Playgroud)

我想要一种方法来替换该行,Auditor.Stamp因为HttpContext.Current.NET Core 显然没有。

这篇文章给出了如何在 Core 中获取用户名的示例:

public class UserResolverService  
{
    private readonly IHttpContextAccessor _context;
    public UserResolverService(IHttpContextAccessor context)
    {
        _context = context;
    }

    public string GetUser()
    {
       return await _context.HttpContext.User?.Identity?.Name;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我遇到了相同问题的另一个版本:如何IHttpContextAccessor从类库中获取对象?

我的大多数搜索结果只处理如何从 MVC 控制器方法内部获取用户名的问题。过去,我已经将一个 User 对象传递给每个服务的每个方法,但这是很多额外的输入——我宁愿有一些我可以输入一次的东西(也许在 Startup 中?)然后忘记它。

我确实想要一些可以模拟单元测试的东西,但老实说,我认为将 System.Web.HttpContext.Current.User.Identity.Name 包装在可以模拟的东西中非常容易。

Nko*_*osi 4

此版本中仍然存在相同的灵活性。

注入IHttpContextAccessor,您就可以访问您需要的内容。

重构静态Auditor使其更易于注入

public interface IAuditor {
    IAuditable Stamp(IAuditable model);
}

public class Auditor : IAuditor {
    private readonly IHttpContextAccessor accessor;

    public Auditor(IHttpContextAccessor accessor) {
        this.accessor = accessor;
    }

    public IAuditable Stamp(IAuditable model){            
        model.CreateDate = DateTime.UtcNow;
        model.CreatedBy = accessor.HttpContext.User?.Identity?.Name;
        return model;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,服务将依赖于新的抽象

public interface IMyService : IDisposable {
    Task<int> Create(MyEntity model);
}

public sealed class MyService : IMyService {
    MyDb db = new MyDb();
    private readonly IAuditor auditor;

    public MyService(IAuditor auditor) {
        this.auditor = auditor;
    }

    public async Task<int> Create(MyEntity model) {
        auditor.Stamp(model);
        db.MyEntities.Add(model);
        return await db.SaveAsync();
    }

    public void Dispose() {
        db.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

您确实也应该注入,MyDb但这超出了当前问题的范围。

最后,您将库配置为能够在启动期间设置服务

public static IServiceCollection AddMyLibrary(this IServiceCollection services) {

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddSingleton<IAuditor, Auditor>();  
    services.AddScoped<IMyService, MyService>();

    //...add other services as needed

    return services.  
}
Run Code Online (Sandbox Code Playgroud)

然后你可以在从根项目启动中调用它

public void ConfigureServices(IServiceCollection services) {

    //...

    services.AddMyLibrary();

    //...
}
Run Code Online (Sandbox Code Playgroud)

通过这些抽象和使用 DI 应用程序部件是解耦的,并且可以单独测试。