Ale*_*man 7 c# asp.net-core asp.net-core-middleware
我使用ASP.NET Core 2.1并希望User
在服务级别获取.
我已经看到了HttpContextAccessor
注入某个服务然后我们获取当前User
通道的示例UserManager
var user = await _userManager.GetUserAsync(accessor.HttpContext.User);
Run Code Online (Sandbox Code Playgroud)
或在控制器中
var user = await _userManager.GetUserAsync(User);
Run Code Online (Sandbox Code Playgroud)
问题:
注入HttpContextAccessor
服务似乎是错误的 - 仅仅因为我们违反了SRP并且服务层没有被隔离(它依赖于http上下文).
我们当然可以在控制器中获取用户(一种更好的方法),但我们面临两难 - 我们根本不想User
在每个服务方法中作为参数传递
我花了几个小时思考如何最好地实现它,并提出了一个解决方案.我不完全确定我的方法是否足够,并且不违反任何软件设计原则.
共享我的代码希望从StackOverflow社区获得建议.
这个想法如下:
首先,我介绍SessionProvider
哪些注册为Singleton.
services.AddSingleton<SessionProvider>();
Run Code Online (Sandbox Code Playgroud)
SessionProvider
具有Session
用于保持属性User
,Tenant
等等.
其次,我介绍SessionMiddleware
并注册它
app.UseMiddleware<SessionMiddleware>();
Run Code Online (Sandbox Code Playgroud)
在Invoke
我解决的方法中HttpContext
,SessionProvider
&UserManager
.
我拿 User
然后我初始化单身人士的Session
属性ServiceProvider
:
sessionProvider.Initialise(user);
在这个阶段ServiceProvider
,Session
对象包含我们需要的信息.
现在我们注入SessionProvider
任何服务,它的Session
对象就可以使用了.
码:
SessionProvider
:
public class SessionProvider
{
public Session Session;
public SessionProvider()
{
Session = new Session();
}
public void Initialise(ApplicationUser user)
{
Session.User = user;
Session.UserId = user.Id;
Session.Tenant = user.Tenant;
Session.TenantId = user.TenantId;
Session.Subdomain = user.Tenant.HostName;
}
}
Run Code Online (Sandbox Code Playgroud)
Session
:
public class Session
{
public ApplicationUser User { get; set; }
public Tenant Tenant { get; set; }
public long? UserId { get; set; }
public int? TenantId { get; set; }
public string Subdomain { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
SessionMiddleware
:
public class SessionMiddleware
{
private readonly RequestDelegate next;
public SessionMiddleware(RequestDelegate next)
{
this.next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(
HttpContext context,
SessionProvider sessionProvider,
MultiTenancyUserManager<ApplicationUser> userManager
)
{
await next(context);
var user = await userManager.GetUserAsync(context.User);
if (user != null)
{
sessionProvider.Initialise(user);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在服务层代码:
public class BaseService
{
public readonly AppDbContext Context;
public Session Session;
public BaseService(
AppDbContext context,
SessionProvider sessionProvider
)
{
Context = context;
Session = sessionProvider.Session;
}
}
Run Code Online (Sandbox Code Playgroud)
所以这是任何服务的基类,因为你可以看到我们现在可以Session
轻松获取对象并且可以使用了:
public class VocabularyService : BaseService, IVocabularyService
{
private readonly IVocabularyHighPerformanceService _vocabularyHighPerformanceService;
private readonly IMapper _mapper;
public VocabularyService(
AppDbContext context,
IVocabularyHighPerformanceService vocabularyHighPerformanceService,
SessionProvider sessionProvider,
IMapper mapper
) : base(
context,
sessionProvider
)
{
_vocabularyHighPerformanceService = vocabularyHighPerformanceService;
_mapper = mapper;
}
public async Task<List<VocabularyDto>> GetAll()
{
List<VocabularyDto> dtos = _vocabularyHighPerformanceService.GetAll(Session.TenantId.Value);
dtos = dtos.OrderBy(x => x.Name).ToList();
return await Task.FromResult(dtos);
}
}
Run Code Online (Sandbox Code Playgroud)
关注以下几点:
.GetAll(Session.TenantId.Value);
Run Code Online (Sandbox Code Playgroud)
此外,我们可以轻松获得当前用户
Session.UserId.Value
Run Code Online (Sandbox Code Playgroud)
要么
Session.User
Run Code Online (Sandbox Code Playgroud)
就是这样了.
我测试了我的代码,当打开几个选项卡时它运行良好 - 每个选项卡在url中有不同的子域(租户是从子域解析的 - 数据正在被正确获取).
使用动作过滤器可以确保在动作调用管道中调用所需的行为,以便已经实现必要的依赖关系(如HttpContext.User)
实现异步操作过滤器以避免调用.Result
阻塞调用,因为它可能导致请求管道中的死锁.
public class SessionFilter : IAsyncActionFilter {
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next) {
// do something before the action executes
var serviceProvider = context.HttpContext.RequestServices;
var sessionProvider = serviceProvider.GetService<SessionProvider>();
var userManager = serviceProvider.GetService<MultiTenancyUserManager<ApplicationUser>>()
var user = await userManager.GetUserAsync(context.HttpContext.User);
if (user != null) {
sessionProvider.Initialise(user);
}
//execute action
var resultContext = await next();
// do something after the action executes; resultContext.Result will be set
//...
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1572 次 |
最近记录: |