上周初步对Blazor WebAssembly进行了初步的探索(ASP.NET Core Blazor 初探之 Blazor WebAssembly)。这次来看看Blazor Server该怎么玩。

Blazor Server

Blazor 技术又分两种:

  • Blazor WebAssembly
  • Blazor Server

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<IStudentRepository, StudentRepository>();

实现学生列表

跟上次一样,先删除默认生成的一些内容,减少干扰,这里不多说了。在pages文件夹下新建student文件夹,新建List.razor文件:

@page "/student/list"

@using BlazorServerDemo.Model
@using BlazorServerDemo.Data @inject IStudentRepository Repository <h1>List</h1> <p class="text-right">
<a class="btn btn-primary" href="/student/add">Add</a>
</p> <table class="table">
<tr>
<th>Id</th>
<th>Name</th>
<th>Age</th>
<th>Sex</th>
<th>Class</th>
<th></th>
</tr>
@if (_stutdents != null)
{
foreach (var item in _stutdents)
{
<tr>
<td>@item.Id</td>
<td>@item.Name</td>
<td>@item.Age</td>
<td>@item.Sex</td>
<td>@item.Class</td>
<td>
<a class="btn btn-primary" href="/student/modify/@item.Id">修改</a>
<a class="btn btn-danger" href="/student/delete/@item.Id">删除</a>
</td>
</tr>
}
} </table> @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项目复制过来,不用做任何改动。

@using BlazorServerDemo.Model

<div>
<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>
</div> @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"

@using BlazorServerDemo.Model
@using BlazorServerDemo.Data @inject NavigationManager NavManager
@inject IStudentRepository Repository <h1>Add</h1> <Edit Student="Student" OnSaveCallback="OnSave"></Edit> <div class="text-danger">
@_errmsg
</div> @code { 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}"

