MiniController 2.0.0

dotnet add package MiniController --version 2.0.0
                    
NuGet\Install-Package MiniController -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MiniController" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MiniController" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="MiniController" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MiniController --version 2.0.0
                    
#r "nuget: MiniController, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MiniController@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MiniController&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=MiniController&version=2.0.0
                    
Install as a Cake Tool

MiniController

MiniController 是一个基于 Roslyn Incremental Source Generator 的 ASP.NET Core Minimal API 组织工具。它使用 attribute 声明路由、绑定、授权和 OpenAPI 元数据,并在编译期生成 endpoint 注册代码。

NuGet License: MIT

运行基线

  • 生产样例与 CI 使用 .NET 10。
  • Generator 和 Attributes 包保持 netstandard2.0
  • 仓库通过 global.json 固定已验证的稳定 SDK。

安装

dotnet add package MiniController --version 2.0.0
dotnet add package MiniController.Attributes --version 2.0.0
<ItemGroup>
  <PackageReference Include="MiniController" Version="2.0.0" PrivateAssets="all" />
  <PackageReference Include="MiniController.Attributes" Version="2.0.0" />
</ItemGroup>

MiniController 是 analyzer-only 包,不会成为应用的运行时依赖。

快速开始

using Microsoft.AspNetCore.Mvc;
using MiniController.Attributes;

namespace MyApi;

[MiniController("/api/products")]
public sealed class ProductController
{
    [HttpGet("{id:int}")]
    [ProducesResponseType(typeof(Product), StatusCodes.Status200OK)]
    public IResult GetProduct(
        [FromRoute(Name = "id")] int productId,
        [FromQuery(Name = "include-details")] bool includeDetails = false)
    {
        return TypedResults.Ok(new Product(productId, includeDetails));
    }
}

public sealed record Product(int Id, bool IncludeDetails);
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMiniControllers();

var app = builder.Build();
app.MapMiniController();
app.Run();

实例 controller 自动以 Transient 生命周期注册;静态类不进入 DI。

路由

显式路由

[MiniController("/api/orders")]
public static class OrderEndpoints
{
    [HttpGet("{id:int}")]
    public static IResult GetOrder(int id) => TypedResults.Ok(id);
}

名称推断

省略 HTTP attribute 时,可按方法名前缀推断:

  • Get* → GET
  • Post*Create*Add* → POST
  • Put*Update* → PUT
  • Delete*Remove* → DELETE
  • Patch*Head*Options* → 对应 method

默认 controller 路由会移除 ControllerServiceEndpointEndpoints 后缀并转为 kebab-case。例如 OrderHistoryController 对应 /api/order-history

路由 token

支持 [area][controller][action]

[Area("Admin")]
[MiniController("/api/[area]/[controller]/[action]")]
public static class ReportEndpoints
{
    [HttpGet("weekly")]
    public static IResult GetWeeklyReport() => TypedResults.Ok();
}

上例路由为 GET /api/admin/report/weekly。当模板包含 [action] 时,显式 method template 是 action token 的值,不会再次追加到路径。

继承

抽象基类可以定义共享 endpoint,但不会自身注册:

[MiniController("/api/[controller]")]
public abstract class BaseController
{
    [HttpGet("health")]
    public virtual IResult GetHealth() => TypedResults.Ok();
}

public sealed class StatusController : BaseController;

派生类的 override/new 声明优先。两个具体类型若形成相同 HTTP method 与 route,编译器会报告 MC001 Error,并定位两个冲突方法。

开放泛型 controller 或嵌套在开放泛型中的 controller 会报告 MC002

参数绑定

实例方法包装器保留以下信息:

  • [FromServices]
  • [FromRoute(Name = "...")]
  • [FromQuery(Name = "...")]
  • [FromHeader(Name = "...")]
  • [FromBody]
  • [FromForm(Name = "...")]
  • nullable 标注和显式默认值
[HttpGet("search")]
public IResult Search(
    [FromQuery(Name = "q")] string keyword,
    [FromHeader(Name = "X-Tenant")] string tenant,
    [FromQuery] int page = 1)
{
    return TypedResults.Ok(new { keyword, tenant, page });
}

授权

类级和方法级的多个 [Authorize] 会全部保留,并按 ASP.NET Core 的组合规则执行。RolesPolicyAuthenticationSchemes 会作为 AuthorizeAttribute 元数据生成。

[MiniController("/api/security")]
[Authorize(Policy = "tenant")]
public sealed class SecurityController
{
    [HttpGet("admin")]
    [Authorize(Roles = "Admin", AuthenticationSchemes = "Bearer")]
    public IResult GetAdmin() => TypedResults.Ok();

    [HttpGet("public")]
    [AllowAnonymous]
    public IResult GetPublic() => TypedResults.Ok();
}

应用仍需配置 authentication、authorization middleware 和 policy。

Endpoint filters

使用可重复的 [MiniControllerFilter] 添加类级或方法级 filter:

public sealed class LoggingFilter : IEndpointFilter
{
    public ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context,
        EndpointFilterDelegate next) => next(context);
}

