Blazor web App的结构——(二)
一 创建解决方案及项目架构
1 创建解决方案
dotnet new sln -o KBlazorWebAppDemo
将在目录KBlazorWebAppDemo下生成一个解决方案文件KBlazorWebAppDemo.sln
2 创建项目
进入子目录:cd KBlazorWebAppDemo
创建新项目:dotnet new web -o KBWebApp
3 将项目加到解决方案
dotnet sln add KBWebApp\KBWebApp.csproj
4 查看目录结构
tree /f

查看program.cs文件
Minimal API使用四行代码构建一个web程序;默认监听7238和5174端口;
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run();
这就是最基础的项目架构了,接下来丰富内容;
二 添加Blazor服务
1 添加Razor服务
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
2 添加静态文件使用并添加静态文件目录wwwroot
app.UseStaticFiles();
3 加入路由
添加目录components, components/pages
在components目录下添加App.razor
4 在Pages目录下创建一个页面
Home.razor(可以任意命名),修改Home.razor: 添加路由 @page "/"
5 加入razor交互模式
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
至此,Blazer最基本框架已经创建。
三 定义组件(页面)特定样式
1 样式隔离的作用
将 CSS 样式隔离到各个页面、视图和组件以减少或避免:依赖难以维护的全局样式,嵌套内容中样式冲突;
2 定义组件特定的样式
相同文件夹中创建一个.razor.css,例如创建MainLayout.razor.css;
3 启用样式隔离
3.1 CSS隔离捆绑
在App.razor中添加引用表样式:

3.2 programs.cs中添加对静态资源文件使用的支持
app.MapStaticAssets();

到此,一个最简单的Blazor Web App就创建出来了。
四 其中的问题
1 图标不显示
<link rel="icon" type="image/png" href="Kerry.png" />
解决方法:将图标文件小写<link rel="icon" type="image/png" href="kerry.png" />
2 注册组件——密码合规验证
在Blazor密码合规验证时,如果密码不合规会跳到注册确认组件;相关原始代码如下:
var result = await UserManager.CreateAsync(user, Input.Password); if (!result.Succeeded) { identityErrors = result.Errors; } Logger.LogInformation("User created a new account with password."); var userId = await UserManager.GetUserIdAsync(user); string code = await UserManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = NavigationManager.GetUriWithQueryParameters( NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, new Dictionary<string, object?>() { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl }); await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); Dictionary<string, object?> input = new Dictionary<string, object?>() { ["email"] = Input.Email, ["returnUrl"] = ReturnUrl}; if (UserManager.Options.SignIn.RequireConfirmedAccount) { RedirectManager.RedirectTo("Account/RegisterConfirmation", input); }
个人认为,如果密码合规,应该给出相关提示,代码如下:
ApplicationUser user = CreateUser(); await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); IUserEmailStore<ApplicationUser> emailStore = GetEmailStore(); await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); var result = await UserManager.CreateAsync(user, Input.Password); if (!result.Succeeded) { identityErrors = result.Errors; return; }
即在执行result = await UserManager.CreateAsync(user, Input.Password)后,得到失败结果后,应该直接返回显示相关信息。
五 NavMenu
1 菜单图标
以下代码中<span>中的class定义菜单图标
<NavLink class="nav-link" href="todo">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span>Todo
</NavLink>
2 关于logout
其中action="Account/Logout",当在菜单上点击[Logout]时,会执行await signInManager.SignOutAsync();
<div class="nav-item px-3"> <form action="Account/Logout" method="post"> <AntiforgeryToken/> <input type="hidden" name="ReturnUrl" value="@currentUrl" /> <button type="submit" class="nav-link"> <span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true"/>Logout </button> </form> </div>
这个方法在IdentityComponentsEndpointRouteBuilderExtensions.cs文件MapAdditionalIdentityEndpoints()方法中以委托方式进行注册。
方法的签名为public static IEndpointConventionBuilder MapAdditionalIdentityEndpoints(this IEndpointRouteBuilder endpoints),其中使用了this表明是接口IEndpointRouteBuilder的扩展方法,而WebApplication继承了IEndpointRouteBuilder,因此WebApplication的实例可以直接调用。
accountGroup.MapPost("/Logout", async ( ClaimsPrincipal user, SignInManager<ApplicationUser> signInManager, [FromForm] string returnUrl) => { await signInManager.SignOutAsync(); return TypedResults.LocalRedirect($"~/{returnUrl}"); });
因而在program.cs中可以直接调用:app.MapAdditionalIdentityEndpoints();
浙公网安备 33010602011771号