使用Scalar.AspNetCore來管理你的OpenApi
一直覺得很好的一個組件,網上介紹少得可憐,沒辦法,只有自己爬官網了,又是對照git又是看doc文檔,總算是玩明白了,現在完全拋棄那個誰誰誰了。因人喜好各取所長吧
先來官方參考地址:
https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/openapi/include-metadata?view=aspnetcore-9.0&tabs=minimal-apis
這是scalar的.net 集成文檔地址
https://guides.scalar.com/scalar/scalar-api-references/integrations/net-aspnet-core/integration
github地址
https://github.com/scalar/scalar
先放個圖,誘惑一下,集成了很多主題,還可以自定主題(留給前端去玩吧)

一、簡單使用
1.建立一個API項目,(最小,mvc都可)
2.引用包
dotnet add package Scalar.AspNetCore (當前版本2.9.0)
3.添加引用
using Scalar.AspNetCore;
4.添加配置,在Program.cs中添加下面配置
builder.Services.AddOpenApi(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); app.MapScalarApiReference(); }
現在運行一下,看看,localhost:xxxx/scalar
是不是看到列出漂亮的界面了?
二、基本配置
1.自定義路由
不想使用/salar,可以換成自己的地址
app.MapScalarApiReference("/api-docs"); app.MapScalarApiReference("/docs");
2.多文當或版本控制
// Chain multiple documents app.MapScalarApiReference(options => { options.AddDocument("v1", "Production API", "api/v1/openapi.json") .AddDocument("v2-beta", "Beta API", "api/v2-beta/openapi.json", isDefault: true) .AddDocument("internal", "Internal API", "internal/openapi.json"); });
isDefault: true是默認打開的頁面
3.自定義文檔的默認調試語言
app.MapScalarApiReference(options =>
{
options.WithDefaultHttpClient(ScalarTarget.CSharp, ScalarClient.HttpClient);
});

它對應右邊窗口的語言,基本上都支持,java,php,rust,py,swift
三、高級配置
之前的老版本使用的硬編碼option加配置,2.9.0以后,在界面右上角菜單欄上出現了一個編輯配置功能

