天天看點

ASP.NET Core Blazor 初探之 Blazor ServerListAddModifyDelete

ASP.NET Core Blazor 初探之 Blazor Server

上周初步對Blazor WebAssembly進行了初步的探索(ASP.NET Core Blazor 初探之 Blazor WebAssembly)。這次來看看Blazor Server該怎麼玩。

Blazor Server

Blazor 技術又分兩種:

Blazor WebAssembly

Blazor WebAssembly上次已經介紹過了,這次主要來看看Blazor Server。Blazor Server 有點像WebAssembly的服務端渲染模式。頁面在伺服器端渲染完成之後,通過SignalR(websocket)技術傳輸到前端,再替換dom元素。其實不光是頁面的渲染,大部分計算也是服務端完成的。Blazor Server模式可以讓一些不支援WebAssembly的浏覽器可以運作Blazor項目,可是問題也是顯而易見的,基于SignalR的雙向實時通信給網絡提出了很高的要求,一旦使用者量巨大,對服務端的水準擴容也帶來很大的挑戰,Blazor Server的使用者狀态都維護在服務端,這對服務端記憶體也造成很大的壓力。

我們還是以完成一個簡單的CRUD項目為目标來探究一下Blazor Server究竟是什麼。因為前面Blazor Webassembly已經講過了,相同的東西,比如資料綁定,屬性綁定,事件綁定等内容就不多說了,請參見ASP.NET Core Blazor 初探之 Blazor WebAssembly。

建立Blazor Server項目

打開vs找到Blazor Server模闆,看清楚了不要選成Blazor Webassembly模闆。

看看生成的項目結構:

可以看到Blazor Server的項目結構跟ASP.Net Core razor pages 項目是一模一樣的。看看Startup是怎麼配置的:

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.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddSingleton<WeatherForecastService>();
    }

    // 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.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }
}           

主要有2個地方要注意:

在ConfigureServices方法裡注冊了Blazor的相關service:

services.AddServerSideBlazor();

在Configure方法的終結點配置了Blazor相關的映射:

endpoints.MapBlazorHub();

上次Blazor Webassembly我們的資料服務是通過一個Webapi項目提供的,這次不用了。如果需要提供webapi服務,Blazor Server本身就可以承載,但是Blazor Server根本不需要提供webapi服務,因為他的資料互動都是通過websocket完成的。

實作資料通路

建立student類:

public class Student

{
    public int Id { get; set; }
    public string Name { get; set; }

    public string Class { get; set; }

    public int Age { get; set; }

    public string Sex { get; set; }
}           

上次我們實作了一個StudentRepository,我們直接搬過來:

public interface IStudentRepository
{
    List<Student> List();

    Student Get(int id);

    bool Add(Student student);

    bool Update(Student student);

    bool Delete(int id);
}           

}

public class StudentRepository : IStudentRepository

{
    private static List<Student> Students = new List<Student> {
            new Student{ Id=1, Name="小紅", Age=10, Class="1班", Sex="女"},
            new Student{ Id=2, Name="小明", Age=11, Class="2班", Sex="男"},
            new Student{ Id=3, Name="小強", Age=12, Class="3班", Sex="男"}
    };

    public bool Add(Student student)
    {
        Students.Add(student);

        return true;
    }

    public bool Delete(int id)
    {
        var stu = Students.FirstOrDefault(s => s.Id == id);
        if (stu != null)
        {
            Students.Remove(stu);
        }

        return true;
    }

    public Student Get(int id)
    {
        return Students.FirstOrDefault(s => s.Id == id);
    }

    public List<Student> List()
    {
        return Students;
    }

    public bool Update(Student student)
    {
        var stu = Students.FirstOrDefault(s => s.Id == student.Id);
        if (stu != null)
        {
            Students.Remove(stu);
        }

        Students.Add(student);
        return true;
    }
}           

注冊一下:

services.AddScoped();

實作學生清單

跟上次一樣,先删除預設生成的一些内容,減少幹擾,這裡不多說了。在pages檔案夾下建立student檔案夾,建立List.razor檔案:

@page "/student/list"

@using BlazorServerDemo.Model

@using BlazorServerDemo.Data

@inject IStudentRepository Repository

List

<a class="btn btn-primary" href="/student/add">Add</a>           
Id Name Age Sex Class
@item.Id @item.Name @item.Age @item.Sex @item.Class 修改 删除

