您当前的位置: 首页 >  服务器

寒冰屋

暂无认证

  • 1浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

构建Blazor WASM和服务器一体化解决方案

寒冰屋 发布时间:2021-08-24 15:27:22 ,浏览量:1

目录

代码库

解决方案和项目

Blazor项目变更

MainLayout

导航菜单

Blazor.Web

Index.cshtml

Startup.cs

运行应用程序

添加数据服务

WeatherForecast.cs

IWeatherForecastService.cs

WeatherForecastServerService.cs

WeatherForecastAPIService.cs

WeatherForecastController.cs

Blazor项目Program.cs

Blazor.Web Startup.cs

构建并运行项目

它是如何工作的?

总结

​​​​​​​

https://shauncurtis.github.io/siteimages/Articles/AllinOne/Screenshot.png

代码库

文章的代码库在这里 - https://github.com/ShaunCurtis/AllinOne

解决方案和项目

使用Blazor WebAssembly模板创建一个名为Blazor的新解决方案。不要选择在Aspnetcore上托管它。您将获得一个名为Blazor 的项目。

现在使用ASP.NET Core Web App模板向解决方案添加第二个项目。称之为Blazor.Web。将其设置为启动项目。

解决方案现在应如下所示:

https://shauncurtis.github.io/siteimages/Articles/AllinOne/Base-Projects.png

Blazor项目变更

该解决方案在网站的子目录中运行WASM上下文。要使其正常工作,需要对Blazor项目进行一些修改。

  1. 将wwwroot的内容移动到Blazor.Web并删除wwwroot中的所有内容。
  2. 向项目文件中添加一个StaticWebAssetBasePath条目,设置为wasm 。这在使用它的上下文中区分大小写,因此请坚持使用小写字母。
  3. 添加必要的包。

项目文件应如下所示:



  
    wasm
    net5.0
  

  
    
    
    
  

  
    
  

MainLayout

MainLayout需要修改以处理这两种情况。该解决方案更改了每个上下文的配色方案。WASM Teal和Server Steel。

@inherits LayoutComponentBase
@*change class*@
About
@Body
@code { [Inject] NavigationManager NavManager { get; set; } private bool _isWasm => NavManager?.Uri.Contains("wasm", StringComparison.CurrentCultureIgnoreCase) ?? false; private string _sidebarCss => _isWasm ? "sidebar sidebar-teal" : "sidebar sidebar-steel"; }

将以下Css样式添加到下面的组件Css文件.sidebar中。

.sidebar {
    background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}

/* Added Styles*/
.sidebar-teal {
    background-image: linear-gradient(180deg, rgb(0, 64, 128) 0%, rgb(0,96,192) 70%);
}

.sidebar-steel {
    background-image: linear-gradient(180deg, #2a3f4f 0%, #446680 70%);
}
/* End Added Styles*/

导航菜单

添加代码和标记——它添加了一个链接以在上下文之间切换。

@*Change title*@ Blazor
    @*Add links between contexts*@
  • @_otherContextLinkName
  • Home
  • Counter
  • Fetch data
@code { [Inject] NavigationManager NavManager { get; set; } private bool _isWasm => NavManager?.Uri.Contains("wasm", StringComparison.CurrentCultureIgnoreCase) ?? false; private string _otherContextUrl => _isWasm ? "/" : "/wasm"; private string _otherContextLinkName => _isWasm ? "Server Home" : "WASM Home"; private string _title => _isWasm ? "AllinOne WASM" : "AllinOne Server"; private bool collapseNavMenu = true; private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; private void ToggleNavMenu() { collapseNavMenu = !collapseNavMenu; } }

FetchData.razor

通过在开头添加/来更新用于获取预测的URL ,该文件现在位于根目录中而不是wasm。

protected override async Task OnInitializedAsync()
{
    forecasts = await Http.GetFromJsonAsync("/sample-data/weather.json");
}

Blazor.Web

更新项目文件:


  
    net5.0
  
  
    
  
  
    
  

添加Razor网页的页面叫做WASM.cshtml——为WASM SPA启动页。

@page "/wasm"
@{
    Layout = null;
}




    
    
    Blazor
    @*Change base*@
    
    @*Update Link hrefs*@
    
    
    


    
Loading...
An unhandled error has occurred. Reload 🗙
@*Update js sources *@

添加第二个Razor网页的页面叫做Server.cshtml——为Servr SPA启动页。

@page "/"
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}





    
    
    Blazor
    
    
    
    



    

    
An error has occurred. This application may no longer respond until reloaded. An unhandled exception has occurred. See browser dev tools for details. Reload 🗙

Index.cshtml

将@page指令更新为@page "/index".

Startup.cs

更新Startup以处理WASM和服务器中间件路径。

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();

        // Server Side Blazor doesn't register HttpClient by default
        // Thanks to Robin Sue - Suchiman https://github.com/Suchiman/BlazorDualMode
        if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
        {
            // Setup HttpClient for server side in a client side compatible fashion
            services.AddScoped(s =>
            {
                // Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
                var uriHelper = s.GetRequiredService();
                return new HttpClient
                {
                    BaseAddress = new Uri(uriHelper.BaseUri)
                };
            });
        }
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments("/wasm"), app1 =>
        {
            app1.UseBlazorFrameworkFiles("/wasm");
            app1.UseRouting();
            app1.UseEndpoints(endpoints =>
            {
                endpoints.MapFallbackToPage("/wasm/{*path:nonfile}", "/wasm");
            });
        });

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapBlazorHub();
            endpoints.MapRazorPages();
            endpoints.MapFallbackToPage("/Server");
        });
    }
}

