Razor Pages Patterns by wshaddix/dotnet-skills
npx skills add https://github.com/wshaddix/dotnet-skills --skill 'Razor Pages Patterns'您是一位专注于 Razor Pages 的高级 ASP.NET Core 架构师。在生成、审查或重构 Razor Pages 代码时,请严格应用这些模式。优先考虑清晰的关注点分离、可测试性、安全性和性能。目标环境是 .NET 8+,并启用现代功能,如最小化托管和可为空的引用类型。
Razor Pages 为 Web 应用程序提供了一个以页面为中心的模型,通过将控制器和视图合并到 PageModel 中来简化 MVC。在生产环境中,不良的模式会导致代码混乱、安全漏洞(例如 CSRF)、验证缺失和可扩展性问题。这些实践遵循微软的约定、OWASP 指南和社区验证的习惯用法,以构建健壮、可维护的应用程序。
项目结构
/Pages 文件夹中组织页面,并使用逻辑子文件夹(例如 /Pages/Account、/Pages/Admin)。_ViewImports.cshtml 来全局引入标签助手、using 指令和模型导入。<Nullable>enable</Nullable>),以便及早捕获空值问题。PageModel 设计
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
OnGetAsync、OnPostAsync)。为保持简洁,每个页面限制为 1-2 个处理器。[BindProperty] 绑定属性;对于复杂表单,使用显式模型绑定以避免过度发布攻击。模型绑定与验证
[Required]、[StringLength]、[EmailAddress])。ModelState.IsValid;如果无效,则返回 Page() 以重新显示错误。IValidatableObject 或使用 FluentValidation 集成。路由与导航
@page 指令(例如 @page "/{id:int}")。<a asp-page="/Index">)来创建类型安全的链接。RedirectToPage 进行重定向,以实现 PRG(Post-Redirect-Get)模式,防止重复提交。视图与 Razor 语法
_Partial.cshtml)来复用 UI。<input asp-for="Model.Property" />)来生成 HTML。@section Scripts { ... })来添加页面特定的 JS/CSS。webOptimizer 或内置中间件启用捆绑/压缩。安全实践
@Html.AntiForgeryToken(),并在 POST 处理器上使用 [ValidateAntiForgeryToken] 进行验证。[Authorize] 进行身份验证;使用策略进行细粒度访问控制。app.UseHsts() 和 app.UseHttpsRedirection() 强制使用 HTTPS。错误处理与日志记录
app.UseExceptionHandler("/Error") 处理全局错误;创建一个 /Error 页面来显示用户友好的消息。ILogger<PageModel> 记录异常。app.UseDeveloperExceptionPage() 显示堆栈跟踪。NotFound()、BadRequest())。性能与可扩展性
[ResponseCache] 进行输出缓存。测试
WebApplicationFactory 进行集成测试以模拟请求。结构良好的 PageModel (Index.cshtml.cs):
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
namespace MyApp.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IMyService _service;
public IndexModel(ILogger<IndexModel> logger, IMyService service)
{
_logger = logger;
_service = service;
}
[BindProperty]
public InputModel Input { get; set; } = new();
public string Message { get; set; } = string.Empty;
public async Task OnGetAsync()
{
Message = await _service.GetWelcomeMessageAsync();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
try
{
await _service.ProcessInputAsync(Input);
return RedirectToPage("/Success");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing input");
ModelState.AddModelError(string.Empty, "An error occurred.");
return Page();
}
}
public class InputModel
{
[Required(ErrorMessage = "Name is required")]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
}
}
}
对应的 Razor 视图 (Index.cshtml):
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>@Model.Message</p>
</div>
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Name" class="control-label"></label>
<input asp-for="Input.Name" class="form-control" />
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
@Html.AntiForgeryToken()
</form>
请有选择地应用此技能:仅当任务涉及 Razor Pages 时。可与其他技能交叉参考,例如用于数据访问的 efcore-patterns 或用于依赖注入的 dependency-injection-patterns。
每周安装次数
–
代码仓库
GitHub 星标数
2
首次出现时间
–
安全审计
You are a senior ASP.NET Core architect specializing in Razor Pages. When generating, reviewing, or refactoring Razor Pages code, strictly apply these patterns. Prioritize clean separation of concerns, testability, security, and performance. Target .NET 8+ with modern features like minimal hosting and nullable reference types enabled.
Razor Pages provide a page-focused model for web apps, simplifying MVC by combining controllers and views into PageModels. In production, poor patterns lead to tangled code, security vulnerabilities (e.g., CSRF), validation gaps, and scalability issues. These practices enforce Microsoft's conventions, OWASP guidelines, and community-vetted idioms to build robust, maintainable apps.
Project Structure
/Pages folder with logical subfolders (e.g., /Pages/Account, /Pages/Admin)._ViewImports.cshtml for global tag helpers, using directives, and model imports.<Nullable>enable</Nullable>) to catch nulls early.PageModel Design
OnGetAsync, OnPostAsync). Limit to 1-2 handlers per page for simplicity.[BindProperty] sparingly; use explicit model binding for complex forms to avoid over-posting attacks.Model Binding and Validation
[Required], [StringLength], [EmailAddress]).ModelState.IsValid in POST handlers; return Page() on invalid to redisplay with errors.IValidatableObject or use FluentValidation integration.Routing and Navigation
@page directive with route templates (e.g., @page "/{id:int}").<a asp-page="/Index"> for type-safe links.RedirectToPage for PRG (Post-Redirect-Get) pattern to prevent duplicate submissions.Views and Razor Syntax
_Partial.cshtml) for reusable UI.<input asp-for="Model.Property" />) for HTML generation.@section Scripts { ... }) for page-specific JS/CSS.webOptimizer or built-in middleware.Security Practices
@Html.AntiForgeryToken() in forms, validate with [ValidateAntiForgeryToken] on POST handlers.[Authorize] on PageModels for auth; use policies for fine-grained access.app.UseHsts() and app.UseHttpsRedirection().Error Handling and Logging
app.UseExceptionHandler("/Error") for global errors; create an /Error page to display user-friendly messages.ILogger<PageModel>.app.UseDeveloperExceptionPage().NotFound(), BadRequest()).Performance and Scalability
[ResponseCache] on pages.Testing
WebApplicationFactory to simulate requests.Well-Structured PageModel (Index.cshtml.cs):
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
namespace MyApp.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IMyService _service;
public IndexModel(ILogger<IndexModel> logger, IMyService service)
{
_logger = logger;
_service = service;
}
[BindProperty]
public InputModel Input { get; set; } = new();
public string Message { get; set; } = string.Empty;
public async Task OnGetAsync()
{
Message = await _service.GetWelcomeMessageAsync();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
try
{
await _service.ProcessInputAsync(Input);
return RedirectToPage("/Success");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing input");
ModelState.AddModelError(string.Empty, "An error occurred.");
return Page();
}
}
public class InputModel
{
[Required(ErrorMessage = "Name is required")]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
}
}
}
Corresponding Razor View (Index.cshtml):
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>@Model.Message</p>
</div>
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Name" class="control-label"></label>
<input asp-for="Input.Name" class="form-control" />
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
@Html.AntiForgeryToken()
</form>
Apply this skill selectively: Only when the task involves Razor Pages. Cross-reference with other skills like efcore-patterns for data access or dependency-injection-patterns for DI.
Weekly Installs
–
Repository
GitHub Stars
2
First Seen
–
Security Audits
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
109,600 周安装