[MiniController("/api/filtered")]
[MiniControllerFilter(typeof(LoggingFilter))]
public static class FilteredEndpoints
{
    [HttpGet]
    public static IResult GetValue() => TypedResults.Ok();
}

多个 filter 按声明顺序生成。为兼容 1.x,MiniControllerAttribute.FilterType 仍可用。

OpenAPI metadata

ProducesResponseTypeApiExplorerSettings.IgnoreApiApiExplorerSettings.GroupName 会映射为 Minimal API metadata:

[MiniController("/api/reports")]
[ApiExplorerSettings(GroupName = "Admin")]
public static class ReportEndpoints
{
    [HttpGet]
    [ProducesResponseType(typeof(Report), StatusCodes.Status200OK)]
    public static IResult GetReport() => TypedResults.Ok(new Report(1));
}

.NET 10 第一方 OpenAPI 配置:

builder.Services.AddOpenApi();
builder.Services.AddOpenApi("Admin");

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

GroupName 使用 .WithGroupName(...),与 endpoint tag 是不同概念。

Native AOT

当 SDK 启用 AOT analyzer(例如项目设置 <PublishAot>true</PublishAot>)时,Generator 自动改用显式 RequestDelegate,不会调用带 Delegate 参数且要求反射/动态代码的 route overload。

AOT backend 支持:

  • controller 与方法参数的 DI
  • HttpContextHttpRequestHttpResponseClaimsPrincipalCancellationToken
  • route、query、header 的 binding name 与默认值
  • string、enum、StringValues 和实现 IParsable<T> 的标量
  • 单个 JSON body
  • voidIResultstring、JSON DTO,以及对应的 Task/ValueTask 返回
  • authorization、OpenAPI metadata 和 endpoint filters

所有 JSON body、直接 JSON 返回和 typed ProducesResponseType 根类型都必须出现在应用的 JsonSerializerContext 中,并将该 context 注册到 HTTP JSON options:

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});

[JsonSerializable(typeof(CreateItemRequest))]
[JsonSerializable(typeof(ItemResponse))]
internal partial class AppJsonSerializerContext : JsonSerializerContext;

缺少 required route/query/header 值或解析失败返回 400;缺少 required body、null body 或 JSON 无效返回 400;非 JSON body 返回 415。

form、[AsParameters]、keyed services、ref 参数、generic endpoint method 和其他无法静态生成的 binder 当前会报告 MC003,而不是退回反射。JSON 根类型未登记会报告 MC004。项目不需要也不应 suppress IL2026/IL3050。

生成内容

Generator 为每个 controller 生成独立扩展,并生成聚合入口:

  • Map{ControllerName}(IEndpointRouteBuilder)
  • MapMiniController(IEndpointRouteBuilder)
  • AddMiniControllers(IServiceCollection),仅在存在实例 controller 时生成

全局命名空间、嵌套类型、不同命名空间中的同名类型和 C# 关键字标识符均使用稳定的完整类型身份生成。

诊断

ID Severity 含义
MC001 Error 两个具体 endpoint 具有相同 HTTP method 与规范化 route
MC002 Error controller 类型形状无法安全注册,例如开放泛型
MC003 Error Native AOT backend 不支持 endpoint 的 binding 或返回形状
MC004 Error Native AOT 使用的 JSON 根类型未声明在 JsonSerializerContext

从 1.x 升级

2.0.0 包含有意的行为修正:

  • 重复路由由运行时歧义改为编译错误。
  • [action] 中的显式 method template 替换 token,不再重复追加。
  • 抽象 controller 不注册,但其 endpoint 可被具体派生类继承。
  • 默认 controller 名使用 kebab-case,并优先移除最长后缀。
  • ApiExplorerSettings.GroupName 不再错误映射成 tag。
  • 多重授权、binding alias、nullable/default values 和 typed Produces 不再丢失。

仓库验证

dotnet test MiniController.sln -c Release -warnaserror

dotnet pack MiniController.Attributes/MiniController.Attributes.csproj \
  -c Release -o artifacts/packages -warnaserror
dotnet pack MiniController/MiniController.csproj \
  -c Release -o artifacts/packages -warnaserror

NUGET_PACKAGES="$PWD/artifacts/package-consumer-cache" \
  dotnet test tests/MiniController.PackageConsumer/Tests/MiniController.PackageConsumer.Tests.csproj \
  -c Release -warnaserror

SampleApi 展示 .NET 10 第一方 OpenAPI;SampleAotApi 覆盖 DI、route/query/header、默认值、JSON body/response、异步返回、filter 和 native HTTP smoke。

增量 generator 基准独立运行,不属于功能测试门禁:

dotnet run -c Release \
    --project benchmarks/MiniController.Generator.Benchmarks \
    -- --filter '*IncrementalGeneratorBenchmarks*'

License

MIT

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 97 8/28/2026
1.0.5 264 9/1/2025
1.0.4 592 7/22/2025
1.0.3 227 7/16/2025
1.0.2 225 7/15/2025
1.0.1 188 7/11/2025
1.0.0 179 7/11/2025