Blazor
Blazor他是一個開源的Web框架,不,這不是重點,重點是它可以使c#開發在瀏覽器上執行Web應用程式.它其實也簡化了SPA的開發過程.
Blazor = Browser + Razor
為什麼選擇Blazor?
Blazor可以讓.NET附有全棧開發功能,它可以使Web開發變得輕鬆而高效.而且Blazor是開源的,它得到了社群的大力支援,而且發展速度會很快.
它還擁有SPA的一些功能比如:
- 路由
- 依賴注入
- 服務端渲染
- Layout
等等
建立應用
如果說無法在看到Blazor WebAssembly App那麼執行如下命令即可.
dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview5.20216.8
專案結構如下所示
我們可以看到上圖中的專案結構
- BlazorServerCRUDSample.Client:該專案工程中包含了客戶端的相關內碼表面等檔案
- BlazorServerCRUDSample.Server:該專案工程中包含了webapi.
- BlazorServerCRUDSample.Shared:該類庫中用於存放客戶端和服務端之間的共享程式碼.
BlazorServerCRUDSample.Server
控制器程式碼如下所示
[Route("api/[controller]")]
public class StudentController : Controller
{
private readonly Shared.Data.AppContext _dbcontext;
public StudentController(Shared.Data.AppContext dbcontext)
{
this._dbcontext = dbcontext;
}
[HttpGet]
public async Task<List<Student>> Get()
{
return await _dbcontext.Students.AsQueryable().ToListAsync();
}
[HttpGet("{id}")]
public async Task<Student> Get(int id)
{
return await _dbcontext.Students.FindAsync(id);
}
[HttpPost]
public async Task Post([FromBody] Student student)
{
student.CreateTime = DateTime.Now;
if (ModelState.IsValid)
await _dbcontext.AddAsync(student);
await _dbcontext.SaveChangesAsync();
}
[HttpPut]
public void Put([FromBody] Student student)
{
if (ModelState.IsValid)
_dbcontext.Update(student);
_dbcontext.SaveChanges();
}
[HttpDelete("delete/{id}")]
public void Delete(int id)
{
var entity = _dbcontext.Students.Find(id);
_dbcontext.Students.Remove(entity);
_dbcontext.SaveChanges();
}
}
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseConfiguration(new ConfigurationBuilder()
.AddCommandLine(args)
.Build())
.UseStartup<Startup>()
.Build();
}
對於Startup類,我們可以看到在開發模式下,啟動Blazor除錯,並且我們可以看到我們通過UseClientSideBlazorFiles來啟動我們的客戶端Startup
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddResponseCompression();
services.AddDbContext<AppContext>(options =>
{
options.UseSqlServer("Data Source=.;Initial Catalog=BlazorServerCRUDSample;User ID=sa;Password=sa;MultipleActiveResultSets=true;");
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
app.UseStaticFiles();
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
}
}
BlazorServerCRUDSample.Client
如下所示我建立了一個列表頁面,在程式碼中我們可以看到@page他定義了該頁面的url,當然在razor中也是這樣的,而且下最下面我通過HttpClient進行我們的api呼叫,在這 System.Net.Http.Json這篇文章中我們也可以看到他簡直就是為了我們blazor而生的大大減少了我們的程式碼量.
而且在我的程式碼中最後一部分有一個@functions片段,它包含了頁面所有的業務邏輯,在我們頁面初始化時我們通過OnInitializedAsync方法進行呼叫我們的api然後將其進行填充賦值並填充到我們的html中.
@page "/fetchstudent"
@inject HttpClient Http
@using BlazorServerCRUDSample.Shared.Models
<h1>Students</h1>
<p>
<a href="/addstudent">Create New</a>
</p>
@if (students == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class='table'>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th>CreateTime</th>
</tr>
</thead>
<tbody>
@foreach (var student in students)
{
<tr>
<td>@student.Id</td>
<td>@student.Name</td>
<td>@student.Description</td>
<td>@student.CreateTime</td>
<td>
<a href='/editstudent/@student.Id'>Edit</a> |
<a href='/delete/@student.Id'>Delete</a>
</td>
</tr>
}
</tbody>
</table>
}
@functions {
Student[] students;
protected override async Task OnInitializedAsync()
{
students = await Http.GetJsonAsync<Student[]>("api/student");
}
}
如下程式碼中我們還是對我們的頁面提供了url,其中Id是將從url中的引數傳遞到我們的@functions程式碼中,在Id上面指定 [Parameter] 屬性,該屬性指定的就是url中的引數值.在這我們通過使用 @bind 來將我們的html元件和類物件進行雙向繫結.
@page "/editstudent/{Id}"
@inject HttpClient Http
@using BlazorServerCRUDSample.Shared.Models
@inject Microsoft.AspNetCore.Components.NavigationManager Navigation
<h2>Edit Student</h2>
<hr />
<div class="row">
<div class="col-md-4">
<form @onsubmit="@(async () => await UpdateStudent())">
<div class="form-group">
<label for="Name" class="control-label">Name</label>
<input for="Name" class="form-control" @bind="@student.Name" />
</div>
<div class="form-group">
<label asp-for="Description" class="control-label">Description</label>
<textarea asp-for="Description" class="form-control" @bind="@student.Description"> </textarea>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
<input type="submit" value="Cancel" @onclick="@cancel" class="btn btn-warning" />
</div>
</form>
</div>
</div>
@functions {
[Parameter]
public string id { get; set; }
public Student student = new Student();
protected override async Task OnInitializedAsync()
{
student = await Http.GetJsonAsync<Student>("/api/Student/" + Convert.ToInt32(id));
}
protected async Task UpdateStudent()
{
await Http.SendJsonAsync(HttpMethod.Put, "api/Student", student);
Navigation.NavigateTo("/fetchstudent");
}
void cancel()
{
Navigation.NavigateTo("/fetchstudent");
}
}
在ConfigureServices方法中,可以在依賴項注入容器中註冊本地服務。
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IComponentsApplicationBuilder app)
{
app.AddComponent<App>("app");
}
}
BlazorWebAssemblyHost可以用於在DI容器中定義介面和實現。
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) =>
BlazorWebAssemblyHost.CreateDefaultBuilder()
.UseBlazorStartup<Startup>();
}
Blazor可以基於服務端執行但是需要注意服務端的話需要為每一個客戶端開啟連線,並且我們必須一直與服務端保持連線才行.如果說切換到WebAssembly客戶端版本,限制是完全不同的,但是目前來說的話他首次需要下載一些執行時檔案到瀏覽器中.
通過如上程式碼我們可以看到一個簡單的blazor應用程式的建立,詳細程式碼的話大家可以看一下github倉庫中的內容.通過原始碼的話直接啟動BlazorServerCRUDSample.Server即可,希望可以通過本示例幫助到你~共同學習共同進步.