完成了《AD 密码验证》与《AD 密码修改》两个 API 后,接下来用 .NET MAUI Blazor 来写桌面端的程式。稍微了解了 Blazor 后,觉得好神奇,感觉就是把网页塞进应用程式里面,运行时还可以在「工作管理员」看到背后有 Edge 的相关元件在运行,然后按 F12 还会有开发者工具视窗!不过这也让我衍生了一点安全性的疑虑。但整体感觉还是挺特别的,刚好我对网页语法比较熟,搭配 Razor 来设计 UI 画面,感觉亲切不少。
程式的结构是两个页面,第一个页面是 AD 密码验证,通过后才会导到 AD 密码变更页面。
《专案范本》
- .NET MAUI Blazor 应用程式
《套件》
- 引导程序
服务/PasswordManagementService.cs
using System.Net.Http.Json; namespace NTool.Services { public class PasswordManagementService(HttpClient httpClient) { public async Task< bool > ValidateAsync( string username, string password) { var request = new { Username = username, Password = password }; var response = await httpClient.PostAsJsonAsync( "Password/Validate" , request); return response.IsSuccessStatusCode; } public async Task< bool > ChangeAsync( string username, string password, string newPassword) { var request = new { Username = username, OldPassword = password, NewPassword = newPassword }; var response = await httpClient.PostAsJsonAsync( "Password/ChangeSdsP" , request); return response.IsSuccessStatusCode; } } } |
组件/页面/PasswordValidation.razor
@page "/" @ using NTool.Services @ using NTool.Models @inject PasswordManagementService PasswordManagementService @inject ValidRequest ValidRequest @inject NavigationManager Navigation <h3 class = "mt-5 text-center" >AD 密碼變更工具</h3> <p>請驗證帳號密碼</p> <EditForm Model= "ValidRequest" OnValidSubmit= "HandleValidSubmit" > <DataAnnotationsValidator /> <ValidationSummary /> <div> <label for = "username" >使用者登入名稱</label> <InputText id= "username" @bind-Value= "ValidRequest.Username" placeholder= "輸入帳號" /> </div> <div> <label for = "password" >密碼</label> <InputText id= "password" @bind-Value= "ValidRequest.Password" placeholder= "輸入密碼" type= "password" /> </div> <button type= "submit" >驗證</button> </EditForm> <p>@resultMessage</p> @code { private string resultMessage = string .Empty; private async Task HandleValidSubmit() { var isValid = await PasswordManagementService.ValidateAsync(ValidRequest.Username, ValidRequest.Password); if (isValid) { Navigation.NavigateTo( "/PasswordChange" ); } else { resultMessage = "驗證失敗。" ; } } } |
组件/页面/PasswordChange.razor
@page "/PasswordChange" @ using NTool.Models @ using NTool.Services @inject PasswordManagementService PasswordManagementService @inject HttpClient Http @inject NavigationManager Navigation @inject ChangeRequest ChangeRequest @inject ValidRequest ValidRequest <h3 class = "mt-5 text-center" >AD 密碼變更工具</h3> <p>@Message</p> <EditForm Model= "ChangeRequest" OnValidSubmit= "HandleChangeSubmit" > <DataAnnotationsValidator /> <ValidationSummary /> <div> <label for = "newPassword" >新密碼</label> <InputText id= "newPassword" @bind-Value= "ChangeRequest.NewPassword" placeholder= "輸入新密碼" type= "password" /> </div> <div> <label for = "confirmPassword" >確認新密碼</label> <InputText id= "confirmPassword" @bind-Value= "ChangeRequest.ConfirmPassword" placeholder= "確認新密碼" type= "password" /> </div> <button type= "submit" >變更密碼</button> </EditForm> @code { private string Message { get ; set ; } = "驗證成功!請變更密碼" ; private async Task HandleChangeSubmit() { var isValid = await PasswordManagementService.ChangeAsync(ValidRequest.Username, ValidRequest.Password, ChangeRequest.NewPassword); if (isValid) { Message = "密碼變更成功。" ; ValidRequest.Password = string .Empty; ChangeRequest.NewPassword = string .Empty; ChangeRequest.ConfirmPassword = string .Empty; // Navigation.NavigateTo("/"); } else { Message = "密碼變更失敗。" ; ChangeRequest.NewPassword = string .Empty; ChangeRequest.ConfirmPassword = string .Empty; } } } |
模型/PasswordModel.cs
using System.ComponentModel.DataAnnotations; namespace NTool.Models { public class ValidRequest { [Required(ErrorMessage = "[使用者登入名稱] 為必填欄位。" )] public required string Username { get ; set ; } [Required(ErrorMessage = "[密碼] 為必填欄位。" )] public required string Password { get ; set ; } } public class ChangeRequest { [Required(ErrorMessage = "[新密碼] 為必填欄位。" )] public required string NewPassword { get ; set ; } [Required(ErrorMessage = "[確認新密碼] 為必填欄位。" )] [Compare( "NewPassword" , ErrorMessage = "[新密碼] 與 [確認新密碼] 不相符。" )] public required string ConfirmPassword { get ; set ; } } } |
MauiProgram.cs
using Microsoft.Extensions.Logging; using NTool.Services; using NTool.Models; namespace NTool { public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont( "OpenSans-Regular.ttf" , "OpenSansRegular" ); }); builder.Services.AddMauiBlazorWebView(); #if DEBUG builder.Services.AddBlazorWebViewDeveloperTools(); builder.Logging.AddDebug(); #endif // 注入 HttpClient,並設定 API 的基底網址 builder.Services.AddScoped(sp => new HttpClient { }); // 注入 Services、Models builder.Services.AddScoped<PasswordManagementService>(); builder.Services.AddScoped<ValidRequest>(); builder.Services.AddScoped<ChangeRequest>(); return builder.Build(); } } } |
wwwroot/css/styles.css
body { background-color : #f0f8ff ; /* 淡粉藍背景色 */ font-family : Arial , sans-serif ; margin : 0 ; padding : 0 ; } form { max-width : 400px ; margin : 50px auto ; background : white ; border-radius : 8px ; box-shadow : 0 4px 8px rgba ( 0 , 0 , 0 , 0.1 ); padding : 20px ; } form div { margin-bottom : 15px ; } form label { display : block ; font-weight : bold ; margin-bottom : 5px ; } form input { width : 100% ; padding : 8px ; border : 1px solid #ccc ; border-radius : 4px ; } form button { width : 100% ; padding : 10px ; background-color : #4caf50 ; color : white ; border : none ; border-radius : 4px ; font-size : 16px ; cursor : pointer ; } form button:hover { background-color : #45a049 ; } p { text-align : center ; color : #ff0000 ; font-weight : bold ; } |
《结果画面》