@code {

private List<Student> _stutdents;

protected override void OnInitialized()
{
    _stutdents = Repository.List();
}           

這個頁面是從上次的WebAssembly項目上複制過來的,隻改了下OnInitialized方法。上次OnInitialized裡需要通過Httpclient從背景擷取資料,這次不需要注入HttpClient了,隻要注入Repository就可以直接擷取資料。

運作一下:

F12看一下這個頁面是如何工作的:

首先/student/list是一次标準的Http GET請求。傳回了頁面的html。從傳回的html代碼上來看綁定的資料已經有值了,這可以清楚的證明Blazor Server技術使用的是服務端渲染技術。

_blazor?id=Fv2IGD6CfKpQFZ-fi-e1IQ連接配接是個websocket長連接配接,用來處理服務端跟用戶端的資料互動。

實作Edit元件

Edit元件直接從Webassembly項目複制過來,不用做任何改動。

<div class="form-group">
    <label>Id</label>
    <input @bind="Student.Id" class="form-control" />
</div>
<div class="form-group">
    <label>Name</label>
    <input @bind="Student.Name" class="form-control" />
</div>
<div class="form-group">
    <label>Age</label>
    <input @bind="Student.Age" class="form-control" />
</div>
<div class="form-group">
    <label>Class</label>
    <input @bind="Student.Class" class="form-control" />
</div>
<div class="form-group">
    <label>Sex</label>
    <input @bind="Student.Sex" class="form-control" />
</div>

<button class="btn btn-primary" @onclick="TrySave">
    儲存
</button>

<CancelBtn Name="取消"></CancelBtn>           

@code{

[Parameter]
public Student Student { get; set; }
[Parameter]
public EventCallback<Student> OnSaveCallback { get; set; }

protected override Task OnInitializedAsync()
{
    if (Student == null)
    {
        Student = new Student();
    }

    return Task.CompletedTask;
}

private void TrySave()
{
    OnSaveCallback.InvokeAsync(Student);
}           

實作新增頁面

同樣新增頁面從上次的Webassembly項目複制過來,可以複用大量的代碼,隻需改改儲存的代碼。原來儲存代碼是通過HttpClient送出到背景來完成的,現在隻需要注入Repository調用Add方法即可。

@page "/student/add"

@inject NavigationManager NavManager

Add

@_errmsg           
private Student Student { get; set; }

private string _errmsg;

protected override Task OnInitializedAsync()
{
    Student = new Student()
    {
        Id = 1
    };

    return base.OnInitializedAsync();
}

private void OnSave(Student student)
{
    Student = student;

    var result = Repository.Add(student);

    if (result)
    {
        NavManager.NavigateTo("/student/list");
    }
    else
    {
        _errmsg = "儲存失敗";
    }
}
           

這裡不再多講綁定屬性,綁定事件等内容,因為跟Webassembly模式是一樣的,請參見上一篇。

運作一下 :

我們的頁面出來了。繼續F12看看頁面到底是怎麼渲染出來的:

這次很奇怪并沒有發生任何Http請求,那麼我們的Add頁面是哪裡來的呢,讓我們繼續看Websocket的消息:

用戶端通過websocket給服務端發了一個消息,裡面攜帶了一個資訊:OnLocation Changed "http://localhost:59470/student/add",服務端收到消息後把對應的頁面html渲染出來通過Websocket傳遞到前端,然後前端進行dom的切換,展示新的頁面。是以這裡看不到任何傳統的Http請求的過程。

點一下儲存看看發生了什麼:

我們可以看到點選儲存的時候用戶端同樣沒有發送任何Http請求,而是通過websocket給背景發了一個消息,這個消息表示哪個按鈕被點選了,背景會根據這個資訊找到需要執行的方法,方法執行完後通知前端進行頁面跳轉。

但是這裡有個問題,我們填寫的資料呢?我們在文本框裡填寫的資料貌似沒有傳遞到背景,這就不符合邏輯了啊。想了下有可能是文本框編輯的時候資料就送出回去了,讓我們驗證下:

我們一邊修改文本框的内容,一邊監控websocket的消息,果然發現了,當我們修改完焦點離開文本框的時候,資料直接被傳遞到了伺服器。厲害了我的軟,以前vue,angularjs實作的是前端html跟js對象的綁定技術,而Blazor Server這樣就實作了前後端的綁定技術,666啊。

實作編輯跟删除頁面

這個不多說了使用上面的知識點輕松搞定。

編輯頁面:

@page "/student/modify/{Id:int}"

Modify

@_errmsg           
[Parameter]
public int Id { get; set; }

private Student Student { get; set; }

private string _errmsg;

protected override void OnInitialized()
{
    Student = Repository.Get(Id);
}

private void OnSave(Student student)
{
    Student = student;

    var result = Repository.Update(student);

    if (result)
    {
        NavManager.NavigateTo("/student/list");
    }
    else
    {
        _errmsg = "儲存失敗";
    }
}           

删除頁面:

@page "/student/delete/{Id:int}"

Delete

确定删除(@Student.Id)@Student.Name ?           
删除           
[Parameter]
public int Id { get; set; }

private Student Student { get; set; }

protected override void OnInitialized()
{
    Student = Repository.Get(Id);
}

private void OnDeleteAsync()
{
    var result = Repository.Delete(Id);
    if (result)
    {
        NavManager.NavigateTo("/student/list");
    }
}           

總結

Blazor Server總體開發體驗上跟Blazor Webassembly模式保持了高度一直。雖然是兩種不同的渲染模式:Webassembly是用戶端渲染,Server模式是服務端渲染。但是微軟通過使用websocket技術作為一層代理,巧妙隐藏了兩者的差異,讓兩種模式開發保持了高度的一緻性。Blazor Server除了第一次請求使用Http外,其他資料互動全部通過websocket技術在服務端完成,包括頁面渲染、事件處理、資料綁定等,這樣給Blazor Server項目的網絡、記憶體、擴充等提出了很大的要求,在項目選型上還是要慎重考慮。

最後demo的源碼:BlazorServerDemo

Email:[email protected]

作者:Agile.Zhou(kklldog)

出處:

http://www.cnblogs.com/kklldog/

繼續閱讀