本质是 一个Server
+ 多个中间件(Middleware)组成的管道(Pipline)
。
本身是一个console application
,经 Main() > CreateHostBuilder(args).Build()
后成为web application
。
- MVC:/Home/Index
- Razor Pages: /SomePage
- SignalR: /Hub/Chat
- New > Project > Asp .Net core web application
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>();
//根据不同的环境找不同的Startup类,(StartupDevelopment/StartupProduction/StartupStaging...)
//webBuilder.UseStartup(typeof(Program));
//webBuilder.UseKestrel(); 可以不写,源码默认调用
});
}
// 不同环境可配置不同类,(StartupDevelopment/StartupProduction/StartupStaging...)
public class Startup
{
// config service, 不同环境可配置不同方法(ConfigureServicesDevelopment/ConfigureServicesProduction/ConfigureServicesStaging...)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
//services.AddControllers();
//services.AddMvc();
//services.AddSingleton<IClock, ChinaClock>();
services.AddSingleton<IClock, UtcClock>();
}
// 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();
}
}
-
\Properties\launchSettings.json
{ "profiles": { "AspCoreDemo": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
- Add > New Item > npm Configuration file : package.json
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"bootstrap": "4.4.1"
}
}
- Add > Client-Side Library > bootstrap@4.4.1 > Install, libman.json
{
"version": "1.0",
"defaultProvider": "unpkg",
"libraries": [
{
"library": "bootstrap@4.4.1",
"destination": "wwwroot/lib/bootstrap/",
"files": [
"dist/css/bootstrap.css"
]
}
]
}
- Add > New Item, bundleconfig.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
- Controller
- Tag Helper
- Settings
- View Component
- Razor page
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public int EmployeeCount { get; set; }
}
public class DepartmentService : IDepartmentService
{
private readonly List<Department> _departments = new List<Department>();
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<IEnumerable<Department>> GetAll()
{
return Task.Run(() => _departments.AsEnumerable());
}
public Task<Department> GetById(int id)
{
return Task.Run(() => _departments.FirstOrDefault(x => x.Id == id));
}
public Task<CompanySummary> GetCompanySummary()
{
return Task.Run(() =>
{
return new CompanySummary
{
EmployeeCount = _departments.Sum(x => x.EmployeeCount),
AverageDepartmentEmployeeCount = (int)_departments.Average(x => x.EmployeeCount)
};
});
}
}
- Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IDepartmentService, DepartmentService>();
}
public class DepartmentController : Controller
{
private readonly IDepartmentService departmentService;
public DepartmentController(IDepartmentService departmentService)
{
this.departmentService = departmentService;
}
public async Task<IActionResult> 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<IActionResult> Add(Department department)
{
if (ModelState.IsValid)
{
await departmentService.Add(department);
}
return RedirectToAction(nameof(Index));
}
}
-
Views > _ViewImports.cshtml 全局启用TagHelper: Add > New Item > Razor View Imports
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
-
Views > Shared > _Layout.cshtml 公共页面 : Add > New Item > Razor Layout
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<environment include="Development">
<link rel="stylesheet" asp-href-include="css/*" asp-href-exclude="css/all.main.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" asp-href-include="css/all.main.css" />
</environment>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-2">
<!-- taghelper: asp-append-version 防止图片被缓存 -->
<img asp-append-version="true" alt="logo" src="~/images/Home.png" style="height: 60px; width: 100px;" />
</div>
<div class="col-md-10">
<span class="h2">@ViewBag.Title</span>
</div>
</div>
<div class="row">
<div class="col-md-12">
@RenderBody()
</div>
</div>
</div>
</body>
</html>
- Views > _ViewStart.cshtml start 页面: Add > New Item > Razor View Start
@{
Layout = "_Layout";
}
- Views > Department > Index.cshtml Index页面 : Add > New Item > Razor View
@using AspCoreDemo.Models
@model IEnumerable<Department>
<div class="row">
<div class="col-md-10 offset-md-2">
<table class="table">
<tr>
<th>Name</th>
<th>Location</th>
<th>EmployeeCount</th>
<th>Opration</th>
</tr>
@Html.DisplayForModel()
</table>
</div>
</div>
<div class="row">
<div class="col-md-4 offset-md-2">
<a asp-action="Add">Add</a>
</div>
</div>
- Views > Department > DisplayTemplates > Department.cshtml 模板页面: Add > New Item > Razore View
@model AspCoreDemo.Models.Department
<tr>
<td>@Model.Name</td>
<td>@Model.Location</td>
<td>@Model.EmployeeCount</td>
<td>
<a asp-controller="Employee" asp-action="Index" asp-route-departmentId="@Model.Id">
Employees
</a>
</td>
</tr>
- Views > Department > Add.cshtml Add页面 : Add > New Item > Razor View
@using AspCoreDemo.Models
@model Department
<form asp-action="Add">
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Name"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Name" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Location"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Location" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="EmployeeCount"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="EmployeeCount" />
</div>
</div>
<div class="row">
<div class="col-md-2 offset-md-2">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>
-
appsettings.json
- appsettings.{Environment}.json
-
Secret Manager
-
环境变量
-
命令行参数
(相同参数,下面的配置覆盖前面配置)
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"AspCoreDemo": {
"BoldDepartmentEmployeeCount": 30
}
}
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
// register service
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IDepartmentService, DepartmentService>();
services.AddSingleton<IEmployeeService, EmpolyeeService>();
services.Configure<AspCoreDemoOptions>(configuration.GetSection("AspCoreDemo"));
}
private readonly IDepartmentService departmentService;
private readonly IOptions<AspCoreDemoOptions> options;
public DepartmentController(IDepartmentService departmentService, IOptions<AspCoreDemoOptions> options)
{
this.departmentService = departmentService;
this.options = options;
}
@using AspCoreDemo.Models
@using Microsoft.Extensions.Options
@model AspCoreDemo.Models.Department
@inject IOptions<AspCoreDemoOptions> options
<tr>
@if (Model.EmployeeCount > options.Value.BoldDepartmentEmployeeCount)
{
<td><strong>@Model.Name</strong></td>
}
else
{
<td>@Model.Name</td>
}
<td>@Model.Location</td>
<td>@Model.EmployeeCount</td>
<td>
<a asp-controller="Employee" asp-action="Index" asp-route-departmentId="@Model.Id">
Employees
</a>
</td>
</tr>
- ViewComponents > CompanySummaryViewComponent.cs component类
public class CompanySummaryViewComponent : ViewComponent
{
private readonly IDepartmentService departmentService;
public CompanySummaryViewComponent(IDepartmentService departmentService)
{
this.departmentService = departmentService;
}
public async Task<IViewComponentResult> InvokeAsync(string title)
{
ViewBag.Title = title;
var companySummary = await departmentService.GetCompanySummary();
return View(companySummary);
}
}
- Views\Shared\Components\CompanySummary\Default.cshtml component页面
@model AspCoreDemo.Models.CompanySummary
<div class="small">
<div class="row h6">@ViewBag.Title</div>
<div class="row">
<div class="col-md-10">Employee Count:</div>
<div class="col-md-2">@Model.EmployeeCount</div>
</div>
<div class="row">
<div class="col-md-10">Average Count :</div>
<div class="col-md-2">@Model.AverageDepartmentEmployeeCount</div>
</div>
</div>
-
Views\_ViewImports.cshtml 引入本项目程序集
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" @addTagHelper "*, AspCoreDemo"
-
Views\Department\Index.cshtml 使用ViewComponent
<div class="row">
<div class="col-md-2">
@await Component.InvokeAsync("CompanySummary", new { title = "Summary of Company" })
<vc:company-summary title="Summary"></vc:company-summary>
</div>
<div class="col-md-4">
<a asp-action="Add">Add</a>
</div>
</div>
- Model: 数据
- View: Html, Razor, TagHelpers
- Controller: 逻辑
- 数据
- Html, Razor, TagHelpers
- 逻辑
- New > Project > Asp .Net core web application
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();
});
}
}
-
Startup.cs
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddSingleton<IDepartmentService, DepartmentService>(); services.AddSingleton<IEmployeeService, EmpolyeeService>(); }
-
appsettings.json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "AspCoreRazorDemo": { "BoldDepartmentEmployeeC: 30 } }
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton<IDepartmentService, DepartmentService>();
services.AddSingleton<IEmployeeService, EmpolyeeService>();
services.Configure<AspCoreRazorDemoOptions>(configuration.GetSection("AspCoreRazorDemo"));
}
-
Add > Client-Side Library > bootstrap@4.4.1 > Install, libman.json
{ "version": "1.0", "defaultProvider": "unpkg", "libraries": [ { "library": "bootstrap@4.4.1", "destination": "wwwroot/lib/bootstrap/", "files": [ "dist/css/bootstrap.css" ] } ] }
-
Add > New Item, bundleconfig.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
-
\Properties\launchSettings.json
{ "profiles": { "AspCoreRazorDemo": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
-
Views > _ViewImports.cshtml 全局启用TagHelper : Add > New Item > Razor View Imports
@using AspCoreRazorDemo @namespace AspCoreRazorDemo.Pages @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
-
Views > Shared > _Layout.cshtml 公共页面 : Add > New Item > Razor Layout
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <environment include="Development"> <link rel="stylesheet" asp-href-include="css/*" asp-href-exclude="css/all.main.css" /> </environment> <environment exclude="Development"> <link rel="stylesheet" asp-href-include="css/all.main.css" /> </environment> </head> <body> <div class="container"> <div class="row"> <div class="col-md-2"> <!-- taghelper: asp-append-version 防止图片被缓存 --> <img asp-append-version="true" alt="logo" src="~/images/Home.png" style="height: 60px; width: 100px;" /> </div> <div class="col-md-10"> <span class="h2">@ViewBag.Title</span> </div> </div> <div class="row"> <div class="col-md-12"> @RenderBody() </div> </div> </div> </body> </html>
-
Views > _ViewStart.cshtml start 页面: Add > New Item > Razor View Start
@{ Layout = "_Layout"; }
-
Pages > Index.cshtml : Add > NewItem > Razor View
@page @using AspCoreRazorDemo.Models @using AspCoreRazorDemo.Services @inject IDepartmentService departmentService <div class="row"> <div class="col-md-10 offset-md-2"> <table class="table"> <tr> <th>Name</th> <th>Location</th> <th>EmployeeCount</th> <th>Opration</th> </tr> @Html.DisplayFor(x => x.Departments) </table> </div> </div> <div class="row"> <div class="col-md-4"> <a asp-page="Department/AddDepartment">Add</a> </div> </div> @functions { public IEnumerable<Department> Departments { get; set; } public async Task OnGetAsync() { Departments = await departmentService.GetAll(); } }
-
Pages > DisplayTemplates > Department.cshtml: Add > NewItem > Razor View
@using AspCoreRazorDemo.Models @using Microsoft.Extensions.Options @model AspCoreRazorDemo.Models.Department @inject IOptions<AspCoreRazorDemoOptions> options <tr> @if (Model.EmployeeCount > options.Value.BoldDepartmentEmployeeCount) { <td><strong>@Model.Name</strong></td> } else { <td>@Model.Name</td> } <td>@Model.Location</td> <td>@Model.EmployeeCount</td> </tr>
-
Pages > Department> AddDepartment.cshtml: Add > New Item > Razor Page
@page @model AspCoreRazorDemo.Pages.Department.AddDepartmentModel <form method="post"> <div class="row form-group"> <div class="col-md-2 offset-md-2"> <label asp-for="Department.Name"></label> </div> <div class="col-md-6"> <input class="form-control" asp-for="Department.Name" /> </div> </div> <div class="row form-group"> <div class="col-md-2 offset-md-2"> <label asp-for="Department.Location"></label> </div> <div class="col-md-6"> <input class="form-control" asp-for="Department.Location" /> </div> </div> <div class="row form-group"> <div class="col-md-2 offset-md-2"> <label asp-for="Department.EmployeeCount"></label> </div> <div class="col-md-6"> <input class="form-control" asp-for="Department.EmployeeCount" /> </div> </div> <div class="row"> <div class="col-md-2 offset-md-2"> <button type="submit" class="btn btn-primary">Add</button> </div> </div> </form>
-
Pages > Department> AddDepartment.cshtml.cs
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<IActionResult> OnPostAsync() { if (ModelState.IsValid) { await departmentService.Add(Department); return RedirectToPage("/Index"); } return Page(); } }
-
Pages> Shared> Components> CompanySummary> Default.cshtml
-
ViewComponents > CompanySummaryViewComponent.cs
-
Views\_ViewImports.cshtml 引入本项目程序集
@using AspCoreRazorDemo @namespace AspCoreRazorDemo.Pages @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" @addTagHelper "*, AspCoreRazorDemo"
-
Pages\Index.cshtml 使用ViewComponent
<div class="row"> <div class="col-md-2"> @await Component.InvokeAsync("CompanySummary", new { title = "Summary of Company" }) <vc:company-summary title="Summary"></vc:company-summary> </div> <div class="col-md-4"> <a asp-page="Department/AddDepartment">Add</a> </di> </div>
实时web应用技术:默认采用回落机制
- webSocket
- server sent enevts (SSE)
- long polling
SignalR采用RPC范式进行客户端与服务端的通信:RPC(Remote procedure call)可以像调用本地方法一样调用远程服务。
Hub: SignalR的一个组件,运行再Asp .net core 应用里的一个服务端的类,进行服务端与客户端通信。Hub支持Json和MessagePack协议
- New > Project > Asp .Net core web application
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();
});
}
}
-
\Properties\launchSettings.json
{ "profiles": { "SignalRDemo": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
public interface ICountService
{
public Task<int> GetCount();
}
public class CountService : ICountService
{
private int _count;
public Task<int> GetCount()
{
return Task.Run(() => _count++);
}
}
//[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<connectionId> method.
await client.SendAsync("someFunc", new { random= "Init" });
//incoke except client<connectionId> method.
await Clients.AllExcept(connectionId).SendAsync("someFunc");
await Groups.AddToGroupAsync(connectionId, "MyGroup");
await Groups.RemoveFromGroupAsync(connectionId, "MyGroup");
await Clients.Group("MyGroup").SendAsync("someFunc");
}
}
[Route("api/count")]
public class CountController : Controller
{
private readonly IHubContext<CountHub> countHub;
public CountController(IHubContext<CountHub> countHub)
{
this.countHub = countHub;
}
[HttpPost]
public async Task<IActionResult> Post(string random)
{
await countHub.Clients.All.SendAsync("someFunc", new { random = "Start" });
return Accepted(10);
}
}
-
Add > Client-Side Library > @aspnet/signalr@1.1.4 > Install, libman.json
{ "version": "1.0", "defaultProvider": "unpkg", "libraries": [ { "library": "@aspnet/signalr@1.1.4", "destination": "wwwroot/lib/@aspnet/signalr/", "files": [ "dist/browser/signalr.js" ] } ] }
-
wwwroot > index.html 客户端html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> <button id="submit">Submit</button> <div id="result" style="color: green; font-weight: bold; font-size: 24px;"></div> <script src="/lib/@aspnet/signalr/dist/browser/signalr.js"></script> <script src="index.js"></script> </body> </html>
-
wwwroot > index.js 客户端js
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)); })
基于Component的编程模型,Blazor宿主模型:
- 客户端宿主模型:
- Mono解释器:
- 开源的.NET Freamework
- 可以解释IL(中间语言)
- 代码的IL是包含在.NET的Assembly里
- 浏览器可以执行mono,(webAssembly)
- mono将Assembly的代码解析为WebAssembly
- Mono解释器:
- 服务端宿主模型
- New > Project > Asp .Net core web application
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");
});
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<IDepartmentService, DepartmentService>();
services.AddSingleton<IEmployeeService, EmpolyeeService>();
}
-
appsettings.json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "BlazorAppDemo": { "BoldDepartmentEmployeeC: 30 } }
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton<IDepartmentService, DepartmentService>();
services.AddSingleton<IEmployeeService, EmpolyeeService>();
services.Configure<BlazorAppDemoOptions>(configuration.GetSection("BlazorAppDemo"));
}
Add > Client-Side Library > bootstrap@4.4.1 > Install, libman.json
{
"version": "1.0",
"defaultProvider": "unpkg",
"libraries": [
{
"library": "bootstrap@4.4.1",
"destination": "wwwroot/lib/bootstrap/",
"files": [
"dist/css/bootstrap.css"
]
}
]
}
-
Add > New Item, bundleconfig.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
-
\Properties\launchSettings.json
{ "profiles": { "BlazorAppDemo": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
-
Pages > _Host.cshtml: Add > NewItem > Razor View
@page "/" @namespace BlazorAppDemo.Pages @addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Blazore App Demo</title> <base href="~/" /> <link rel="stylesheet" href="css/bootstrap.css" /> <link rel="stylesheet" href="css/site.css" /> </head> <body> <app> <component type="typeof(App)" render-mode="ServerPrerendered" /> </app> <script src="_framework/blazor.server.js"></script> </body> </html>
-
App.razor: Add > NewItem > Razor Component
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
-
_Imports.razor : Add > NewItem > Razor Component
@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
@inherits LayoutComponentBase <div class="container"> <div class="row"> <div class="col-md-2"> <img asp-append-version="true" alt="logo" src="/images/Home.png" style="height: 60px; width: 100px;" /> </div> <div class="col-md-10"> <span class="h2">Blazor App Demo</span> </div> </div> <div class="row"> <div class="col-md-12"> @Body </div> </div> </div>
-
_Imports.razor
@using BlazorAppDemo.Shared
-
-
Pages > Index.razor: Add > NewItem > Razor Component
@page "/" @inject IDepartmentService departmentService @if (departments == null) { <p><em>loading...</em></p> } else { <div class="row"> <div class="col-md-10 offset-md-2"> <table class="table"> <tr> <th>Name</th> <th>Location</th> <th>EmployeeCount</th> <th>Opration</th> </tr> @foreach (var item in departments) { <DepartmentItem department="@item"></DepartmentItem> } </table> </div> </div> <div class="row"> <div class="col-md-4"> <a href="/add-department">Add</a> </div> </div> } @code { IEnumerable<Department> departments; protected override async Task OnInitializedAsync() { departments = await departmentService.GetAll(); } }
-
Componets > DepartmentItem.razor: Add > NewItem > Razor Component
@using Microsoft.Extensions.Options @inject IOptions<BlazorAppDemoOptions> options <tr> @if (Department.EmployeeCount > options.Value.BoldDepartmentEmployeeCount) { <td><strong>@Department.Name</strong></td> } else { <td>@Department.Name</td> } <td>@Department.Location</td> <td>@Department.EmployeeCount</td> </tr> @code { [Parameter] public Department Department { get; set; } }
-
_Imports.razor
@using BlazorAppDemo.Components
-
-
Pages > Department > AddDepartment.razor: Add > NewItem > Razor Component
@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服务定义
- 服务端与客户端之间传递的消息