我有一个引用.NET Core类库的MVC核心应用程序。所有数据访问和业务逻辑都在类库中。如何从类库中访问经过身份验证的用户?
过去使用.NET Framework,您可以使用
string UserName = System.Web.HttpContext.Current.User.Identity.Name
从类库中的方法中获取用户名。在.NET Core中,HttpContext
似乎不再具有Current
或User
属性。
这是一个简单的用例。假设我有一个数据实体和服务"标记"在将它们保存到数据库之前具有日期和用户名的实体。
这些将在外部类库中:
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();
}
}
然后在我的MVC控制器中,我有一个调用该服务的帖子操作:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(MyEntity model)
{
await service.Create(model);
return RedirectToAction("Index")
}
我想在Auditor.Stamp
中替换该行,因为.NET Core中没有HttpContext.Current
,显然。
This post给出了如何在Core中获取用户名的示例:
public class UserResolverService
{
private readonly IHttpContextAccessor _context;
public UserResolverService(IHttpContextAccessor context)
{
_context = context;
}
public string GetUser()
{
return await _context.HttpContext.User?.Identity?.Name;
}
}
但我还有另一个版本的问题:如何从类库中获取IHttpContextAccessor
对象?
我的大部分搜索结果只涉及如何从MVC控制器方法中获取用户名的问题。在过去,我已经将User对象传递给每个服务的每个方法,但是这需要额外的输入 - 我宁愿有一些我可以输入的东西(可能在Startup中?)然后忘掉它。
我确实想要一些我可以模拟单元测试的东西,但老实说,我认为将System.Web.HttpContext.Current.User.Identity.Name包装在可以模拟的东西中非常简单。
答案 0 :(得分:0)
此版本仍然具有相同的灵活性。
注入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;
}
}
该服务将取决于新的抽象
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();
}
}
你也应该注射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.
}
然后你可以在根项目的启动中调用它
public void ConfigureServices(IServiceCollection services) {
//...
services.AddMyLibrary();
//...
}
通过这些抽象和使用DI应用程序部分是分离的,可以单独测试。