×

告别老旧 Swagger UI:在 .NET 里用 Scalar 打造更舒服的 API 文档

独孤求败 独孤求败 发表于2026-06-29 08:55:43 浏览22 评论0

抢沙发发表评论

Swagger UI 不是 OpenAPI,本质上只是一个展示工具

很多 .NET 开发者习惯把 Swagger UI 和 OpenAPI 混在一起说。文章一开始就强调了一个关键区别:OpenAPI 是规范,Swagger UI 是读取这个规范并展示出来的界面。

OpenAPI 描述 REST API 的路径、参数、请求体、响应、鉴权方式和 schema。它可以被很多工具消费:文档 UI、客户端生成器、测试工具、网关、Mock 服务。Swagger UI 只是其中一个 UI。

因此,从 Swagger UI 换到 Scalar,不是抛弃 OpenAPI,而是换一个更现代的展示和调试界面。

.NET 新模板变化带来的误解

很多人发现新建 .NET Web API 项目后,熟悉的 /swagger 页面不见了,于是以为 OpenAPI 支持被削弱了。实际不是这样。

新的 ASP.NET Core 已经能直接生成 OpenAPI 文档。变化在于:模板不再默认绑定老的 Swagger UI 方案,UI 需要开发者自行选择。

这是一种更清晰的分工:

  • • ASP.NET Core 负责生成 OpenAPI 文档。
  • • Scalar、Swagger UI 或其他工具负责展示文档。

这也意味着你可以保留标准文档输出,同时选择更适合团队体验的 UI。

最小 API 中启用 OpenAPI

先看一个简单的 .NET API。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();

var app = builder.Build();

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

app.MapGet("/products/{id:guid}"async (
    Guid id,
    ProductRepository repository,
    CancellationToken cancellationToken) =>
{
    var product = await repository.FindAsync(id, cancellationToken);
    return product isnull ? Results.NotFound() : Results.Ok(product);
})
.WithName("GetProduct")
.WithSummary("获取商品详情")
.WithDescription("根据商品 ID 查询商品详情。");

app.Run();

这段代码的关键是 AddOpenApi() 和 MapOpenApi()。前者注册生成能力,后者暴露 OpenAPI 文档端点。

加上 Scalar UI

Scalar 的价值在于提供更现代的 API 文档界面。它可以读取 OpenAPI 文档,并生成更适合日常调试的页面。

典型配置如下:

using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();

var app = builder.Build();

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

    app.MapScalarApiReference(options =>
    {
        options.Title = "商品服务 API";
        options.Theme = ScalarTheme.Moon;
        options.DefaultHttpClient = new(ScalarTarget.CSharp, ScalarClient.HttpClient);
    });
}

app.Run();

现在,OpenAPI 文档仍由 ASP.NET Core 生成,Scalar 只负责展示。这样结构更清楚,也更容易替换 UI。

给接口补充更好的文档信息

API 文档好不好,不只取决于 UI,也取决于元数据是否完整。接口命名、摘要、描述、响应类型都应该写清楚。

app.MapPost("/products"async (
    CreateProductRequest request,
    ProductRepository repository,
    CancellationToken cancellationToken) =>
{
    var product = Product.Create(
        request.Name,
        request.Price,
        request.Currency);

    await repository.AddAsync(product, cancellationToken);

    return Results.Created($"/products/{product.Id}", ProductResponse.From(product));
})
.WithName("CreateProduct")
.WithSummary("创建商品")
.WithDescription("创建一个新的商品,并返回创建后的商品信息。")
.Produces<ProductResponse>(StatusCodes.Status201Created)
.ProducesValidationProblem();

请求和响应模型也应保持清晰。

public sealed record CreateProductRequest(
    string Name,
    decimal Price,
    string Currency
)
;

public sealed record ProductResponse(
    Guid Id,
    string Name,
    decimal Price,
    string Currency
)

{
    public static ProductResponse From(Product product)
    {
        return new ProductResponse(
            product.Id,
            product.Name,
            product.Price,
            product.Currency);
    }
}

好的文档 UI 能提升体验,但如果接口元数据很差,再好的 UI 也只是把混乱展示得更漂亮。

生产环境是否应该公开 API 文档

很多项目在开发环境开放 API 文档,在生产环境关闭。这是合理默认值,但不是唯一选择。

