【ASP.NET Core】在Blazor中获取 HTTP 上下文信息

今天咱们来扯一下 Blazor 应用程序怎么访问 HttpContext。其实这句话有坑,为了避免大伙伴们掉茅坑,老周直接说明:Blazor 是不能访问 HttpContext 的。哪怕你在服务容器中注册了 IHttpContextAccessor 也不行,无法返回有效的上下文。

为啥?这得从 Blazor 的运行方式说起。银河系周知,Blazor 允许用 .NET 代码编写客户端逻辑,在某种程度(注意,是某种程度)上替代“奸商”……哦不,是 JS。Javascript 虽然语法上很像C某某,但和C某某比还是差得很远,代码阅读起来总感觉XYZ。尽管不能完全替换,但能实现一部分也是很好的。Blazor 主要是照顾像老周这种屌丝程序猴,一旦来项目了,一个人承担后台前台,连美工、图片处理都要一手包。所以用写后台代码的方式写前台更习惯。

上面一段全是废话!Blazor 只是在第一次运行的时候(不管是 Server 版还是 WASM 版)才会产生 HTTP 请求,之后的交互都由 Web Socket 来担任。往上说一层就是 SignalR。说直白一点就是,它只有第一次访问服务器才有 HttpContext,因为 Blazor 应用还没加载。一旦它加载成功了,后面就没 HttpContext 的事了。

结论:要取得 HttpContext 中需要的数据,只能在第一次请求时获得,然后你想尽办法让这些数据传给 Blazor 应用程序。本文老周将演示两种方法——基本能对付过去。

 方法一:级联参数

Blazor 需要一个HTML页面来承载,然后 Page 之间的切换都在这个HTML页面上进行。项目模板默认使用 Razor Pages—— 生成一个名为 _Host 的页面(当然你改用 MVC 的视图来加载也可以,一样的原理)。然后使用 component 标记帮助器来加载 Blazor 应用程序。Blazor 应用是通过组件来构建的,一般会有一个名为 App 的组件,充当根组件。

component type="typeof(App)" render-mode="ServerPrerendered" />

App 组件放个 Router,作用无非就是能找到 Blazor 应用页面就呈现,找不到就显示反馈。

Router AppAssembly="@typeof(App).Assembly">
    Found Context="routeData">
        RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    Found>
    NotFound>
        PageTitle>Not foundPageTitle>
        LayoutView Layout="@typeof(MainLayout)">
            p role="alert">Sorry, there's nothing at this address.p>
        LayoutView>
    NotFound>
Router>

其实,Blazor 页面元素就是显示在 RouteView 组件下面的。所以,要使用级联参数组件,就可以把 CascadingValue 组件作为 RouteView 父级。

咱们先看一下数据的传递顺序:

1、要读 HttpContext 对象里的东西,获取数据的代码得在 _Host 页面上写;

2、在用 component 组件加载 App 组件时通过参数把数据传递给 App 内部;

3、App 组件内将数据传递给 CascadingValue;

4、各个 Blazor 页面组件都能够从 CascadingValue 组件中获取到数据。

HttpContext --> _Host.cshtml --> App.razor --> CascadingValue  --> XXX

下面是实现过程:(假设我们要获取 URL 查询参数)

1、在 App 组件中定义名为 Version 的属性,字符串类型。要应用 Parameter 特性,说明它是一个组件参数,在 component 标记帮助器中可以传参。

2、把 RouteView 组件放到 CascadingValue 组件下面,并让它的值(Value)引用 Version 属性的值。

// App.razor 文件

Router AppAssembly="@typeof(App).Assembly">
    Found Context="routeData">
        CascadingValue Name="ver" TValue="string" Value="@Version">
            RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        CascadingValue>
    Found>
    NotFound>
        PageTitle>Not foundPageTitle>
        LayoutView Layout="@typeof(MainLayout)">
            p role="alert">Sorry, there's nothing at this address.p>
        LayoutView>
    NotFound>
Router>

@code
{
    [Parameter]
    public string? Version { get; set; }
}

3、在 _Host.cshtml 文件中(或你自己定义的 MVC 视图文件)中,读取 URL 查询中的 “v” 字段的值,然后通过 component 组件把值传递 App 组件。

……
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = "_Layout";

    // 从URL参数中读参数
    if(!HttpContext.Request.Query.TryGetValue("v",out var value))
    {
        value = "0.0.0";
    }
}

component type="typeof(App)" render-mode="ServerPrerendered" param-Version="@value.ToString()" />

用 component 组件传递参数,可以用 param-* 特性,后面的 * 表示 App 组件接收参数的成员名称,这里是我们前面定义的 Version 属性,直接写成 param-Version 即可。如果属性名为 Name,那就写成 param-Name。

4、假设现在 Index.razor 组件要使用数据。需要在 Index 组件中定义一个属性成员,一定要应用 CascadingParameter 特性。注意这里 Name = "ver"。这个名字和刚才 App 组件中 CascadingValue 的 Name 是匹配的。

p>接收到的数据:@Datap>

@code
{
    [CascadingParameter(Name = "ver")]
    public string? Data{ get; set; }
}

要呈现 Data 属性的内容,只需在 HTML 中引用即可。

在运行程序后,访问时加上 v=5.0.3,这个字段的值就能传到 Index 组件中。

【ASP.NET Core】在Blazor中获取 HTTP 上下文信息插图

 

 

二、单实例服务

这个方案是运用了依赖注入的功能,咱们定义一个类,它的属性用于存储参数。

    public class PassDataService
    {
        public int Key1 { get; set; }
        public string? Key2 { get; set; }
    }

然后把它注册为单实例服务。

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
……
builder.Services.AddSingleton();

var app = builder.Build();

在 _Host.cshtml 文件或你自定义的 MVC 视图文件中,读出 HttpContext 中数据,然后设置到 PassDataService 实例的属性上。

@page "/"
@namespace TestApp.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@inject PassDataService _datasv;
@{
    Layout = "_Layout";

    // 读取URL查询
    if(!HttpContext.Request.Query.TryGetValue("key1", out var kVal1))
    {
        kVal1 = "0";
    }
    if(!HttpContext.Request.Query.TryGetValue("key2", out var kVal2))
    {
        kVal2 = string.Empty;
    }
    // 赋值
    _datasv.Key1 = int.TryParse(kVal1, out int k1) ? k1 : 0;
    _datasv.Key2 = kVal2;
}

"typeof(App)" render-mode="ServerPrerendered" />

@inject 指令可以注入需要的服务实例。

@inject 类型> 变量名>

 

在 Index 组件中,只要注入 PassDataService 服务实例,就能获取到数据。

@page "/"
@inject PassDataService _datasv

PageTitle>IndexPageTitle>

h1>Hello, world!h1>

p>获取到的数据:p>
div>Key1 = @_datasv.Key1div>
div>Key2 = @_datasv.Key2div>

运行应用程序,在 URL 后面加上 key1 和 key2 参数。就得到传递的数据。

【ASP.NET Core】在Blazor中获取 HTTP 上下文信息插图1

 

文章来源于互联网:【ASP.NET Core】在Blazor中获取 HTTP 上下文信息

THE END
分享
二维码