根據自己的喜好,調試編輯完配置文件后,可以復制到文件中單獨保存,真是太貼心了
{ "title": "Aquxa API Documentation", "slug": "aquxa-api-documentation", "hideClientButton": true, "servers": [ { "url": "http://localhost:5215", "description": "Development server" } ], "showSidebar": true, "showToolbar": "localhost",//這里特別說明一下,編輯完后,不想出現這個菜單欄,就在這里可以關閉showToolbar: "never" "operationTitleSource": "summary", "theme": "solarized",//主題可以自己選,喜歡哪個選哪個 "_integration": "dotnet", "persistAuth": false, "telemetry": true, "layout": "modern", "isEditable": false, "isLoading": false, "hideModels": true, "documentDownloadType": "both", "hideTestRequestButton": false, "hideSearch": false, "showOperationId": false, "hideDarkModeToggle": false, "favicon": "favicon.svg", "withDefaultFonts": true, "defaultOpenAllTags": false, "expandAllModelSections": true, "expandAllResponses": true, "orderSchemaPropertiesBy": "alpha", "orderRequiredPropertiesFirst": true, "url": "http://localhost:5215/openapi/v1.json" }
PS:這里特別說明一下,編輯完后,不想出現這個菜單欄,就在這里可以關閉showToolbar: "never"
得到這個文件,保存到wwwroot/js/scalar-config.js,注意,一定要保存到能訪問的靜態目錄里,并在program.cs添加靜態目錄的配置
app.UseStaticFiles(). //這個要放在scalar配置的前面,不然訪問不到
添加配置文件加載
.WithJavaScriptConfiguration("/js/scalar-config.js")

這里費了好大的勁,查官方,看代碼,因為官方文檔還是老文檔,只是簡單的概括了一下。最后整出來了
四、文檔的編輯
使用最重要的還是API文檔編輯,其實它完全用的標準的OpenApi,只要參考這個表就可以完全配置了

[ApiController] [Route("api/[controller]")] [ApiExplorerSettings(GroupName = "v1")] [Tags("Admin")] // 為整個控制器添加標簽 public class AdminController : ControllerBase { [HttpPost("reload-cache")] public IActionResult ReloadCache() { // 模擬重新加載緩存的操作 return Ok("Cache reloaded successfully"); } [HttpGet("stats")] public IActionResult GetStats() { return Ok(new { Users = 100, Requests = 1000 }); } }
下面說一下常用的特性
1.API分組
[ApiExplorerSettings]
這個比較熟悉,它可以分組,分版本,當你分好版本后[ApiExplorerSettings(GroupName = "v1")]/[ApiExplorerSettings(GroupName = "v2")],會在scalar中左上角可以選擇,當然,你也可以把它做為組來用

如果有不想顯示的API也可以用[ApiExplorerSettings(IgnoreApi = true)]來排除顯示
[HttpGet("/private")] [ApiExplorerSettings(IgnoreApi = true)] public IActionResult PrivateEndpoint() { return Ok("This is a private endpoint"); }
2.API分類
[Tags]
分類的API,會歸檔在一起,方便查詢,這樣看起來沒有那么亂了
[Tags(["Admin", "OtherAPI"])] [HttpGet("attributes")] public IResult Attributes() { return Results.Ok("Hello world!"); }

3.描述
[EndpointSummary("OtherApi")] [EndpointDescription("這是一個公開接口,無需認證")] [HttpGet("attributes")] public IResult Attributes() { return Results.Ok("Hello world!"); }

4.過濾
不想顯示的接口可以用
上面說的
[ApiExplorerSettings(IgnoreApi = true)]來關閉
還有一個就是根目錄,如果在配置文件中有MapGet,可以使用.ExcludeFromDescription();排除顯示
// 默認打開首頁
app.MapGet("/", () => "Hangfire 服務運行中。訪問 /hangfire 查看儀表盤,訪問 /docs 查看API文檔").ExcludeFromDescription();
//可以使用.ExcludeFromDescription();排除顯示
更多編輯文檔就看這里吧
https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/openapi/include-metadata?view=aspnetcore-9.0&tabs=controllers
五、認證授權
這里就使用自己的授權就可以,這里就偷懶找AI完成了。參考部分都有備注
using Scalar.AspNetCore; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Options; using System.Security.Claims; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Mvc; using MyWebApi; // 添加對WeatherForecast的引用 var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi("v1"); builder.Services.AddOpenApi("v2"); // 添加控制器服務 builder.Services.AddControllers(); // 添加身份驗證服務 builder.Services.AddAuthentication("BasicAuthentication") .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null); // 添加授權服務 builder.Services.AddAuthorization(options => { options.AddPolicy("ScalarAccess", policy => policy.RequireAuthenticatedUser()); }); // 配置服務器URL,避免端口沖突 builder.WebHost.UseUrls("http://localhost:5215"); var app = builder.Build(); // Configure static file middleware to serve the JavaScript config file app.UseStaticFiles(); // 添加身份驗證和授權中間件 app.UseAuthentication(); app.UseAuthorization(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.MapOpenApi(); // Add Scalar for API management with JavaScript configuration and authorization app.MapScalarApiReference("/scalar", options => { options.WithTitle("MyWebApi") .WithJavaScriptConfiguration("/js/scalar-config.js") .AddDocument("v1", "Aquxa API Documentation",isDefault: true) .AddDocument("v2", "Beta API"); }) .RequireAuthorization("ScalarAccess"); // 應用授權策略 } // 添加控制器路由 app.MapControllers(); app.Run(); // Basic Authentication Handler public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { public BasicAuthenticationHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) { } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { // 檢查是否有Authorization頭 if (!Request.Headers.ContainsKey("Authorization")) return AuthenticateResult.NoResult(); try { // 解析Basic認證頭 var authHeader = Request.Headers["Authorization"].ToString(); if (!authHeader.StartsWith("Basic ")) return AuthenticateResult.NoResult(); var encodedCredentials = authHeader.Substring("Basic ".Length).Trim(); var decodedCredentials = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encodedCredentials)); var credentials = decodedCredentials.Split(':', 2); var username = credentials[0]; var password = credentials[1]; // 驗證用戶名和密碼(這里使用硬編碼,實際應用中應從配置或數據庫獲?。?/span> if (username == "admin" && password == "password123") { var claims = new[] { new Claim(ClaimTypes.Name, username) }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket); } return AuthenticateResult.Fail("Invalid username or password"); } catch { return AuthenticateResult.Fail("Invalid Authorization Header"); } } protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { // 發送WWW-Authenticate頭以觸發瀏覽器的認證對話框 Response.Headers["WWW-Authenticate"] = "Basic realm=\"Scalar API Documentation\""; await base.HandleChallengeAsync(properties); } }


浙公網安備 33010602011771號