• 【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 found</PageTitle>
            <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 found</PageTitle>
            <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>接收到的数据:@Data</p>
    
    @code
    {
        [CascadingParameter(Name = "ver")]
        public string? Data{ get; set; }
    }

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

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

    二、单实例服务

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

        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<PassDataService>();
    
    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;
    }
    
    <component type="typeof(App)" render-mode="ServerPrerendered" />

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

    @inject <类型> <变量名>

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

    @page "/"
    @inject PassDataService _datasv
    
    <PageTitle>Index</PageTitle>
    
    <h1>Hello, world!</h1>
    
    <p>获取到的数据:</p>
    <div>Key1 = @_datasv.Key1</div>
    <div>Key2 = @_datasv.Key2</div>

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

  • 相关阅读:
    呵呵,23号了,难道要通宵???
    开会效果不错
    开完会罗,明天离开这上有点奢华的地方:)
    感冒了,感觉糟透了。。。
    你好,深圳!你好,2010!
    上班 第一天
    。。轻。。。
    那天我生日
    英雄七十寿 无物下冷酒 横刀上闹市 直取数人头
    李孝利 唠叨
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/16654364.html
Copyright © 2020-2023  润新知