使用Scalar.AspNetCore来管理你的OpenApi
<p><span style="font-size: 16px">一直觉得很好的一个组件,网上介绍少得可怜,没办法,只有自己爬官网了,又是对照git又是看doc文档,总算是玩明白了,现在完全抛弃那个谁谁谁了。因人喜好各取所长吧</span></p><p><span style="font-size: 16px">先来官方参考地址:</span></p>
<p><span style="font-size: 16px">https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/openapi/include-metadata?view=aspnetcore-9.0&tabs=minimal-apis</span></p>
<p><span style="font-size: 16px">这是scalar的.net 集成文档地址</span></p>
<p><span style="font-size: 16px">https://guides.scalar.com/scalar/scalar-api-references/integrations/net-aspnet-core/integration</span></p>
<p><span style="font-size: 16px">github地址</span></p>
<p><span style="font-size: 16px">https://github.com/scalar/scalar</span></p>
<p><span style="font-size: 16px">先放个图,诱惑一下,集成了很多主题,还可以自定主题(留给前端去玩吧)</span></p>
<p><img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017160114922-1289434129.png" alt="01" width="965" height="482" loading="lazy"></p>
<p> </p>
<p><span style="font-size: 16px"><strong>一、简单使用</strong></span></p>
<p>1.建立一个API项目,(最小,mvc都可)</p>
<p>2.引用包</p>
<p> dotnet add package Scalar.AspNetCore (当前版本2.9.0)</p>
<div> dotnet add package Microsoft.AspNetCore.OpenApi(当前版本10.0)</div>
<p>3.添加引用</p>
<p><span class="hljs-keyword">using Scalar.AspNetCore;</span></p>
<p><span class="hljs-keyword">4.添加配置,在Program.cs中添加下面配置</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">builder.Services.AddOpenApi();
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.MapScalarApiReference();
}</span></pre>
</div>
<p>现在运行一下,看看,localhost:xxxx/scalar</p>
<p>是不是看到列出漂亮的界面了?</p>
<p><strong><span style="font-size: 16px">二、基本配置</span></strong></p>
<p>1.自定义路由</p>
<p>不想使用/salar,可以换成自己的地址</p>
<div class="cnblogs_code">
<pre>app.MapScalarApiReference(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/api-docs</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
app.MapScalarApiReference(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/docs</span><span style="color: rgba(128, 0, 0, 1)">"</span>);</pre>
</div>
<p>2.多文当或版本控制</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Chain multiple documents</span>
app.MapScalarApiReference(options =><span style="color: rgba(0, 0, 0, 1)">
{
options.AddDocument(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">v1</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Production API</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">api/v1/openapi.json</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
.AddDocument(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">v2-beta</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Beta API</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">api/v2-beta/openapi.json</span><span style="color: rgba(128, 0, 0, 1)">"</span>, isDefault: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
.AddDocument(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">internal</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Internal API</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">internal/openapi.json</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<pre>isDefault: true是默认打开的页面<br>3.自定义文档的默认调试语言</pre>
<div class="cnblogs_code">
<pre>app.MapScalarApiReference(options =><span style="color: rgba(0, 0, 0, 1)">
{
options.WithDefaultHttpClient(ScalarTarget.CSharp, ScalarClient.HttpClient);
});</span></pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017162848040-1986574236.png" alt="02" width="744" height="242" loading="lazy"></p>
<p>它对应右边窗口的语言,基本上都支持,java,php,rust,py,swift</p>
<p><strong><span style="font-size: 16px">三、高级配置</span></strong></p>
<p>之前的老版本使用的硬编码option加配置,2.9.0以后,在界面右上角菜单栏上出现了一个编辑配置功能</p>
<p><img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017163326465-796338346.png" alt="03" width="346" height="612" loading="lazy"></p>
<p>根据自己的喜好,调试编辑完配置文件后,可以复制到文件中单独保存,真是太贴心了</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
</span>"title": "Aquxa API Documentation"<span style="color: rgba(0, 0, 0, 1)">,
</span>"slug": "aquxa-api-documentation"<span style="color: rgba(0, 0, 0, 1)">,
</span>"hideClientButton": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"servers"<span style="color: rgba(0, 0, 0, 1)">: [
{
</span>"url": "http://localhost:5215"<span style="color: rgba(0, 0, 0, 1)">,
</span>"description": "Development server"<span style="color: rgba(0, 0, 0, 1)">
}
],
</span>"showSidebar": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"showToolbar": "localhost"<span style="color: rgba(0, 0, 0, 1)">,<span style="color: rgba(255, 0, 0, 1)">//这里特别说明一下,编辑完后,不想出现这个菜单栏,就在这里可以关闭showToolbar: "never"
</span></span>"operationTitleSource": "summary"<span style="color: rgba(0, 0, 0, 1)">,
</span>"theme": "solarized"<span style="color: rgba(0, 0, 0, 1)">,<span style="color: rgba(255, 0, 0, 1)">//主题可以自己选,喜欢哪个选哪个
</span></span>"_integration": "dotnet"<span style="color: rgba(0, 0, 0, 1)">,
</span>"persistAuth": <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"telemetry": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"layout": "modern"<span style="color: rgba(0, 0, 0, 1)">,
</span>"isEditable": <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"isLoading": <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"hideModels": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"documentDownloadType": "both"<span style="color: rgba(0, 0, 0, 1)">,
</span>"hideTestRequestButton": <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"hideSearch": <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"showOperationId": <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"hideDarkModeToggle": <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"favicon": "favicon.svg"<span style="color: rgba(0, 0, 0, 1)">,
</span>"withDefaultFonts": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"defaultOpenAllTags": <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"expandAllModelSections": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"expandAllResponses": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"orderSchemaPropertiesBy": "alpha"<span style="color: rgba(0, 0, 0, 1)">,
</span>"orderRequiredPropertiesFirst": <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span>"url": "http://localhost:5215/openapi/v1.json"<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<pre><span>PS:这里特别说明一下,编辑完后,不想出现这个菜单栏,就在这里可以关闭showToolbar: "never"</span><br>得到这个文件,保存到wwwroot/js/scalar-config.js,注意,一定要保存到能访问的静态目录里,并在program.cs添加静态目录的配置<br><code class="t-editor__inline-code"></code></pre>
<div class="cnblogs_code">
<pre>app.UseStaticFiles(). <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这个要放在scalar配置的前面,不然访问不到</span></pre>
</div>
<p>添加配置文件加载</p>
<div class="cnblogs_code">
<pre>.WithJavaScriptConfiguration(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/js/scalar-config.js</span><span style="color: rgba(128, 0, 0, 1)">"</span>) </pre>
</div>
<p> </p>
<p><img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017164201407-1694509419.png" alt="04" loading="lazy"></p>
<p>这里费了好大的劲,查官方,看代码,因为官方文档还是老文档,只是简单的概括了一下。最后整出来了</p>
<p><span style="font-size: 16px"><strong>四、文档的编辑</strong></span></p>
<p>使用最重要的还是API文档编辑,其实它完全用的标准的OpenApi,只要参考这个表就可以完全配置了</p>
<p><img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017165047566-1432136520.png" alt="05" loading="lazy"></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)]
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 为整个控制器添加标签</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> AdminController : ControllerBase
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> IActionResult ReloadCache()
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟重新加载缓存的操作</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> Ok(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Cache reloaded successfully</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> IActionResult GetStats()
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> Ok(<span style="color: rgba(0, 0, 255, 1)">new</span> { Users = <span style="color: rgba(128, 0, 128, 1)">100</span>, Requests = <span style="color: rgba(128, 0, 128, 1)">1000</span><span style="color: rgba(0, 0, 0, 1)"> });
}
}</span></pre>
</div>
<p>下面说一下常用的特性</p>
<p>1.API分组</p>
<div>
<div class="cnblogs_code">
<pre> </pre>
</div>
<p>这个比较熟悉,它可以分组,分版本,当你分好版本后/,会在scalar中左上角可以选择,当然,你也可以把它做为组来用</p>
<p><img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017165845787-1124928420.png" alt="06" loading="lazy"></p>
<p>如果有不想显示的API也可以用来排除显示</p>
<div class="cnblogs_code">
<pre>
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> IActionResult PrivateEndpoint() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> Ok(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">This is a private endpoint</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>2.API分类</p>
<div class="cnblogs_code">
<pre></pre>
</div>
<p>分类的API,会归档在一起,方便查询,这样看起来没有那么乱了</p>
<div class="cnblogs_code">
<pre>)]
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> IResult Attributes()
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> Results.Ok(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Hello world!</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017170534464-553519957.png" alt="07" loading="lazy"></p>
<p>3.描述</p>
<div class="cnblogs_code">
<pre>
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> IResult Attributes()
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> Results.Ok(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Hello world!</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017170839444-1940993122.png" alt="08" loading="lazy"></p>
<p>4.过滤</p>
<p>不想显示的接口可以用</p>
<p>上面说的</p>
<pre><span>来关闭<br>还有一个就是根目录,如果在配置文件中有MapGet,</span></span>可以使用.ExcludeFromDescription();排除显示</pre>
<pre><span><span> </span></span></pre>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"> // 默认打开首页
app.MapGet("/", () => "Hangfire 服务运行中。访问 /hangfire 查看仪表盘,访问 /docs 查看API文档").ExcludeFromDescription();
//可以使用.ExcludeFromDescription();排除显示
</pre>
</div>
<p> </p>
<pre><span><span><br><br></span></span></pre>
<p>更多编辑文档就看这里吧</p>
<p>https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/openapi/include-metadata?view=aspnetcore-9.0&tabs=controllers</p>
<p><strong><span style="font-size: 16px">五、认证授权</span></strong></p>
<p>这里就使用自己的授权就可以,这里就偷懒找AI完成了。参考部分都有备注</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Scalar.AspNetCore;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Microsoft.AspNetCore.Authentication;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Microsoft.Extensions.Options;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Security.Claims;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text.Encodings.Web;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> Microsoft.AspNetCore.Mvc;
</span><span style="color: rgba(0, 0, 255, 1)">using</span> MyWebApi; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加对WeatherForecast的引用</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> builder =<span style="color: rgba(0, 0, 0, 1)"> WebApplication.CreateBuilder(args);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Add services to the container.
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Learn more about configuring OpenAPI at </span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://aka.ms/aspnet/openapi</span>
builder.Services.AddOpenApi(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">v1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
builder.Services.AddOpenApi(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">v2</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加控制器服务</span>
<span style="color: rgba(0, 0, 0, 1)">builder.Services.AddControllers();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加身份验证服务</span>
builder.Services.AddAuthentication(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BasicAuthentication</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
.AddScheme</span><AuthenticationSchemeOptions, BasicAuthenticationHandler>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BasicAuthentication</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加授权服务</span>
builder.Services.AddAuthorization(options =><span style="color: rgba(0, 0, 0, 1)">
{
options.AddPolicy(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ScalarAccess</span><span style="color: rgba(128, 0, 0, 1)">"</span>, policy =><span style="color: rgba(0, 0, 0, 1)"> policy.RequireAuthenticatedUser());
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 配置服务器URL,避免端口冲突</span>
builder.WebHost.UseUrls(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">http://localhost:5215</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> app =<span style="color: rgba(0, 0, 0, 1)"> builder.Build();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Configure static file middleware to serve the JavaScript config file</span>
<span style="color: rgba(0, 0, 0, 1)">app.UseStaticFiles();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加身份验证和授权中间件</span>
<span style="color: rgba(0, 0, 0, 1)">app.UseAuthentication();
app.UseAuthorization();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Configure the HTTP request pipeline.</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (app.Environment.IsDevelopment())
{
app.MapOpenApi();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Add Scalar for API management with JavaScript configuration and authorization</span>
app.MapScalarApiReference(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/scalar</span><span style="color: rgba(128, 0, 0, 1)">"</span>, options =><span style="color: rgba(0, 0, 0, 1)">
{
options.WithTitle(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyWebApi</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
.WithJavaScriptConfiguration(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/js/scalar-config.js</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
.AddDocument(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">v1</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Aquxa API Documentation</span><span style="color: rgba(128, 0, 0, 1)">"</span>,isDefault: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
.AddDocument(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">v2</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Beta API</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
})
.RequireAuthorization(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ScalarAccess</span><span style="color: rgba(128, 0, 0, 1)">"</span>); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 应用授权策略</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加控制器路由</span>
<span style="color: rgba(0, 0, 0, 1)">app.MapControllers();
app.Run();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Basic Authentication Handler</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> BasicAuthenticationHandler(
IOptionsMonitor</span><AuthenticationSchemeOptions><span style="color: rgba(0, 0, 0, 1)"> options,
ILoggerFactory logger,
UrlEncoder encoder)
: </span><span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">(options, logger, encoder)
{
}
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<AuthenticateResult><span style="color: rgba(0, 0, 0, 1)"> HandleAuthenticateAsync()
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检查是否有Authorization头</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (!Request.Headers.ContainsKey(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Authorization</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> AuthenticateResult.NoResult();
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解析Basic认证头</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> authHeader = Request.Headers[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Authorization</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">].ToString();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!authHeader.StartsWith(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Basic </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> AuthenticateResult.NoResult();
</span><span style="color: rgba(0, 0, 255, 1)">var</span> encodedCredentials = authHeader.Substring(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Basic </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">.Length).Trim();
</span><span style="color: rgba(0, 0, 255, 1)">var</span> decodedCredentials =<span style="color: rgba(0, 0, 0, 1)"> System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encodedCredentials));
</span><span style="color: rgba(0, 0, 255, 1)">var</span> credentials = decodedCredentials.Split(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">:</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> username = credentials[<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 0, 255, 1)">var</span> password = credentials[<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 验证用户名和密码(这里使用硬编码,实际应用中应从配置或数据库获取)</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (username == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">admin</span><span style="color: rgba(128, 0, 0, 1)">"</span> && password == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">password123</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> claims = <span style="color: rgba(0, 0, 255, 1)">new</span>[] { <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Claim(ClaimTypes.Name, username) };
</span><span style="color: rgba(0, 0, 255, 1)">var</span> identity = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ClaimsIdentity(claims, Scheme.Name);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> principal = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ClaimsPrincipal(identity);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> ticket = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AuthenticationTicket(principal, Scheme.Name);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> AuthenticateResult.Success(ticket);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> AuthenticateResult.Fail(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Invalid username or password</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> AuthenticateResult.Fail(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Invalid Authorization Header</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> Task HandleChallengeAsync(AuthenticationProperties properties)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送WWW-Authenticate头以触发浏览器的认证对话框</span>
Response.Headers[<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">WWW-Authenticate</span><span style="color: rgba(128, 0, 0, 1)">"</span>] = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Basic realm=\"Scalar API Documentation\"</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">await</span> <span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">.HandleChallengeAsync(properties);
}
}</span></pre>
</div>
<p> <img src="https://img2024.cnblogs.com/blog/43211/202510/43211-20251017172606610-102964762.png" alt="09" loading="lazy"></p>
<p> </p>
</div>
<pre></pre><br><br>
来源:https://www.cnblogs.com/wangbin5542/p/19148516
頁:
[1]