有啥方法可以通过“http 请求”和通过 DI 在 Blazor 组件中的“DI 范围”中为每个用户提供持久性?

Posted

技术标签:

【中文标题】有啥方法可以通过“http 请求”和通过 DI 在 Blazor 组件中的“DI 范围”中为每个用户提供持久性?【英文标题】:is there any way to have persistence per user in a "DI scope" both through "http request" and in blazor components via DI?有什么方法可以通过“http 请求”和通过 DI 在 Blazor 组件中的“DI 范围”中为每个用户提供持久性? 【发布时间】:2021-09-23 04:37:13 【问题描述】:

我通常使用“Windows 身份验证”并且总是有一个带有记录用户信息的 HTTPContext。

我观察到“PerScope”DI 并不总是保留,尤其是在向 Blazor 组件(Blazor 服务器)请求“Per Scope”服务时。

我通过创建一个带有由用户名缓存的范围字典的单例来解决这个问题。

    public class PerUserPersistentScopeService
            
                private readonly MemoryCache _usersCache;
                private readonly IServiceScopeFactory _serviceScopeFactory;
                
                public PerUserPersistentScopeService(IServiceScopeFactory scopeFactory)
                
                    _serviceScopeFactory = scopeFactory;
                    _usersCache = new MemoryCache(new MemoryCacheOptions()ExpirationScanFrequency = new TimeSpan(0,5,0));
                
...
               public T GetService<T>()
                  using here httpcontext.user.identity.name to obtain
                         a per use service
               

有了这个单例,我总是得到每个用户范围的 GetService。

但是

现在,当我使用 Blazor 组件(服务器端)时,IIS 服务器(不是 IIS Express)不会在 Windows 身份验证下返回 HTTPContext 中的用户身份。

一些组件非常通用(例如:导航面板等),而另一些则非常具体,以至于级联如此多复杂的每个用户实体是不切实际的。

有没有办法让通用 Scoped DI 重新在 blazor 页面和组件上工作?

或者它也可以工作......

是否有任何其他方式来获取(不使用级联参数)为其提供 blazor 组件或 blazor 页面的用户的名称?

【问题讨论】:

缺乏真实的例子。 【参考方案1】:
    创建可序列化的 ContextIdentity 类 (类必须有一个空的构造函数)
[Serializable] public class ContextIdentity public string Name get; set; public string AuthenticationType get; set; public bool IsAuthenticated get; set; public string Domain get; set; public string Account get; set; public ContextIdentity() public ContextIdentity(string name, string authenticationType, bool isAuthenticated) Name = name; AuthenticationType = authenticationType; IsAuthenticated = isAuthenticated; if (isAuthenticated) var sp = Name.Split('&bsol;&bsol;'); if (sp.Length == 2) Domain = sp[0]; Account = sp[1]; else Domain = ""; Account = Name; public ContextIdentity(IIdentity identity): this(identity?.Name,identity?.AuthenticationType, identity?.IsAuthenticated??false)
    创建一个名为 ContextIdentityProvider 的范围服务
public class ContextIdentityProvider public ContextIdentity Identity get; set;
    编辑 App.razor 以注入身份
@inject ContextIdentityProvider contextIdentityProvider
&lt;CascadingAuthenticationState&gt;
    &lt;Router AppAssembly="@typeof(Program).Assembly" 
    PreferExactMatches="@true"&gt;
    &lt;Found Context="routeData"&gt;
    &lt;AuthorizeRouteView RouteData="@routeData" 
    DefaultLayout="@typeof(MainLayout)" /&gt;
    &lt;/Found&gt;
    &lt;NotFound&gt;
        &lt;LayoutView Layout="@typeof(MainLayout)"&gt;
            &lt;p&gt;Sorry, there's nothing at this 
            address.&lt;/p&gt;
        &lt;/LayoutView&gt;
    &lt;/NotFound&gt;
    &lt;/Router&gt;
&lt;/CascadingAuthenticationState&gt;
@code
    &#91;Parameter&#93;
    public ContextIdentity ContextIdentity  get; set; 
    protected override void OnInitialized()
    
        base.OnInitialized();
        contextIdentityProvider.Identity = ContextIdentity;
    

    要将 ContextIdentity 跨不同选项卡持久化以供相同用途,请为同一用户创建 SCOPE 的 SINGLETON 存储库服务。
public class PerUserPersistentScopeService private readonly MemoryCache _usersCache; private readonly IServiceScopeFactory _serviceScopeFactory; public PerUserPersistentScopeService(IServiceScopeFactory scopeFactory) _serviceScopeFactory = scopeFactory; _usersCache = new MemoryCache( new MemoryCacheOptions() ExpirationScanFrequency = new TimeSpan(0,5,0) ); public IServiceProvider GetProvider(ContextIdentity identity) if (identity is null) return null; if (!identity.IsAuthenticated) return null; var serviceScope = _usersCache.GetOrCreate(identity.Name, entry => entry.SlidingExpiration = new TimeSpan(0, 15, 0); var e=_serviceScopeFactory.CreateScope(); var newidentity = e.ServiceProvider.GetService(); newidentity.Identity = identity; return e; ); return serviceScope.ServiceProvider; public T GetService(ContextIdentity identity) where T:class var p = GetProvider(identity); if (p is null) return null; return p.GetService();

注意:在创建新的范围服务提供者时,您必须将 contextidentity 重新注入到新的范围中。

【讨论】:

以上是关于有啥方法可以通过“http 请求”和通过 DI 在 Blazor 组件中的“DI 范围”中为每个用户提供持久性?的主要内容,如果未能解决你的问题,请参考以下文章

PUT 和 DELETE HTTP 请求方法有啥用处?

jmeter http请求中的httpclient4和java有啥区别

@RequestBody 和 @RequestParam 有啥区别?

fiddler断点测试修改响应指令有啥作用

春季请求和会话范围有啥区别?

uni,mono有啥区别?di,bi有啥区别?除了their origins