ASP.NET Core Blazor 初探之 Blazor Server

上周初步对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=398940public 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

关注我的公众号一起玩转技术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308757.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[SpringSecurity]web权限方案_用户认证_设置用户名密码

设置登陆的用户名和密码 第一种方式&#xff1a;通过配置文件 spring.security.user.nameatguigu spring.security.user.passwordatguigu第二种方式&#xff1a;通过配置类 package com.atguigu.securitydemo1.config;import org.springframework.context.annotation.Bean; i…

[SpringSecurity]web权限方案_用户认证_查询数据库完成认证

#mysql 数据库连接 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver spring.datasource.urljdbc:mysql://localhost:3306/demo?serverTimezoneUTC spring.datasource.usernameroot spring.datasource.passwordrootpackage com.atguigu.securitydemo1.config;i…

.Net Core 2.2升级3.1的避坑指南

写在前面微软在更新.Net Core版本的时候&#xff0c;动作往往很大&#xff0c;使得每次更新版本的时候都得小心翼翼&#xff0c;坑实在是太多。往往是悄咪咪的移除了某项功能或者组件&#xff0c;或者不在支持XX方法&#xff0c;这就很花时间去找回需要的东西了&#xff0c;下面…

[SpringSecurity]web权限方案_用户认证_自定义用户登录页面

在配置类中实现相关的配置 Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin() //自定义自己编写的登陆页面.loginPage("/login.html") //登陆页面设置.loginProcessingUrl("/user/login") //登陆访问路径.defa…

Asp.Net Core Blazor之容器部署

写在前面Docker作为开源的应用容器引擎&#xff0c;可以让我们很轻松的构建一个轻量级、易移植的容器&#xff0c;通过Docker方式进行持续交付、测试和部署&#xff0c;都是极为方便的&#xff0c;并且对于我们开发来说&#xff0c;最直观的优点还是解决了日常开发中的环境配置…

[SpringSecurity]web权限方案_用户授权_基于权限访问控制_基于角色访问控制_hasAuthority和hasAnyAuthority_hasRole和hasAnyRole

基于角色或权限进行访问控制 hasAuthority 方法 如果当前的主体具有指定的权限&#xff0c;则返回 true,否则返回 false 在配置类设置当前访问地址有哪些 Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin() //自定义自己编写的登…

.Net Core WebAPI + Axios +Vue 实现下载与下载进度条

写在前面老板说&#xff1a;系统很慢&#xff0c;下载半个小时无法下载&#xff0c;是否考虑先压缩再给用户下载&#xff1f;本来是已经压缩过了&#xff0c;不过第一反应应该是用户下的数量多&#xff0c;导致压缩包很大&#xff0c;然后自己测试发现&#xff0c;只是等待的时…

[SpringSecurity]web权限方案_用户授权_自定义403页面

自定义403页面 unauth.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body><h1>没有访问权限</h1></body> </html>配置类…

三分钟Docker-环境搭建篇

如题目显示&#xff0c;三分钟让你学会在windows上安装docker环境&#xff0c;开启docker之旅的第一步。安装前要求Windows 10 64位&#xff1a;专业版&#xff0c;企业版或教育版&#xff08;内部版本16299或更高版本&#xff09;。必须启用Hyper-V控制面板->程序和功能-&g…

[SpringSecurity]web权限方案_用户注销

用户注销 在配置类中添加退出映射地址 //退出http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();测试 修改配置类&#xff0c;登陆成功之后跳转到成功页面 在成功页面添加超链接&#xff0c;写设置退出路径 success.htm…

骚年快答 | 为何微服务项目都使用单体代码仓库?

【答疑解惑】| 作者 / Edison Zhou这是恰童鞋骚年的第265篇原创内容之前在学习微软的示例eShopOnContainers时发现它使用的是单体代码仓库库&#xff0c;之后又发现大家在进行微服务项目开发时也都在使用单体代码仓库。问题来了&#xff0c;为啥要微服务项目都要使用单体仓库&a…

[SpringSecurity]web权限方案_自动登陆_原理分析和具体实现

自动登陆 1.cookie技术 2.安全框架机制实现自动登陆 这里我们使用安全框架机制实现自动登陆技术 实现原理 具体实现 第一步 创建数据库 CREATE TABLE persistent_logins (username varchar(64) NOT NULL,series varchar(64) NOT NULL,token varchar(64) NOT NULL,last_us…

[SpringSecurity]web权限方案_CSRF功能

CSRF CSRF功能默认是已经打开了&#xff01; 具体过程可以阅读CsrfFilter这个过滤器的源码 CSRF 理解 在登录页面添加一个隐藏域 <input type"hidden"th:if"${_csrf}!null"th:value"${_csrf.token}"name"_csrf "/>关闭安全…

[SpringSecurity]web权限方案_用户授权_注解使用

注解使用 Secured 判断用户是否具有角色&#xff0c;可以访问方法&#xff0c;另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。 使用注解先要开启注解功能&#xff01; 启动类(配置类)开启注解 EnableGlobalMethodSecurity(securedEnable true) 在controller的…

我和ABP vNext 的故事

Abp VNext是Abp的.NET Core 版本&#xff0c;但它不仅仅只是代码重写了。Abp团队在过去多年社区和商业版本的反馈上做了很多的改进。包括性能、底层的框架设计&#xff0c;它融合了更多优雅的设计实践。不管你是自己需要快速上手项目、或者是公司的研发团队没有足够的能力去完整…

微软为 Visual Studio 推出新的 Razor 编辑器

随着 Visual Studio 最新版本的发布&#xff0c;微软推出了一款新的 Razor 编辑器&#xff0c;用于使用 MVC、Razor Pages 和 Blazor 进行本地开发。该工具目前还处于实验状态。Razor 是一种基于 HTML 和 C# 的模板语言&#xff0c;可以用来为 .NET Web 应用程序创建动态内容。…

禁用了云服务器的网卡怎么办?

点击上方关注“汪宇杰博客” ^_^导语我们平时管理云服务器时&#xff0c;难免误操作把网卡给禁用了&#xff0c;于是再也无法远程连接了。这时候怎么办呢&#xff1f;如果有虚拟机快照&#xff0c;能够恢复到上一个良好的时刻&#xff0c;但通常会损失这个时间段内的数据和应用…

[SpringBoot2]@MatrixVariableUrlPathHelper

场景 页面开发&#xff0c;cookie禁用了&#xff0c;session里面的内容怎么使用&#xff1a; session.set(a,b)—>jessionid—>cookie—>每次发请求携带 此时cookie禁用了&#xff0c;我们要怎么得到session里面的内容呢&#xff1f; url重写&#xff1a;/abc;jse…

WebBenchmark之动态数据测试

对于很多WebApi管理工具来说&#xff0c;针对接口的性能测试都拿固定的数据进行一个循环式的测试&#xff1b;这种测试只能确保当前数据下的有效性&#xff0c;但在性能测试中往往需要压测不同的数据分布&#xff0c;这样能够更准确地反映在不同数据下系统的处理能力。WebBench…

[SpringBoot2]Thymeleaf

引入starter <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>自动配置好了thymeleaf Configuration(proxyBeanMethods false) EnableConfigurationPrope…