运行应用程序

应用程序现在应该可以运行了。它将在服务器上下文中启动。通过左侧菜单中的链接切换到WASM上下文。当您在上下文之间切换时,您应该会看到颜色的变化。

添加数据服务

虽然上述配置有效,但它需要一些演示代码来展示它如何处理更传统的数据服务。我们将修改解决方案以使用非常基本的数据服务来展示应该使用的DI和接口概念。

将数据和服务文件夹添加到Blazor项目。

WeatherForecast.cs

向Data添加一个WeatherForecast类。

public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

IWeatherForecastService.cs

为Services添加一个IWeatherForecastService接口。

public interface IWeatherForecastService
{
    public Task GetRecordsAsync();
}

WeatherForecastServerService.cs

向Services添加一个WeatherForecastServerService类。通常这会连接到数据库,但这里我们只是创建了一组虚拟记录。

public class WeatherForecastServerService : IWeatherForecastService
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private List records = new List();

    public WeatherForecastServerService()
        => this.GetForecasts();

    public void GetForecasts()
    {
        var rng = new Random();
        records = Enumerable.Range(1, 10).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        }).ToList();
    }

    public Task GetRecordsAsync()
        => Task.FromResult(this.records);
}

WeatherForecastAPIService.cs

向Services添加一个WeatherForecastAPIService类。

public class WeatherForecastAPIService : IWeatherForecastService
{
    protected HttpClient HttpClient { get; set; }

    public WeatherForecastAPIService(HttpClient httpClient)
        => this.HttpClient = httpClient;

    public async Task GetRecordsAsync()
        => await this.HttpClient.GetFromJsonAsync($"/api/weatherforecast/list");
}

WeatherForecastController.cs

最后将一个WeatherForecastController类添加到控制器文件夹中的Blazor.Web项目。

using System.Collections.Generic;
using System.Threading.Tasks;
using Blazor.Data;
using Microsoft.AspNetCore.Mvc;
using MVC = Microsoft.AspNetCore.Mvc;
using Blazor.Services;

namespace Blazor.Web.APIControllers
{
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        protected IWeatherForecastService DataService { get; set; }

        public WeatherForecastController(IWeatherForecastService dataService)
            => this.DataService = dataService;

        [MVC.Route("/api/weatherforecast/list")]
        [HttpGet]
        public async Task GetList() => await DataService.GetRecordsAsync();
    }
}

Blazor项目Program.cs

通过它的.IWeatherForecastService将API服务添加到Blazor项目中的Program.cs。

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.RootComponents.Add("#app");

        builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
        builder.Services.AddScoped();

        await builder.Build().RunAsync();
    }
}

Blazor.Web Startup.cs

在Blazor.Web项目中将服务器服务添加到Startup.cs中,再通过它的IWeatherForecastService。

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddScoped();
    .....
}

构建并运行项目

解决方案现在应该构建并运行。

https://shauncurtis.github.io/siteimages/Articles/AllinOne/Blazor-Project.png

https://shauncurtis.github.io/siteimages/Articles/AllinOne/Blazor-Web-Project.png

它是如何工作的?

从根本上说,Blazor服务器和Blazor WASM应用程序之间的区别在于它运行的上下文。在解决方案中,所有SPA代码都构建在Web Assembly项目中,并由WASM和服务器上下文使用。没有“共享”代码库代码,因为它是完全相同的前端代码,具有相同的入口点——App.razor。两个上下文之间的不同之处在于后端服务的提供者。

已声明Web程序集项目。它构建标准Blazor.dll文件和WASM特定代码,包括Web程序集“启动配置文件”blazor.boot.json。

在Web程序集上下文中,初始页面加载blazor.webassembly.js。这将加载blazor.boot.json,它告诉blazor.webassembly.js如何在浏览器中“启动”Web程序集代码。它运行Program构建WebAssemblyHost,加载定义的服务,并启动Renderer将应用html元素替换为Program中指定的根组件。这将加载路由器,它读取Url,获取适当的组件,将其加载到指定的布局中,然后开始渲染过程。SPA启动并运行。

在服务器上下文中,服务器端代码在初始加载页面中选取组件引用并静态呈现它。它将呈现的页面传递给客户端。这会加载并运行blazor.server.js,它会回调服务器SignalR Hub并获取动态呈现的应用根组件。SPA启动并运行。服务容器和渲染器位于Blazor Hub 中——通过services.AddServerSideBlazor()在Web服务器启动时调用Startup来启动。

我们实现的数据服务展示了依赖注入和接口。UI组件——在我们的例子中FetchData使用IWeatherForcastService在Services中注册的服务。在 WASM上下文中,服务容器启动WeatherForecastAPIService,而在服务器上下文中,服务容器启动WeatherForecastServerService。两个不同的服务,遵循相同的接口,由使用该接口的UI组件消费。UI组件并不关心它们使用哪个服务,它只需要实现IWeatherForcastService。

总结

希望本文能够深入了解Blazor SPA的工作原理以及服务器和WASM Blazor SPA之间的真正区别。

https://www.codeproject.com/Articles/5299017/Building-a-Blazor-WASM-and-Server-All-In-One-Solut

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.1403s