课程简介
- 与2.x相比发生的一些变化,项目结构、Blazor、SignalR、gRPC等
- 课程预计结构
- ASP.NET Core 3.0项目架构简介
- ASP.NET Core MVC 简介
- Blazor
- SignalR
- Web API
- gRPC
- 发布
一. 创建项目
- dotnet core 本质上是控制台应用
1. DI 依赖注入(Dependency Injection)
- IoC 容器(Inversion of Control)控制反转
- 注册(服务)
- 请求实例
- 实例的生命周期
- 生命周期
- Transient(每次被请求都会生成一个新的实例,最短)
- Scoped(一次 Web 请求产生一次实例,较长)
- Singleton(从应用程序启动到停止,只创建一次,最长)
2. ConfigureServices
services.AddControllersWithViews();
services.AddControllers();
// 别的类每次请求 IClock 这个接口时,都会返回一个 ChinaClock 类的实例
// services.AddSingleton<IClock, ChinaClock>();
services.AddSingleton<IClock, UtcClock>();
当需要更改接口的实现类的时候,只需要在依赖注入中修改即可。
public class HomeController: Controller
{ // 不需要更改
public HomeController(IClock clock)
{
}
}
2.1 DI 的优点
- 解耦,没有强依赖。
- 利于单元测试
- 不需要了解具体的服务类
- 也不需要管理服务类的生命周期
3. Configure
- 针对 HTTP 请求,建立管道
3.1 ASP.NET Core 管道(pipeline)
- 放在管道中处理请求的东西(请求的数据,或者一些Web Server的东西),叫做中间件
- Auth 身份认证,MVC 框架,Static Files 静态文件(HTML,JS,CSS)
3.2 中间件注册(顺序非常重要)
// 在开发环境中,一些没有被处理或者捕获的异常就会传到这里,展示成一个页面
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 不使用这个中间件就无法访问静态文件(HTML,JS,CSS)
// 它可以放在 UesRouting 前,因为不需要知道路由信息
app.UseStaticFiles();
// 将 HTTP 请求转换为 HTTPS 请求,强制用户使用 TLS 协议(SLL)
app.UseHttpsRedirection();
// 身份认证,必须放在 UseEndpoints 前面,因为在使用 MVC 等之前就应该完成身份认证
app.UseAuthtication();
// 路由中间件,检查在应用中已经被注册的端点,这些端点有可能是被 MVC 注册的, 也有可能是RazorPages 注册的。
// 会判断这个 HTTP 请求是在哪个端点上出现的,并把这些信息传达给后面需要用到这些信息的中间件。
app.UseRouting();
// 端点中间件,对端点进行注册
app.UseEndpoints(endpoints = >
{
endpoints.MapGet(pattern:"/", requestDelegate:async context =>
{
await context.Response.WriteAsync(text: "Hello World!");
});
// MVC
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// Attribute:类似在Controller上加上[Route("api/[Controller]")]
// 注意, services.AddControllers(); 和 endpoints.MapControllers(); 方法成对出现,。
endpoints.MapControllers();
});
3.3 端点 endpoint
端点就是进来的 HTTP 请求的 URL 的结尾那部分,这部分会被中间件进行处理。
- /{controller} /{action}
/home /index
为什么要将 ”路由“ 从 MVC 中分离出来,配合其他中间件使用呢?
3.4 ASP.NET Core 应用的多样性
- MVC:/Home /Index
- Razor Pages:/SomePage
- SignalR:/Hub /Chat
4. 自带的 Kestrel 服务器
- Visual Studio 会配置IIS服务器,但 ASP.NET Core 应用程式本身也会带有一个小型的服务器 Kestrel
4.1 launchSettings.json
iisSettings, IISExpress 服务器的设置,如果删除后面 profiles 中的 IISExpress 的话,可以把这里的iisSettings也进行删除
- profiles 配置,在这里可以配置多个服务器
- 默认有 IISExpress 服务器和 Kestrel 服务器
5. 自定义 ConfigureDevelopment 方法
// 自定义 ConfigureDevelopment 方法
public void ConfigureDevelopment(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
}
// 如果找不到自定义的 Configure 方法,就会执行默认的
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 通过 IWebHostEnvironment 进行注入
// 可以是 IsDevelopment(),也可以是 IsProduction(),还可以自定义 IsEnvironment("OK")
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
// ConfigureServices()方法也可以自定义配置,还可以对Starup 类进行自定义(例如StartupDevelopment)
6. 静态文件
- 手动创建 wwwroot 文件夹(css,js,image)
- 使用 app.UseStaticFiles() 中间件就可以让 wwwroot 里的静态文件变的可访问(例如:localhost:5000/images/logo.png)
二. 包管理
- 服务器端(后端):Nuget
- 前端:Npm
- 添加 npm Configuration File (package.json)
- libman.json(右键--管理客户端)
1. 合并 CSS 文件,并对其进行压缩
建立 bundleconfig.json 文件
[
// 合并
{
"outputFileName":"wwwroot/css/all.min.css",
"innputFiles":[
"wwwroot/lib/bootstrap/dist/css/bootstrap.css",
"wwwroot/css/site.css"
]
},
// 移动 + 压缩
{
"outputFileName":"wwwroot/css/bootstrap.css",
"innputFiles":[
"wwwroot/lib/bootstrap/dist/css/bootstrap.css"
],
"minify":{
"enabled":true
}
}
]
通过 Nuget 安装 BuildBundlerMinifier ,然后生成即可
三. 使用 MVC 相关技术
- Controller
- Tag Helper
- Settings
- View Component
- Razor Page
1. Tag Helper
添加 Razor View Imports,全局添加 Tag Helper
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
Tag Helper 设定环境
<environmet include="Development">
<link rel="stylesheet" asp-href-include="css/*" asp-href-exclude="css/all.min.css" />
</environmet>
<environmet exinclude="Development">
<link rel="stylesheet" asp-href-include="all.min.css" />
</environmet>
<!-- 防止图片被缓存 -->
<img asp-append-version="true" alt="Logo" src="images/logo.png" />
四. ASP.NET Core 的配置信息
- Key-Value 键值对
- 内存里、JSON、XML、INI等文件,或者系统环境变量
- 配置信息与配置系统是解耦的
- 可以依赖注入
1. ASP.NET Core 的配置信息源(顺序查找,相同时后面覆盖前面)
- appsettings.json
- appsettings.{Environment}.json
- Secret Manager
- 环境变量
- 命令行参数
1.1 appsettings.json
{
"Three":{
"Threshold":30
}
}
1.2 Startup:
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
// var three = _configuration["Three:Threshold"];
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ThreeOptions>(_configuration.GetSection("Three"));
// ...
}
1.3 Controller 配置注入
Controller:
private readonly IOptions<ThreeOptions> _threeOptions;
public HomeController(IOptions<ThreeOptions> threeOptions)
{
_threeOptions = threeOptions;
}
Razor:
@inject IOptions<ThreeOptions> options
1.4 清除默认源
Program:
Host.ConfigureAppConfiguration((context, configBuilder) =>
{
configBuilder.Sources.Clear();
configBulider.AddJsonFile("nick.json");
})
五. View Component
- 为什么Partial View 不行?因为没法添加业务逻辑
- 如果在 Controller 里面写呢?那就无法到处复用
- ChildAction呢?开销太大,它需要走完整个Controller的生命周期
1. View Component
- 就像Partial View ,但是还带有一个迷你的Controller,在这里也可以写业务逻辑
- 可以使用Razor语法来渲染View Component
ViewComponent / CompanySummaryViewComponent.cs :
public class CompanySummaryViewComponent: ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
// ...
return View();
}
}
六. Razor Page
- 所有的 MVC 都放在 Pages 目录下的页面中
七. SignalR
- 实时的 Web 应用(Server 主动发送到 Client)
1. SignalR "底层"技术
-
SignalR 使用了三种“底层”技术来实现实时Web,它们分别是Long Polling,
Server Sent Events 和 WebSocket
1.1 Polling
- Polling是实现实时Web的一种笨方法,它就是通过定期的向服务器发送请求,来查看服务器的数据是否有变化
- 如果服务器的数据没有变化,那么就返回204 No Content;如果有变化就把最新的数据发送给客户端
- 这就是Polling,很简单,但是比较浪费资源
- SignalR没有采用Polling这种技术
1.2 Long Polling
- Long Polling和Polling有类似的地方,客户端都是发送请求到服务器。但是不同之处是:如果服务器没有新数据要发送给客户端的话,那么服务器会继续保持连接,直到有新的数据产生,服务器才把新的数据返回给客户端
- 如果请求发出后一段时间内没有响应,那么请求就会超时。这时,客户端会再次发出请求
1.3 Server Sent Event(SSE)
- 使用SSE的话,Web服务器可以在任何时间把数据发送到浏览器,可以称之为推送,而浏览器则会监听进来的信息,这些信息就像流数据一样,这个连接也会一直保持开放,直到服务器主动关闭它
浏览器会使用一个叫做 EventSource 的对象用来处理传过来的信息
单向的,有连接次数限制,并且只支持文本类型
1.4 Web Socket
- Web Socket 是不同于 HTTP 的另一个 TCP 协议,它使得浏览器和服务器之间的交互式通信变得可能。使用 WebSocket,消息可以从服务器发往客户端,也可以用客户端发往服务器,并且没有 HTTP 那样的延迟。信息流没有完成的时候,TCP Socket 通常是保持打开的状态
- 使用线代浏览器时,SignalR 大部分情况下都会使用Web Socket,这也是最有效的传输方式
- 全双工通信:客户端和服务器可以同时往对方发送信息
- 并且不受 SSE 的那个浏览器连接数限制(6个),大部分浏览器对 Web Socket 连接数的限制是50个
- 消息类型:可以是文本和二进制,Web Socket 也支持流媒体(音频和视频)
- 其实正常的 HTTP 请求也使用了 TCP Socket。Web Socket 标准使用了握手机制把用于 HTTP 的 Socket 升级为使用 WS 协议的 Web Socket socket
1.4.1 Web Socket 生命周期
- 所有的一切都发生在 TCP Soket 里面
1.4.2 HTTP 握手
- 每一个 Web Socket 开始的时候都是一个简单的 HTTP Socket
- 客户端首先发送一个 GET 请求到服务器,来请求升级 Socket
- 如果服务器同意的话,这个 Socket 从这时开始就变成了 Web Socket
1.4.3 消息类型
- Web Socket 的消息类型可以是文本,二进制。也包括控制类的消息:Ping/Pong,和关闭
- 每个消息由一个或多个Frame组成,每个Frame有一个head bytes(表示出这个Frame的信息)
2. SignalR
- SignalR 是一个.NET Core/.NET Framework 的开源实时框架。SignalR 的可使用 Web Socket,Server Sent Event 和 Long Polling 作为底层传输方式
- SignalR 基于这三种技术构建,抽象于它们之上,它让你更好的关注业务问题而不是底层传输技术问题
- SignalRR 这个框架分服务器和客户端,服务器端支持ASP.NET Core 和 ASP.NET;而客户端除了支持浏览器里的 javascript 以外,也支持其他类似的客户端,例如桌面应用
3. SignalR 回落机制
- SignalR 使用的三种底层传输技术分别是 Web Socket,Server Sent Event 和 Long Polling
- 其中 Web Socket 仅支持比较现代的浏览器,Web 服务器也不能太老
- 而 Server Sent Event 情况可能好一些,但也存在同样的问题
- 所以 SignalR 采用了回落机制,SignalR 有能力去协商支持的传输类型
4. RPC
- RPC(Remote Procedure Call),它的有点就是可以像调用本地方法一样调用远程服务
- SignalR 采用 RPC 范式来进行客户端与服务器之间的通信
- SignalR 利用底层传输来让服务器可以调用客户端的方法,反之亦然,这些方法可以带参数,参数也可以是复杂对象,SignalR 负责序列化和反序列化
5. Hub
- Hub 是 SignalR 的一个组件,它运行在ASP.NET Core 应用里,所以它是服务器端的一个类
- Hub使用 RPC 接受从客户端发来的信息,也能把消息发送给客户端。所以它就是一个通信用的 Hub
- 在ASP.NET Core 里,自己创建的 Hub 类需要继承于基类 Hub
在 Hub 类里面,我们就可以调用所有客户端上的方法了,同样客户端也可以调用 Hub 类里面的方法
- 之前说过方法调用的时候可以传递复杂参数,SignalR 可以将参数序列化和反序列化,这些参数被序列化的格式叫做 Hub 协议,所以 Hub 协议就是一种用来序列化和反序列化的格式
- Hub 协议默认协议是JSON,还支持的另一个协议是 MessagePack。MessagePack 是二进制格式的,它比JSON更紧凑,而且处理起来更简单快速,因为它是二进制的
此外,SignalR 也可以拓展是用其它协议
6. 横向扩展(将应用运行在多个服务器上:负载均衡)
- 这时负载均衡器会保证每个进来的请求按照一定的逻辑分配到可能是不同的服务器上
- 是使用 Web Socket 的使用,没什么问题,因为一旦 Web Socket 的连接建立,就像在浏览器和那个服务器之间打开了隧道一样,服务器是不会切换的
- 但是如果使用 Long Polling,就可能有问题了,因为使用 Long Polling 的情况下,每次发送消息都是不同的请求,而每次请求可能会到达不同的服务器。不同的服务器可能不知道前一个服务器的内容,这就会造成问题
- 针对这个问题,我们需要使用 Sticky Sessions(粘性会话)
- Sticky Sessions 貌似有很多种实现方式,但是主要是下面要介绍的这种方式
- 作为第一次请求的响应的一部分,负载均衡器会在浏览器里面设置一个Cookie,来表示使用过这个服务器。在后续的请求里,负载均衡读取Cookie,然后把请求分配给同一个服务器
八. Blazor(Core3.0真正的新东西)
MVC:
九. Web API
1. 使用 Attribute Route
// ConfigureService 服务注入
services.AddContollers();
// Configure 中间件
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
2. Controller 方面使用 Route ,ApiController,并且继承于ControllerBase
[Route("v1/[Controller]")]
[ApiController]
public class XXXController: ControllerBase
{
/* 继承于ControllerBase,而不是Contoller。因为Controller也是继承于ControllerBase
并且Controller封装了一些有关View所需的方法,这些是Api所不需要的 */
// ....
}
3. ApiController Attribute ([ApiController] 的作用)
Attribute 路由
对 Model 自动验证 (传入的复杂 Model 类)
-
推断绑定源
[FromBody],[FromForm],[FromRoute],[FromQuery]
[FromHeader],[FromServices]
3.1 默认 [ApiController] 返回的是 application/json;charset=utf-8 数据
- 若需要返回 xml ,其他或者自定义数据,请在 Startup 配置
// ConfigureServices 服务注入
services.AddControllers()
.AddXmlSerializerFormatters();
十. gRPC
1. Protocol Buffer
1.1 为什么需要gRPC ?
-
现如今微服务很流行,而微服务很有可能是使用不同语言进行构建的。而微服务之间通常需要相互通信,所以微服务之间必须在以下几个方面达成共识
需要使用某种API
- 需要使用某种API
- 数据格式
- 错误的模式
- 负载均衡
- 。。。
现在最流行的一种API风格可能是REST,它主要是通过HTTP协议来传输JSON数据。
但是现在我们可以看看gRPC (https://grpc.io/) ,它来自Google,并且支持众多主流的语言,包括Go,Dart,C#,C/C++,Nodejs,Python等等。
1.2 gRPC 能解决哪些问题 ?
-
构建 (Web) API 是挺麻烦的,因为构建API时我们得考虑:
- 数据的格式是JSON、XML还是二进制
- 端点地址以及GET还是POST等
- 如何调用API以及对异常的处理规则
- API的效率:一次调用读取多少数据?是否太多了或太少了?太少的话可能会导致多次API的调用
- 延迟
- 扩展性,是否能支持上千个客户端
- 负载均衡
- 与其他语言的互操作性
- 如何处理身份验证、监控、日志等
1.3 再次介绍一下 gRPC
- gRPC 来自Google,它是一个开源的框架;它同时也是Cloud Native Computer基金会(CNCF)的一部分,就像Dockers和Kubernetes一样
- gRPC 允许你为RPC(RemoteProcedureCall)定义请求和响应,然后gRPC会帮你处理一切剩余问题。
- 它速度快,执行效率高,基于HTTP/2构建,低延迟,支持流,与开发语言无关,并且可以很简单的插入身份验证、负载均衡、日志和监控等功能
1.4 RPC 是什么 ?
- RPC 是(RemoteProcedureCall)远程过程调用
- 在客户端代码使用RPC调用的时候,就像直接调用了服务端的一个函数一样
服务端代码:
“遥远”的客户端调用:
- RPC 它不是一个新的概念,很早它就出现了。但它存在很多的问题。而gRPC它是对RPC一种非常简洁的实现并且解决了很多RPC的问题
1.5 如何学习 gRPC ?
- 首先,你得学习Protocol Buffers (https://developers.google.com/protocol-buffers/) ,简单来说它可以用来定义消息和服务
- 然后,你只需要实现服务即可,剩余的 gRPC 代码将会自动为你生成
.proto 这个文件可以适用于几十种开发语言(包括服务器和客户端),并且它允许你使用同一个框架来支持每秒百万级以上的RPC调用
-
gRPC 使用的是合约优先的API开发模式,它默认使用 Protocol buffers (protobuf) 作为接口设计语言 (IDL) ,这个 .proto 文件包括两部分
- gRPC 服务的定义
- 服务端和客户端之间传递的消息
1.6 protobuf 例子
- 在这里定义了一个 Greeter 服务,它里面定义了一个 SayHello 的 rpc 调用。SayHello 会发送 HelloRequest 这个消息,接收 HelloReply 这个消息