# Asp.NetCoreDemo **Repository Path**: derek_chen/Asp.NetCoreDemo ## Basic Information - **Project Name**: Asp.NetCoreDemo - **Description**: 杨旭老师的《ASP.NET Core 3.x 入门视频》源代码Frist asp .net core demo. - **Primary Language**: C# - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2022-07-04 - **Last Updated**: 2022-07-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## ASP.NET Core 3.x 入门视频(完结)视频 源代码 https://www.bilibili.com/video/av65313713 ## 以下资源来源: github: https://github.com/leezhuang96/Asp.NetCoreDemo blog: https://leezhuang96.github.io/categories/csharp/ ### Asp .Net Core 本质是 `一个Server` + `多个中间件(Middleware)组成的管道(Pipline)`。 本身是一个`console application`,经 `Main() > CreateHostBuilder(args).Build()`后成为`web application`。 ### Asp .Net Core 应用多样性 * MVC:/Home/Index * Razor Pages: /SomePage * SignalR: /Hub/Chat ### 1. MVC #### Create Project - New > Project > Asp .Net core web application #### Program class ```C# public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); //根据不同的环境找不同的Startup类,(StartupDevelopment/StartupProduction/StartupStaging...) //webBuilder.UseStartup(typeof(Program)); //webBuilder.UseKestrel(); 可以不写,源码默认调用 }); } ``` #### Startup class ```c# // 不同环境可配置不同类,(StartupDevelopment/StartupProduction/StartupStaging...) public class Startup { // config service, 不同环境可配置不同方法(ConfigureServicesDevelopment/ConfigureServicesProduction/ConfigureServicesStaging...) public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); //services.AddControllers(); //services.AddMvc(); //services.AddSingleton(); services.AddSingleton(); } // config middleware, 不同环境可配置不同方法(ConfigureDevelopment/ConfigureProduction/ConfigureStaging...) public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // 判断自定义环境 //if (env.IsEnvironment("OK")) //{ // app.UseDeveloperExceptionPage(); //} // featch static files //app.UseStaticFiles(); // redirection http request to https request app.UseHttpsRedirection(); // Authentication app.UseAuthentication(); // routing app.UseRouting(); app.UseEndpoints(endpoints => { //endpoints.MapGet("/", async context => //{ // await context.Response.WriteAsync("Hello World!"); //}); //MVC use routing table endpoints.MapControllerRoute("default", "{controller=Department}/{action=Index}/{id?}"); //MVC use controller routing attribute endpoints.MapControllers(); } } ``` #### ASPNETCORE_ENVIRONMENT - \Properties\launchSettings.json ```json { "profiles": { "AspCoreDemo": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } ``` #### bootstrap ##### a. Using npm package.json file install bootstrap * - Add > New Item > npm Configuration file : package.json ```json { "version": "1.0.0", "name": "asp.net", "private": true, "devDependencies": { "bootstrap": "4.4.1" } } ``` ##### b. Using libman Install bootstrap - Add > Client-Side Library > bootstrap@4.4.1 > Install, libman.json ```json { "version": "1.0", "defaultProvider": "unpkg", "libraries": [ { "library": "bootstrap@4.4.1", "destination": "wwwroot/lib/bootstrap/", "files": [ "dist/css/bootstrap.css" ] } ] } ``` #### Using BuildBundlerMinifier merge and minify css files - Add > New Item, bundleconfig.json ```json [ { "outputFileName": "wwwroot/css/all.min.css", "inputFiles": [ "wwwroot/css/site.css", "wwwroot/lib/bootstrap/dist/css/bootstrap.css" ] }, { "outputFileName": "wwwroot/css/bootstrap.css", "inputFiles": [ "wwwroot/lib/bootstrap/dist/css/bootstrap.css" ], "minify": {"enabled": true} } ] ``` - Manage NuGet packges > BuildBundlerMinifier #### 使用MVC相关技术 * Controller * Tag Helper * Settings * View Component * Razor page #### Models ```c# public class Department { public int Id { get; set; } public string Name { get; set; } public string Location { get; set; } public int EmployeeCount { get; set; } } ``` #### Services ```c# public class DepartmentService : IDepartmentService { private readonly List _departments = new List(); public DepartmentService() { _departments.Add(new Department { Id = 1, Name = "HR", Location ="ShangHai", EmployeeCount = 10 }); //... } public Task Add(Department department) { department.Id = _departments.Max(x => x.Id) + 1; _departments.Add(department); return Task.CompletedTask; } public Task> GetAll() { return Task.Run(() => _departments.AsEnumerable()); } public Task GetById(int id) { return Task.Run(() => _departments.FirstOrDefault(x => x.Id == id)); } public Task GetCompanySummary() { return Task.Run(() => { return new CompanySummary { EmployeeCount = _departments.Sum(x => x.EmployeeCount), AverageDepartmentEmployeeCount = (int)_departments.Average(x => x.EmployeeCount) }; }); } } ``` #### Register Services - Startup.cs ```c# public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddSingleton(); } ``` #### Controllers ```c# public class DepartmentController : Controller { private readonly IDepartmentService departmentService; public DepartmentController(IDepartmentService departmentService) { this.departmentService = departmentService; } public async Task Index() { ViewBag.Title = "Department Index"; var departments = await departmentService.GetAll(); return View(departments); } public IActionResult Add() { ViewBag.Title = "Add Department"; return View(new Department()); } [HttpPost] public async Task Add(Department department) { if (ModelState.IsValid) { await departmentService.Add(department); } return RedirectToAction(nameof(Index)); } } ``` #### Views - Views > _ViewImports.cshtml 全局启用TagHelper: Add > New Item > Razor View Imports ```html @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" ``` - Views > Shared > _Layout.cshtml 公共页面 : Add > New Item > Razor Layout ```html @ViewBag.Title
logo
@ViewBag.Title
@RenderBody()
``` - Views > _ViewStart.cshtml start 页面: Add > New Item > Razor View Start ```html @{ Layout = "_Layout"; } ``` - Views > Department > Index.cshtml Index页面 : Add > New Item > Razor View ```html @using AspCoreDemo.Models @model IEnumerable
@Html.DisplayForModel()
Name Location EmployeeCount Opration
Add
``` - Views > Department > DisplayTemplates > Department.cshtml 模板页面: Add > New Item > Razore View ```html @model AspCoreDemo.Models.Department @Model.Name @Model.Location @Model.EmployeeCount Employees ``` - Views > Department > Add.cshtml Add页面 : Add > New Item > Razor View ````html @using AspCoreDemo.Models @model Department
```` #### Asp .Net Core 的配置信息源 * appsettings.json * appsettings.{Environment}.json * Secret Manager * 环境变量 * 命令行参数 (相同参数,下面的配置覆盖前面配置) #### appsettings.json ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "AspCoreDemo": { "BoldDepartmentEmployeeCount": 30 } } ``` #### Startup.cs 注入Config,映射为options类 ```c# private readonly IConfiguration configuration; public Startup(IConfiguration configuration) { this.configuration = configuration; } // register service public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddSingleton(); services.AddSingleton(); services.Configure(configuration.GetSection("AspCoreDemo")); } ``` #### Controller 注入options ```c# private readonly IDepartmentService departmentService; private readonly IOptions options; public DepartmentController(IDepartmentService departmentService, IOptions options) { this.departmentService = departmentService; this.options = options; } ``` #### .cshtml注入options ```html @using AspCoreDemo.Models @using Microsoft.Extensions.Options @model AspCoreDemo.Models.Department @inject IOptions options @if (Model.EmployeeCount > options.Value.BoldDepartmentEmployeeCount) { @Model.Name } else { @Model.Name } @Model.Location @Model.EmployeeCount Employees ``` #### ViewComponet 可重用组件 - ViewComponents > CompanySummaryViewComponent.cs component类 ```c# public class CompanySummaryViewComponent : ViewComponent { private readonly IDepartmentService departmentService; public CompanySummaryViewComponent(IDepartmentService departmentService) { this.departmentService = departmentService; } public async Task InvokeAsync(string title) { ViewBag.Title = title; var companySummary = await departmentService.GetCompanySummary(); return View(companySummary); } } ``` - Views\Shared\Components\CompanySummary\Default.cshtml component页面 ```html @model AspCoreDemo.Models.CompanySummary
@ViewBag.Title
Employee Count:
@Model.EmployeeCount
Average Count :
@Model.AverageDepartmentEmployeeCount
``` - Views\\_ViewImports.cshtml 引入本项目程序集 ```html @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" @addTagHelper "*, AspCoreDemo" ``` - Views\Department\Index.cshtml 使用ViewComponent ```html
@await Component.InvokeAsync("CompanySummary", new { title = "Summary of Company" })
Add
``` ### 2. Razor Page #### MVC * Model: 数据 * View: Html, Razor, TagHelpers * Controller: 逻辑 #### Razor Page * 数据 * Html, Razor, TagHelpers * 逻辑 #### Create Project - New > Project > Asp .Net core web application #### Startup.cs ```c# public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseHttpsRedirection(); app.UseAuthentication(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } } ``` #### Create Models, Service and Register services - Startup.cs ```c# public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddSingleton(); services.AddSingleton(); } ``` #### Config配置信息源 - appsettings.json ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "AspCoreRazorDemo": { "BoldDepartmentEmployeeC: 30 } } ``` #### Startup.cs 注入Config,映射为options类 ```c# private readonly IConfiguration configuration; public Startup(IConfiguration configuration) { this.configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddSingleton(); services.AddSingleton(); services.Configure(configuration.GetSection("AspCoreRazorDemo")); } ``` #### Using libman Install bootstrap - Add > Client-Side Library > bootstrap@4.4.1 > Install, libman.json ```json { "version": "1.0", "defaultProvider": "unpkg", "libraries": [ { "library": "bootstrap@4.4.1", "destination": "wwwroot/lib/bootstrap/", "files": [ "dist/css/bootstrap.css" ] } ] } ``` #### Using BuildBundlerMinifier Merge and Minify css files - Add > New Item, bundleconfig.json ```json [ { "outputFileName": "wwwroot/css/all.min.css", "inputFiles": [ "wwwroot/css/site.css", "wwwroot/lib/bootstrap/dist/css/bootstrap.css" ] }, { "outputFileName": "wwwroot/css/bootstrap.css", "inputFiles": [ "wwwroot/lib/bootstrap/dist/css/bootstrap.css" ], "minify": {"enabled": true} } ] ``` - Manage NuGet packges > BuildBundlerMinifier #### ASPNETCORE_ENVIRONMENT - \Properties\launchSettings.json ```json { "profiles": { "AspCoreRazorDemo": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } ``` #### Views - Views > _ViewImports.cshtml 全局启用TagHelper : Add > New Item > Razor View Imports ```html @using AspCoreRazorDemo @namespace AspCoreRazorDemo.Pages @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" ``` - Views > Shared > _Layout.cshtml 公共页面 : Add > New Item > Razor Layout ```html @ViewBag.Title
logo
@ViewBag.Title
@RenderBody()
``` - Views > _ViewStart.cshtml start 页面: Add > New Item > Razor View Start ```html @{ Layout = "_Layout"; } ``` - Pages > Index.cshtml : Add > NewItem > Razor View ```html @page @using AspCoreRazorDemo.Models @using AspCoreRazorDemo.Services @inject IDepartmentService departmentService
@Html.DisplayFor(x => x.Departments)
Name Location EmployeeCount Opration
Add
@functions { public IEnumerable Departments { get; set; } public async Task OnGetAsync() { Departments = await departmentService.GetAll(); } } ``` - Pages > DisplayTemplates > Department.cshtml: Add > NewItem > Razor View ```html @using AspCoreRazorDemo.Models @using Microsoft.Extensions.Options @model AspCoreRazorDemo.Models.Department @inject IOptions options @if (Model.EmployeeCount > options.Value.BoldDepartmentEmployeeCount) { @Model.Name } else { @Model.Name } @Model.Location @Model.EmployeeCount ``` - Pages > Department> AddDepartment.cshtml: Add > New Item > Razor Page ```html @page @model AspCoreRazorDemo.Pages.Department.AddDepartmentModel
``` - Pages > Department> AddDepartment.cshtml.cs ```c# public class AddDepartmentModel : PageModel { private readonly IDepartmentService departmentService; [BindProperty] public AspCoreRazorDemo.Models.Department Department { get; set; } public AddDepartmentModel(IDepartmentService departmentService) { this.departmentService = departmentService; } public async Task OnPostAsync() { if (ModelState.IsValid) { await departmentService.Add(Department); return RedirectToPage("/Index"); } return Page(); } } ``` #### ViewComponet 可重用组件 - Pages> Shared> Components> CompanySummary> Default.cshtml - ViewComponents > CompanySummaryViewComponent.cs - Views\\_ViewImports.cshtml 引入本项目程序集 ```html @using AspCoreRazorDemo @namespace AspCoreRazorDemo.Pages @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" @addTagHelper "*, AspCoreRazorDemo" ``` - Pages\Index.cshtml 使用ViewComponent ```html
@await Component.InvokeAsync("CompanySummary", new { title = "Summary of Company" })
Add
``` ### 3. SignalR 实时web应用技术:默认采用回落机制 - webSocket - server sent enevts (SSE) - long polling SignalR采用RPC范式进行客户端与服务端的通信:RPC(Remote procedure call)可以像调用本地方法一样调用远程服务。 Hub: SignalR的一个组件,运行再Asp .net core 应用里的一个服务端的类,进行服务端与客户端通信。Hub支持Json和MessagePack协议 #### Create Project - New > Project > Asp .Net core web application #### Startup.cs ```c# public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSignalR(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } ``` #### ASPNETCORE_ENVIRONMENT - \Properties\launchSettings.json ```json { "profiles": { "SignalRDemo": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } ``` #### Services > ICountService.cs & CountService.cs ```c# public interface ICountService { public Task GetCount(); } public class CountService : ICountService { private int _count; public Task GetCount() { return Task.Run(() => _count++); } } ``` #### CountHub.cs ```c# //[Authorize] public class CountHub : Hub { private readonly ICountService countService; public CountHub(ICountService countService) { this.countService = countService; } public async Task GetLatestCount(string maxValue) { //var userName = Context.User.Identity.Name; int count; do { count = await countService.GetCount(); Thread.Sleep(1000); //incoke all clients method. await Clients.All.SendAsync("ReciveUpdate", count); } while (count < int.Parse(maxValue)); //incoke all clients method. await Clients.All.SendAsync("Finsihed"); } public async override Task OnConnectedAsync() { var connectionId = Context.ConnectionId; var client = Clients.Client(connectionId); //incoke client method. await client.SendAsync("someFunc", new { random= "Init" }); //incoke except client method. await Clients.AllExcept(connectionId).SendAsync("someFunc"); await Groups.AddToGroupAsync(connectionId, "MyGroup"); await Groups.RemoveFromGroupAsync(connectionId, "MyGroup"); await Clients.Group("MyGroup").SendAsync("someFunc"); } } ``` #### Controllers > CountController.cs ```c# [Route("api/count")] public class CountController : Controller { private readonly IHubContext countHub; public CountController(IHubContext countHub) { this.countHub = countHub; } [HttpPost] public async Task Post(string random) { await countHub.Clients.All.SendAsync("someFunc", new { random = "Start" }); return Accepted(10); } } ``` #### Using libman Install bootstrap - Add > Client-Side Library > @aspnet/signalr@1.1.4 > Install, libman.json ```josn { "version": "1.0", "defaultProvider": "unpkg", "libraries": [ { "library": "@aspnet/signalr@1.1.4", "destination": "wwwroot/lib/@aspnet/signalr/", "files": [ "dist/browser/signalr.js" ] } ] } ``` #### Client - wwwroot > index.html 客户端html ```html
``` - wwwroot > index.js 客户端js ```javascript let connection = null; setupConnection = () => { //设置使用longPolling //connection = new signalR.HubConnectionBuilder() // .withUrl("/counthub", signalR.HttpTransportType.LongPolling) // .build(); connection = new signalR.HubConnectionBuilder() .withUrl("/counthub") .build(); connection.on("ReciveUpdate", (update) => { const resultDiv = document.getElementById("result"); resultDiv.innerHTML = update; }); connection.on("someFunc", function (obj) { const resultDiv = document.getElementById("result"); resultDiv.innerHTML = "Someone called, parametes: " + obj.random; }); connection.on("Finsihed", function (obj) { const resultDiv = document.getElementById("result"); resultDiv.innerHTML = "Finsihed"; }); connection.start() .catch(err => console.error(err.toString())); } setupConnection(); document.getElementById("submit").addEventListener("click", e => { e.preventDefault(); fetch("/api/count", { method: "POST", headers: { 'content-type': 'application/json' } }) .then(response => response.text()) .then(maxValue => connection.invoke("GetLatestCount", maxValue)); }) ``` ### 4. Blazor 基于Component的编程模型,Blazor宿主模型: - 客户端宿主模型: - Mono解释器: - 开源的.NET Freamework - 可以解释IL(中间语言) - 代码的IL是包含在.NET的Assembly里 - 浏览器可以执行mono,(webAssembly) - mono将Assembly的代码解析为WebAssembly - 服务端宿主模型 #### Create Project - New > Project > Asp .Net core web application #### Startup.cs ```c# public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); }); } } ``` #### Create Models, Service and Register services Startup.cs ```c# public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton(); services.AddSingleton(); } ``` #### Config配置信息源 - appsettings.json ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "BlazorAppDemo": { "BoldDepartmentEmployeeC: 30 } } ``` #### Startup.cs 注入Config,映射为options类 ```c# private readonly IConfiguration configuration; public Startup(IConfiguration configuration) { this.configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddSingleton(); services.AddSingleton(); services.Configure(configuration.GetSection("BlazorAppDemo")); } ``` #### Using libman Install bootstrap Add > Client-Side Library > bootstrap@4.4.1 > Install, libman.json ```json { "version": "1.0", "defaultProvider": "unpkg", "libraries": [ { "library": "bootstrap@4.4.1", "destination": "wwwroot/lib/bootstrap/", "files": [ "dist/css/bootstrap.css" ] } ] } ``` #### Using BuildBundlerMinifier Merge and Minify css files - Add > New Item, bundleconfig.json ```json [ { "outputFileName": "wwwroot/css/all.min.css", "inputFiles": [ "wwwroot/css/site.css", "wwwroot/lib/bootstrap/dist/css/bootstrap.css" ] }, { "outputFileName": "wwwroot/css/bootstrap.css", "inputFiles": [ "wwwroot/lib/bootstrap/dist/css/bootstrap.css" ], "minify": {"enabled": true} } ] ``` - Manage NuGet packges > BuildBundlerMinifier #### ASPNETCORE_ENVIRONMENT - \Properties\launchSettings.json ```json { "profiles": { "BlazorAppDemo": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } ``` #### Views - Pages > _Host.cshtml: Add > NewItem > Razor View ```html @page "/" @namespace BlazorAppDemo.Pages @addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers Blazore App Demo ``` - App.razor: Add > NewItem > Razor Component ```html

Sorry, there's nothing at this address.

``` - _Imports.razor : Add > NewItem > Razor Component ```c# @using System.Net.Http @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web @using Microsoft.JSInterop @using BlazorAppDemo.Models @using BlazorAppDemo.Services ``` - Shared > MainLayout.razor: Add > NewItem > Razor Component ```html @inherits LayoutComponentBase
logo
Blazor App Demo
@Body
``` - _Imports.razor ``` @using BlazorAppDemo.Shared ``` - Pages > Index.razor: Add > NewItem > Razor Component ```html @page "/" @inject IDepartmentService departmentService @if (departments == null) {

loading...

} else {
@foreach (var item in departments) { }
Name Location EmployeeCount Opration
Add
} @code { IEnumerable departments; protected override async Task OnInitializedAsync() { departments = await departmentService.GetAll(); } } ``` - Componets > DepartmentItem.razor: Add > NewItem > Razor Component ```html @using Microsoft.Extensions.Options @inject IOptions options @if (Department.EmployeeCount > options.Value.BoldDepartmentEmployeeCount) { @Department.Name } else { @Department.Name } @Department.Location @Department.EmployeeCount @code { [Parameter] public Department Department { get; set; } } ``` - _Imports.razor ``` @using BlazorAppDemo.Components ``` - Pages > Department > AddDepartment.razor: Add > NewItem > Razor Component ```html @page "/add-department" @inject IDepartmentService departmentService @inject NavigationManager navigationManager
@code { private Department department = new Department(); private string employeeCount; private async Task HandleValidSubmit() { department.EmployeeCount = int.Parse(employeeCount); await departmentService.Add(department); navigationManager.NavigateTo("/"); } } ``` ### 5. Web api - RESTful Api - Repository - Controller ### 6. gRPC - gRPC: google Remote Procedure Call - protobuf: Protocol Buffers(.proto) - gRPC服务定义 - 服务端与客户端之间传递的消息