级联参数 Task<AthenticationState> 如何在 Blazor WASM 的 AuthorizeView 和 AuthorizedRouteView 中展开并暴露为 "context"?
How does cascaded parameter Task<AthenticationState> get unwrapped and exposed as "context" in AuthorizeView and AuthorizedRouteView in Blazor WASM?
在 AuthorizeView 和 AuthorizedRouteView 组件中有一个名为“context”的 AuthenticationState 类型的对象。此对象允许通过 context.User.
访问 ClaimsPrincipal
我可以在源代码中看到,AuthenticationState 通过在 CascadingAuthenticationState 组件中实现的 CascadingValue 组件作为任务传递给这些组件(该组件又在 App.razor 中的组件层次结构的顶部定义) ).
但是,当我检查 AuthorizeRouteView 的源时,我可以看到名为 ExistingCascadedAuthenticationState 的任务类型的级联参数。然而,对我来说,任务如何以及在何处被解包并作为“上下文”公开,这完全是个谜。有人知道答案吗?
级联参数为Task<AuthenticationState> context
这告诉您 context
,当等待时,将 return AuthenticationState 类型的对象。所以你得到的是一个任务。任务等待时,returns 值 return 由任务编辑。访问用户的实际语法是
var state = await context;
var user = state.User;
此外,您可以为级联参数指定任何名称。所以
[CascadingParameter]
public Task<AuthenticationState> AuthState {get;set;}
var state = await AuthState;
var user = state.User;
同样有效。
enet 的评论帮助我找到了答案。
当我们有一个 RenderFragment<TValue>
委托时,<TValue>
默认公开为 @context
。
比如在AuthorizeRouteView
中我们有一个参数NotAuthorized
:
[Parameter]
public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
在这种情况下 AuthenticationState
是 TValue
,因此 AuthenticationState
暴露为 @context
。
这篇关于 Blazor 大学的文章是我理解这个概念的关键:Passing placeholders to RenderFragments。
编辑 2022-05-29
@somedotnetguy 最近添加的答案使渲染模板的工作原理更加清晰。我建议还阅读他的回答以获得更完整的图片。
需要深入挖掘,有点复杂。
AuthorizeView
继承自 AuthorizeViewCore
AuthorizedRouteView
构建自己的 AuthorizeRouteViewCore
继承自 AuthorizeViewCore
.
AuthorizedRouteView
底部的代码:
private sealed class AuthorizeRouteViewCore : AuthorizeViewCore
{
[Parameter]
public RouteData RouteData { get; set; } = default!;
protected override IAuthorizeData[]? GetAuthorizeData()
=> AttributeAuthorizeDataCache.GetAuthorizeDataForType(RouteData.PageType);
}
AuthorizedRouteView
将任何级联捕获到 ExistingCascadedAuthenticationState
。如果一个存在(不为空),则 CascadingAuthenticationState
存在于 App
中,因此无需再做任何事情。如果它为 null,则它会将 CascadingAuthenticationState
作为组件根组件添加到其渲染片段中。这保证了 Task<AuthenticationState>
是级联的。
AuthorizeViewCore
捕获级联值:
[CascadingParameter] private Task<AuthenticationState>? AuthenticationState { get; set; }
它在 OnParametersSetAsync
中“展开”
currentAuthenticationState = await AuthenticationState;
isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User);
并在 BuildRenderTree
中用于您看到的“上下文”。
var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(currentAuthenticationState!));
内容来自:
RenderFragment<TValue>
声明如下,其中 TValue
- content
- 声明为 AuthenticationState
:
[Parameter] public RenderFragment<AuthenticationState>? Authorized { get; set; }
我也在想同样的事情:
怎么会突然在组件的内部标记中(在其开始和结束标记之间,当它在父级中使用时)我们可以写 @context
值从何而来?
提供的答案帮助我弄明白了,Blazor University - RenderFragements (entire chapter, this page and the 4 following)
上有很好的解释
正如在官方文档 here and here 中看到的那样,AuthorizeView
有一个 属性 ChildContent
类型 RenderFragment<AuthenticationState>
装饰着 Parameter
。
[Microsoft.AspNetCore.Components.Parameter]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.Authorization.AuthenticationState>?
ChildContent { get; set; }
我来了:
您不能使用普通(空的、新创建的)组件执行此操作:
//Parent.razor consumes MyComp
<MyComp>Some markup as content for MyComp</MyComp>
FRAMEWORK 约定是,您需要在组件 (class) 定义中使用以下代码,以便能够在标签之间编写标记(当它被使用时)。然后标记作为 RenderFragment 类型的 ChildComponent 属性 传递:
//MyComp.razor
[Parameter]
public RenderFragment ChildContent { get; set; }
您还可以将 properties/fields 类型的 RenderFragment 或 RenderFragment 与其他 property-names 一起使用,但它们不会像上面那样从父级自动填充。但是,您可以根据需要在组件定义中使用它们。要在任何 RenderFragment 的消费父级中设置内容,您可以使用其 属性 名称(如 html 标记)并在其中写入剃须刀内容。这里的例外是,如果您只想填充 ChildContent 而不想使用其他 RenderFragment,则可以像上面那样省略它(因为这是一个特殊的名称)。
//MyComp.razor
<p>Fragment1:</p>
@SomeOtherRenderFragment
<p>Fragment2:</p>
@SomeGenericRenderFragment(_instanceT)
@code {
[Parameter]
public RenderFragment SomeOtherRenderFragment{ get; set; } =
@<h1>My Render Fragment Example</h1>;
[Parameter]
public RenderFragment<T> SomeGenericRenderFragment{ get; set; } // not set here in this example
private T _instanceT = null // T must be an explicit type, it's a placeholder in this example... for instance change it to 'string'. You can get the instance from where ever you want, probably through some service that you inject with DI
}
//Parent.razor consumes MyComp
// Implicit setting ChildContent
<MyComp>Some markup as content for MyComp</MyComp>
// Explicit setting ChildContent
<MyComp><ChildContent>Some markup as content for MyComp</ChildContent></MyComp>
// Explicit setting various RenderFragments
<MyComp>
<ChildContent>Some markup as content for MyComp</ChildContent>
<SomeOtherRenderFragment>SomeContent</SomeOtherRenderFragment>
<SomeGenericRenderFragment>SomeContent with @context</SomeGenericRenderFragment>
</MyComp>
现在把它们放在一起。您还可以使用通用 RenderFragment 作为 convention-property ChildContent 的类型。但是,您有责任在组件定义中提供通用 class 的实例。
//MyComp.razor
<p>Content passed from parent:</p>
@ChildContent(_instanceT1)
<p>Content passed from parent a second time:</p>
@ChildContent(_instanceT2)
@code {
[Parameter]
public RenderFragment<T> ChildContent{ get; set; }
private string _instanceT1 = "Hello World!";
private string _instanceT2 = "Good Night!";
}
家长:
//Parent.razor
<h1>Parent:</h1>
<MyComp>
<p>I can type content here and now comes the value of T: @context</p>
<p>and again again: @context</p>
</MyComp>
最终 Html 看起来像这样:
<h1>Parent:</h1>
<p>Content passed from parent:</p>
<p>I can type content here and now comes the value of T: Hello World!</p>
<p>and again again: Hello World!</p>
<p>Content passed from parent a second time:</p>
<p>I can type content here and now comes the value of T: Good Night!</p>
<p>and again again: Good Night!</p>
注意:@context
仅在组件的标签内可用(使用时),如果它在其定义中具有此通用 RenderFragment 属性:
//MyComp.razor
[Parameter]
public RenderFragment<T> ChildContent{ get; set; }
AuthorizeView 可以使用 RenderFragments:
- ChildContent(最常使用隐式,也可能是显式)
- 授权(明确)
- 未授权(明确)
- 然而它是这样实现的,如果两者都指定则抛出异常:未处理的异常渲染组件:不要同时指定'Authorized'和'ChildContent'。基本上授权替代了 ChildContent,或者换句话说,当没有明确设置任何 RenderFragment 时,ChildContent 被视为授权,如 MrC aka Shaun Curtis 的答案所示。
最后的话:我希望这会有所帮助,我希望我将错别字控制在最低限度:)
在 AuthorizeView 和 AuthorizedRouteView 组件中有一个名为“context”的 AuthenticationState 类型的对象。此对象允许通过 context.User.
访问 ClaimsPrincipal我可以在源代码中看到,AuthenticationState 通过在 CascadingAuthenticationState 组件中实现的 CascadingValue 组件作为任务传递给这些组件(该组件又在 App.razor 中的组件层次结构的顶部定义) ).
但是,当我检查 AuthorizeRouteView 的源时,我可以看到名为 ExistingCascadedAuthenticationState 的任务类型的级联参数。然而,对我来说,任务如何以及在何处被解包并作为“上下文”公开,这完全是个谜。有人知道答案吗?
级联参数为Task<AuthenticationState> context
这告诉您 context
,当等待时,将 return AuthenticationState 类型的对象。所以你得到的是一个任务。任务等待时,returns 值 return 由任务编辑。访问用户的实际语法是
var state = await context;
var user = state.User;
此外,您可以为级联参数指定任何名称。所以
[CascadingParameter]
public Task<AuthenticationState> AuthState {get;set;}
var state = await AuthState;
var user = state.User;
同样有效。
enet 的评论帮助我找到了答案。
当我们有一个 RenderFragment<TValue>
委托时,<TValue>
默认公开为 @context
。
比如在AuthorizeRouteView
中我们有一个参数NotAuthorized
:
[Parameter]
public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
在这种情况下 AuthenticationState
是 TValue
,因此 AuthenticationState
暴露为 @context
。
这篇关于 Blazor 大学的文章是我理解这个概念的关键:Passing placeholders to RenderFragments。
编辑 2022-05-29
@somedotnetguy 最近添加的答案使渲染模板的工作原理更加清晰。我建议还阅读他的回答以获得更完整的图片。
需要深入挖掘,有点复杂。
AuthorizeView
继承自AuthorizeViewCore
AuthorizedRouteView
构建自己的AuthorizeRouteViewCore
继承自AuthorizeViewCore
.
AuthorizedRouteView
底部的代码:
private sealed class AuthorizeRouteViewCore : AuthorizeViewCore
{
[Parameter]
public RouteData RouteData { get; set; } = default!;
protected override IAuthorizeData[]? GetAuthorizeData()
=> AttributeAuthorizeDataCache.GetAuthorizeDataForType(RouteData.PageType);
}
AuthorizedRouteView
将任何级联捕获到 ExistingCascadedAuthenticationState
。如果一个存在(不为空),则 CascadingAuthenticationState
存在于 App
中,因此无需再做任何事情。如果它为 null,则它会将 CascadingAuthenticationState
作为组件根组件添加到其渲染片段中。这保证了 Task<AuthenticationState>
是级联的。
AuthorizeViewCore
捕获级联值:
[CascadingParameter] private Task<AuthenticationState>? AuthenticationState { get; set; }
它在 OnParametersSetAsync
currentAuthenticationState = await AuthenticationState;
isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User);
并在 BuildRenderTree
中用于您看到的“上下文”。
var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(currentAuthenticationState!));
内容来自:
RenderFragment<TValue>
声明如下,其中 TValue
- content
- 声明为 AuthenticationState
:
[Parameter] public RenderFragment<AuthenticationState>? Authorized { get; set; }
我也在想同样的事情:
怎么会突然在组件的内部标记中(在其开始和结束标记之间,当它在父级中使用时)我们可以写 @context
值从何而来?
提供的答案帮助我弄明白了,Blazor University - RenderFragements (entire chapter, this page and the 4 following)
上有很好的解释正如在官方文档 here and here 中看到的那样,AuthorizeView
有一个 属性 ChildContent
类型 RenderFragment<AuthenticationState>
装饰着 Parameter
。
[Microsoft.AspNetCore.Components.Parameter] public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.Authorization.AuthenticationState>? ChildContent { get; set; }
我来了:
您不能使用普通(空的、新创建的)组件执行此操作:
//Parent.razor consumes MyComp
<MyComp>Some markup as content for MyComp</MyComp>
FRAMEWORK 约定是,您需要在组件 (class) 定义中使用以下代码,以便能够在标签之间编写标记(当它被使用时)。然后标记作为 RenderFragment 类型的 ChildComponent 属性 传递:
//MyComp.razor
[Parameter]
public RenderFragment ChildContent { get; set; }
您还可以将 properties/fields 类型的 RenderFragment 或 RenderFragment 与其他 property-names 一起使用,但它们不会像上面那样从父级自动填充。但是,您可以根据需要在组件定义中使用它们。要在任何 RenderFragment 的消费父级中设置内容,您可以使用其 属性 名称(如 html 标记)并在其中写入剃须刀内容。这里的例外是,如果您只想填充 ChildContent 而不想使用其他 RenderFragment,则可以像上面那样省略它(因为这是一个特殊的名称)。
//MyComp.razor
<p>Fragment1:</p>
@SomeOtherRenderFragment
<p>Fragment2:</p>
@SomeGenericRenderFragment(_instanceT)
@code {
[Parameter]
public RenderFragment SomeOtherRenderFragment{ get; set; } =
@<h1>My Render Fragment Example</h1>;
[Parameter]
public RenderFragment<T> SomeGenericRenderFragment{ get; set; } // not set here in this example
private T _instanceT = null // T must be an explicit type, it's a placeholder in this example... for instance change it to 'string'. You can get the instance from where ever you want, probably through some service that you inject with DI
}
//Parent.razor consumes MyComp
// Implicit setting ChildContent
<MyComp>Some markup as content for MyComp</MyComp>
// Explicit setting ChildContent
<MyComp><ChildContent>Some markup as content for MyComp</ChildContent></MyComp>
// Explicit setting various RenderFragments
<MyComp>
<ChildContent>Some markup as content for MyComp</ChildContent>
<SomeOtherRenderFragment>SomeContent</SomeOtherRenderFragment>
<SomeGenericRenderFragment>SomeContent with @context</SomeGenericRenderFragment>
</MyComp>
现在把它们放在一起。您还可以使用通用 RenderFragment 作为 convention-property ChildContent 的类型。但是,您有责任在组件定义中提供通用 class 的实例。
//MyComp.razor
<p>Content passed from parent:</p>
@ChildContent(_instanceT1)
<p>Content passed from parent a second time:</p>
@ChildContent(_instanceT2)
@code {
[Parameter]
public RenderFragment<T> ChildContent{ get; set; }
private string _instanceT1 = "Hello World!";
private string _instanceT2 = "Good Night!";
}
家长:
//Parent.razor
<h1>Parent:</h1>
<MyComp>
<p>I can type content here and now comes the value of T: @context</p>
<p>and again again: @context</p>
</MyComp>
最终 Html 看起来像这样:
<h1>Parent:</h1>
<p>Content passed from parent:</p>
<p>I can type content here and now comes the value of T: Hello World!</p>
<p>and again again: Hello World!</p>
<p>Content passed from parent a second time:</p>
<p>I can type content here and now comes the value of T: Good Night!</p>
<p>and again again: Good Night!</p>
注意:@context
仅在组件的标签内可用(使用时),如果它在其定义中具有此通用 RenderFragment 属性:
//MyComp.razor
[Parameter]
public RenderFragment<T> ChildContent{ get; set; }
AuthorizeView 可以使用 RenderFragments:
- ChildContent(最常使用隐式,也可能是显式)
- 授权(明确)
- 未授权(明确)
- 然而它是这样实现的,如果两者都指定则抛出异常:未处理的异常渲染组件:不要同时指定'Authorized'和'ChildContent'。基本上授权替代了 ChildContent,或者换句话说,当没有明确设置任何 RenderFragment 时,ChildContent 被视为授权,如 MrC aka Shaun Curtis 的答案所示。
最后的话:我希望这会有所帮助,我希望我将错别字控制在最低限度:)