@using BlazorServerDemo.Model
@using BlazorServerDemo.Data @inject NavigationManager NavManager
@inject IStudentRepository Repository <h1>Modify</h1> <Edit Student="Student" OnSaveCallback="OnSave"></Edit> <div class="text-danger">
@_errmsg
</div> @code {
[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}"

@using BlazorServerDemo.Model
@using BlazorServerDemo.Data @inject NavigationManager NavManager
@inject IStudentRepository Repository <h1>Delete</h1> <h3>
确定删除(@Student.Id)@Student.Name ?
</h3> <button class="btn btn-danger" @onclick="OnDeleteAsync">
删除
</button> <CancelBtn Name="取消"></CancelBtn> @code {
[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

ASP.NET Core Blazor 初探之 Blazor Server的更多相关文章

  1. ASP.NET Core Blazor 初探之 Blazor WebAssembly

    最近Blazor热度很高,传说马上就要发布正式版了,做为微软脑残粉,赶紧也来凑个热闹,学习一下. Blazor Blazor是微软在ASP.NET Core框架下开发的一种全新的Web开发框架.Bla ...

  2. ASP.NET CORE网站部署到 windows server 的IIS 上去

    章基于我自己经验的一个总结,在windows服务器上部署asp.net core网站.环境是 windows server 2012数据中心版本 第一步先安装 IIS 服务器 接下来就是一路下一步,然 ...

  3. ASP.NET Core多平台部署 (Windows Server+IIS与CentOS 7+Nginx)

    一,Windows Server+IIS部署 1,安装配置IIS,这个应该都不用多说了,教程一堆 2,下载安装.NET Core Runtime 与 .NET Core SDK,下载请点击下载地址,如 ...

  4. ASP.NET Core网站初探

    原文地址:https://blog.csdn.net/iml6yu/article/details/74530679 目录结构如下图   目录: Properties:属性,记录了项目属性的配置文件. ...

  5. [Asp.Net Core] Blazor Server Side 扩展用途 - 配合CEF来制作客户端浏览器软件

    前言 大家用过微信PC端吧? 这是用浏览器做的. 用过Visual Studio Code吧? 也是用浏览器做的. 听说, 暴雪客户端也包含浏览器核心?? 在客户端启动一个浏览器, 并不是什么难事了. ...

  6. ASP.NET Core Blazor Webassembly 之 组件

    关于组件 现在前端几大轮子全面组件化.组件让我们可以对常用的功能进行封装,以便复用.组件这东西对于搞.NET的同学其实并不陌生,以前ASP.NET WebForm的用户控件其实也是一种组件.它封装ht ...

  7. ASP.NET Core Blazor Webassembly 之 路由

    web最精妙的设计就是通过url把多个页面串联起来,并且可以互相跳转.我们开发系统的时候总是需要使用路由来实现页面间的跳转.传统的web开发主要是使用a标签或者是服务端redirect来跳转.那今天来 ...

  8. [Asp.net core 3.1] 通过一个小组件熟悉Blazor服务端组件开发

    通过一个小组件,熟悉 Blazor 服务端组件开发.github 一.环境搭建 vs2019 16.4, asp.net core 3.1 新建 Blazor 应用,选择 asp.net core 3 ...

  9. 初探ASP.NET Core 3.x (3) - Web的运作流程和ASP.NET Core的运作结构

    本文地址:https://www.cnblogs.com/oberon-zjt0806/p/12215717.html 注意:本篇大量地使用了mermaid绘制图表,加载需要较长的时间,请见谅 [TO ...

随机推荐

  1. Python刷CSDN阅读数(仅供娱乐)

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ @File:csdn_reads.py @E-mail:3649427 ...

  2. 4. git log的常见用法

    git log ======见https://blog.csdn.net/daguanjia11/article/details/73823617 +++++++++++++++++++++++ 使用 ...

  3. Aria2任意文件写入漏洞

    目录: 简介 漏洞描述 payload 漏洞复现 一.Aria2介绍 Aria2是一个命令行下运行,多协议,多来源下载工具(HTTP / HTTPS,FTP,BitTorrent,Metalink), ...

  4. [YII2] 去除自带js,加载自己的JS,然后ajax(json)传值接值!

    本想用YII2自带的JS,可是用着效果不好,想从新加载,找了好多终于实现啦!还有ajax(json)传值接值! 首先直接了当的就把YII2自带的js去掉! 把下面代码加入到/config/main.p ...

  5. 15分钟从零开始搭建支持10w+用户的生产环境(三)

    上一篇文章介绍了这个架构中,选择MongoDB做为数据库的原因,及相关的安装操作. 原文地址:15分钟从零开始搭建支持10w+用户的生产环境(二)   三.WebServer 在SOA和gRPC大行其 ...

  6. java中wait和sleep的区别

    文章目录 Wait和sleep的区别 唤醒wait和sleep java中wait和sleep的区别 在本篇文章中,我们将会讨论一下java中wait()和sleep()方法的区别.并讨论一下怎么使用 ...

  7. LightOJ 1287 Where to Run(期望)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1287 题意:给定一个n个点的无向图(0到n-1),你开始在0.你开始遍历这个图 ...

  8. 网课应该这么刷(油猴Tampermonkey脚本自动刷课)

    懒人福利 首先有些人不想学怎么用脚本,满足你们,压缩包解压之后直接登录即可.戳我下载 脚本已经集成好了,登录即可刷课.章节测试还会自动答题呦,正确率高达97%呦. 油猴及脚本安装 油猴的脚本不知可以刷 ...

  9. 商汤提出解偶检测中分类和定位分支的新方法TSD,COCO 51.2mAP | CVPR 2020

    目前很多研究表明目标检测中的分类分支和定位分支存在较大的偏差,论文从sibling head改造入手,跳出常规的优化方向,提出TSD方法解决混合任务带来的内在冲突,从主干的proposal中学习不同的 ...

  10. 慎用ToLower和ToUpper,小心把你的系统给拖垮了

    不知道何时开始,很多程序员喜欢用ToLower,ToUpper去实现忽略大小写模式的字符串相等性比较,有可能这个习惯是从别的语言引进的,大胆猜测下是JS,为了不引起争论,我指的JS是技师的意思~ 一: ...