plugins/languages/csharp/skills/web/SKILL.md
ASP.NET Core 10 Web 开发规范。覆盖 Minimal API、native AOT 发布、Blazor SSR / Streaming / Auto / Interactive 渲染模式、rate limiting、output caching、HybridCache、 middleware 顺序、IExceptionHandler + ProblemDetails、OpenAPI 3.1、健康检查、 JWT / Identity API、WebApplicationFactory 集成测试。 当开发 Web API、REST 服务、Blazor 应用、配置中间件管道、调优 ASP.NET Core, 或说 "ASP.NET Core"、"Minimal API"、"Blazor"、"middleware"、"WebApplication"、 "AOT publish"、"HybridCache" 时加载。
npx skillsauth add lazygophers/ccplugin csharp-webInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
主流形态: Minimal API + EF Core + Blazor SSR。
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddProblemDetails()
.AddOpenApi() // .NET 9+ 内置 (替代 Swashbuckle)
.AddOutputCache()
.AddHybridCache() // .NET 10 GA, 替代 IDistributedCache
.AddExceptionHandler<GlobalExceptionHandler>()
.AddRateLimiter(o => o.AddFixedWindowLimiter("api", w =>
{
w.PermitLimit = 100;
w.Window = TimeSpan.FromMinutes(1);
}))
.AddAuthentication().AddJwtBearer();
builder.Services.AddDbContextPool<AppDb>(o =>
o.UseNpgsql(builder.Configuration.GetConnectionString("Db")));
var app = builder.Build();
app.UseExceptionHandler(); // 早期捕获
app.UseStatusCodePages();
app.UseHttpsRedirection();
app.UseRateLimiter();
app.UseAuthentication();
app.UseAuthorization();
app.UseOutputCache();
app.MapOpenApi();
app.MapHealthChecks("/health");
app.MapOrders(); // 扩展方法分组 endpoint
app.Run();
public partial class Program; // 让集成测试可见
每个资源一个静态类, 扩展方法 + MapGroup:
public static class OrderEndpoints
{
public static IEndpointRouteBuilder MapOrders(this IEndpointRouteBuilder app)
{
var g = app.MapGroup("/api/orders")
.RequireAuthorization()
.RequireRateLimiting("api")
.WithTags("orders");
g.MapGet("/{id:long}", GetById).WithName("GetOrder").CacheOutput();
g.MapPost("/", Create).AddEndpointFilter<ValidationFilter>();
return app;
}
static async Task<Results<Ok<OrderDto>, NotFound>> GetById(
long id, IOrderService svc, CancellationToken ct) =>
await svc.FindAsync(id, ct) is { } o ? TypedResults.Ok(o) : TypedResults.NotFound();
}
Results<T1, T2> / TypedResults.* 让 OpenAPI 推断准确Results.Ok (非类型化), 用 TypedResults.Ok[FromBody] / [FromQuery] / [FromServices] / [AsParameters]public class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) : IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(
HttpContext ctx, Exception ex, CancellationToken ct)
{
logger.LogError(ex, "Unhandled: {Message}", ex.Message);
await Results.Problem(
title: "Internal Server Error",
statusCode: StatusCodes.Status500InternalServerError,
extensions: new Dictionary<string, object?> { ["traceId"] = ctx.TraceIdentifier })
.ExecuteAsync(ctx);
return true;
}
}
业务错误: endpoint 直接返回 TypedResults.Problem(...) / ValidationProblem。
record + required + 数据注解; 复杂规则用 Microsoft.AspNetCore.Http.Validation (.NET 10 内置) 或 FluentValidationAddProblemDetails)<PublishAot>true</PublishAot>
要求:
Newtonsoft.Json; 用 System.Text.Json source generatordotnet publish -c Release 检查 IL2026 / IL3050 警告[JsonSerializable(typeof(OrderDto))]
[JsonSerializable(typeof(CreateOrderDto))]
internal partial class AppJsonContext : JsonSerializerContext;
builder.Services.ConfigureHttpJsonOptions(o =>
o.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default));
| 模式 | 何时用 |
|------|--------|
| Static SSR | 列表/详情页面, 纯展示 |
| SSR Streaming ([StreamRendering]) | 大段数据分块渲染 |
| Interactive Server | 内网工具, 状态在服务端 |
| Interactive WebAssembly | 高交互、可离线 |
| Auto | SSR 首屏 + WebAssembly 接管 |
组件以 @rendermode 显式声明; 不要全站默认 Interactive。
固定顺序: Exception → HTTPS → Static → Routing → CORS → AuthN → AuthZ → RateLimiter → OutputCache → Endpoints。
自定义中间件继承 IMiddleware (DI 友好) 而不是约定方法。
AddJwtBearer; Authority + Audience 必填IAuthorizationHandler + OperationAuthorizationRequirementUser.HasClaim, 用 policyAddIdentityApiEndpoints<T>() 提供注册/登录/MFA 全套 endpointCacheOutput(...)IDistributedCachevar u = await cache.GetOrCreateAsync($"user:{id}",
async ct => await db.Users.FindAsync([id], ct),
tags: ["users"]);
AddOpenTelemetry().WithTracing().WithMetrics().WithLogs()Microsoft.AspNetCore.Hosting、Microsoft.AspNetCore.Server.KestrelWebApplicationFactory<Program>; Program 加 public partial class Program;WithWebHostBuilder + ConfigureTestServicesdevelopment
Go 数据库规范——GORM Model 命名 ModelXxx、表名单数、枚举 uint8 + 常量、索引 idx_ 前缀 + deleted_at leading column、禁 time.Time 统一 int64 unix、禁指针/nullable 字段、TEXT/BLOB/JSON 禁 default、AutoMigrate 禁改主键。设计 DB model、写 GORM tag、建索引、做 migration 审查时触发。
development
Go HTTP API 规范——响应始终 200 + body code 字段、路由 /api/* 全 POST 单段 <Action><Model>、中间件逐路由注册禁 Group(prefix,mw...)、handler 仅返回 (rsp,error)、认证走 header。设计 HTTP API、写路由/handler/中间件时触发。
development
Go 项目结构规范——三层架构(API → Impl → State)、全局状态模式、internal/ 私有包、cmd/ 仅 main.go、go.work 多模块、禁止 Repository 接口和 DI 容器、struct 公共字段开头全 omitempty、handler var rsp 顶声明、禁 legacy migration。设计项目骨架、新建目录、组织包、做架构评审时触发。
development
Go 命名规范——Id/Uid 字段(非 ID)、IsActive/HasMFA 布尔前缀、CreatedAt 时间字段、接收者统一用 p、包名全小写无下划线、泛型类型参数描述性命名、集合字段 xxx_list 禁 xxxs 复数、Enum 0 值 XxxNil 禁 Unknown、禁 Status 统一 State、Set/Update 语义区分。定义结构体字段、函数、变量、包、接收者名、泛型、枚举时触发。