如果生产环境需要开放文档,至少要考虑鉴权。

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.MapScalarApiReference();
}
else
{
    app.MapOpenApi()
        .RequireAuthorization("ApiDocs");

    app.MapScalarApiReference()
        .RequireAuthorization("ApiDocs");
}

还可以用配置开关控制:

var enableApiDocs = app.Configuration.GetValue<bool>("ApiDocs:Enabled");

if (enableApiDocs)
{
    app.MapOpenApi();
    app.MapScalarApiReference(options =>
    {
        options.Title = "内部 API 文档";
    });
}

不要默认把内部接口、调试接口和管理接口暴露给公网。API 文档本身不会执行危险操作,但它会清楚展示系统接口结构。

Swagger UI 是否已经不能用了

文章并不是说 Swagger UI 已经不能用。Swagger UI 仍然是成熟工具,生态广,团队熟悉度高。如果项目已经稳定使用它,也没有必要为了“新”而替换。

Scalar 更适合这些场景:

  • • 新建 .NET 9/10 API 项目。
  • • 团队觉得旧 Swagger UI 页面拥挤或过时。
  • • API 很多,需要更好的浏览和调试体验。
  • • 希望减少旧 Swashbuckle 依赖。
  • • 想让文档页面更符合现代开发者工具审美。

如果当前 Swagger UI 运行稳定、团队没有痛点,迁移优先级可以很低。

迁移建议

比较稳的迁移方式是先保留 OpenAPI 输出,再替换 UI。

  1. 1. 确认 /openapi/v1.json 或等价文档端点可用。
  2. 2. 补齐接口元数据,包括名称、摘要、响应。
  3. 3. 在开发环境加入 Scalar。
  4. 4. 让团队试用一段时间。
  5. 5. 如果体验满意,再决定是否移除旧 Swagger UI。

不要在同一个 PR 里同时重构 API、升级框架、替换文档工具。这样出了问题很难定位。

一个完整的示例结构

下面是一个简化但比较完整的 Program 配置。

using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();
builder.Services.AddOpenApi();
builder.Services.AddSingleton<ProductRepository>();

var app = builder.Build();

app.UseAuthorization();

app.MapOpenApi();

app.MapScalarApiReference(options =>
{
    options.Title = "商品中心 API";
    options.Theme = ScalarTheme.DeepSpace;
    options.DefaultHttpClient = new(ScalarTarget.CSharp, ScalarClient.HttpClient);
});

app.MapGet("/products/{id:guid}"async (
    Guid id,
    ProductRepository repository,
    CancellationToken cancellationToken) =>
{
    var product = await repository.FindAsync(id, cancellationToken);
    return product isnull ? Results.NotFound() : Results.Ok(ProductResponse.From(product));
})
.WithName("GetProduct")
.WithSummary("获取商品详情")
.Produces<ProductResponse>()
.Produces(StatusCodes.Status404NotFound);

app.MapPost("/products"async (
    CreateProductRequest request,
    ProductRepository repository,
    CancellationToken cancellationToken) =>
{
    var product = Product.Create(request.Name, request.Price, request.Currency);
    await repository.AddAsync(product, cancellationToken);
    return Results.Created($"/products/{product.Id}", ProductResponse.From(product));
})
.WithName("CreateProduct")
.WithSummary("创建商品")
.Produces<ProductResponse>(StatusCodes.Status201Created)
.ProducesValidationProblem();

app.Run();

示例仓储:

public sealedclassProductRepository
{
    privatereadonly Dictionary<Guid, Product> _products = new();

    public Task<Product?> FindAsync(Guid id, CancellationToken cancellationToken)
    {
        _products.TryGetValue(id, outvar product);
        return Task.FromResult(product);
    }

    public Task AddAsync(Product product, CancellationToken cancellationToken)
    {
        _products[product.Id] = product;
        return Task.CompletedTask;
    }
}

总结

这篇文章的重点是把 OpenAPI 和 Swagger UI 分开看。OpenAPI 是标准,仍然应该保留;Swagger UI 只是展示方式之一,可以被 Scalar 替换。

对于 .NET 项目来说,更现代的组合是:框架生成 OpenAPI 文档,Scalar 负责展示和交互。
但迁移的前提是团队确实有体验痛点,并且接口元数据足够清楚。

工具不会自动让 API 变好。好的 API 文档来自清晰的接口设计、完整的元数据和合适的展示界面。


群贤毕至

访客