ASP.NET Core | 笔记

引言 参考: .NET Platform - .NET 所有开源代码 依赖注入 注入对相同接口的实现: 不一定会替换,如果通过 TryAddxxxx 注册,那么后面注册的无效(会判断是否已经存在实现,若有则不再添加)。
如果使用Addxxx注册,相同的接口将放在一个字典中,然后解析服务的时候解析最后一个。 但是之前注册的依然还在,可以通过遍历Services可以获取所有注册的接口。 依赖注入

AOP Q: A: ASP.NET Core 中的静态文件 参考: ASP.NET Core 中的静态文件 | Microsoft Docs 断点续传 参考: 5653325/.NET-WPF-MinIO: WPF下使用MinIO的.NET SDK进行文件上传,并展示上传进度。 wangpengxpy/WebAPiResumeDownload: webapi断点续传几种方式及webclient断点续传下载 wangpengxpy/ASP.NETCoreResumeDownload: asp.net core断点续传 vivo 应用商店中的断点续传技术剖析 - SegmentFault 思否 服务端基于Http的Range头规则实现断点续传或分段下载(C#) - SegmentFault 思否 C#断点续传 - 365lei - 博客园 C#实现文件断点续传下载的方法_C#教程_脚本之家 c# 断点续传的实现_C#教程_脚本之家 C#断点续传 - 365lei - 博客园 .net c# 文件分片/断点续传之下载--客户端_mengtoumingren的博客-CSDN博客 WebSocket 参考: ASP.NET Core 中的 WebSocket 支持 | Microsoft Docs WebSocket 教程 - 阮一峰的网络日志 服务器开发- Asp.Net Core中的websocket,并封装一个简单的中间件 - 青城同学 - 博客园 发布 参考: dotnet publish command - .NET CLI | Microsoft Docs
1
2
3
4
5
6
7
8
9
10
11
dotnet publish [<PROJECT>|<SOLUTION>] [-a|--arch <ARCHITECTURE>]
[-c|--configuration <CONFIGURATION>]
[-f|--framework <FRAMEWORK>] [--force] [--interactive]
[--manifest <PATH_TO_MANIFEST_FILE>] [--no-build] [--no-dependencies]
[--no-restore] [--nologo] [-o|--output <OUTPUT_DIRECTORY>]
[--os <OS>] [-r|--runtime <RUNTIME_IDENTIFIER>]
[--self-contained [true|false]]
[--no-self-contained] [-v|--verbosity <LEVEL>]
[--version-suffix <VERSION_SUFFIX>]

dotnet publish -h|--help
在 ASP.NET Core 中 启用跨域 参考: 在 ASP.NET CORE 中 (CORS) 跨 ASP.NET Core | Microsoft Docs

同一源

如果两个 URL 具有相同的方案、主机和端口,则它们具有相同的源 (RFC 6454) 。 这两个 URL 具有相同的来源: https://example.com/foo.html https://example.com/bar.html 这些 URL 的源与前两个 URL 不同: https://example.net:不同的域 https://www.example.com/foo.html:不同的子域 http://example.com/foo.html:不同的方案 https://example.com:9000/foo.html:不同的端口

启用 CORS

有三种方法可以启用 CORS: 在使用命名策略或默认策略的中间件中。 使用 终结点路由。 使用 [EnableCors] 属性。 将 [EnableCors] 属性与命名策略一起使用在限制支持 CORS 的终结点方面提供了最佳控制。 警告 UseCors 必须按正确的顺序调用 。 有关详细信息,请参阅 中间件顺序。 例如, UseCors 在使用 之前必须 UseResponseCaching 调用 UseResponseCaching 。 UseCors 添加 CORS 中间件。 正确的 UseCors 调用必须位于 之后 UseRouting ,但在 之前 UseAuthorization 。 有关详细信息,请参阅 中间件顺序
1
2
app.UseRouting();
app.UseCors();
使用中间件Caching时,UseCors 之前调用 UseResponseCaching

使用默认策略和中间件的 CORS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
// 注意: AddDefaultPolicy
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins("http://*.example.com",
"http://www.contoso.com");
});
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

// 注意: UseCors(), 没有指定 CorsPolicyName
app.UseCors();

app.UseAuthorization();

app.MapControllers();

app.Run();
补充: 使用默认策略, 允许任意跨域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public void ConfigureServices(IServiceCollection services)
{
// 任意跨域
services.AddCors(options =>
{
options.AddDefaultPolicy(policyBuilder =>
{
policyBuilder.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin();
});
});
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
// 注意
app.UseCors();

app.UseDefaultFiles();
app.UseStaticFiles();

app.UseAuthorization();
}

预检请求 preflight-requests

参考: 预检请求 对于某些 CORS 请求,浏览器会在发出实际请求之前发送额外的 OPTIONS 请求。 此请求称为 预检请求。 如果满足以下 所有 条件,浏览器可以跳过预检请求: 请求方法为 GET、HEAD 或 POST。 应用不会设置、、、或以外的请求标头 Accept Accept-Language Content-Language Content-Type Last-Event-ID 。 Content-Type 标头(如果已设置)具有以下值之一: application/x-www-form-urlencoded multipart/form-data text/plain

设置预检过期时间

Access-Control-Max-Age标头指定可以缓存对预检请求的响应的多久。 若要设置此标头,请调用 SetPreflightMaxAge
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
options.AddPolicy("MySetPreflightExpirationPolicy",
builder =>
{
builder.WithOrigins("http://example.com")
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});
});

builder.Services.AddControllers();

var app = builder.Build();

使用终结点路由和 [HttpOptions] 测试 CORS

使用 基于每个终结点启用 CORS RequireCors 目前 不支持自动预检请求。 请考虑以下代码,该代码使用终结点路由来启用 CORS:
1
2
3
4
5
6
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
这时就无法自动响应预检请求,于是需要显式 响应 OPTIONS Q&A 补充

SSH

参考: sshnet/SSH.NET: SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism. c# 使用ssh连接远程主机(ssh.net演示) - axel10 - 博客园 使用ssh客户端连接远程主机执行命令,并拿到输出结果:
1
2
3
4
5
6
7
8
9
using (var sshClient = new SshClient("host", port,"username", "password"))
{
sshClient.Connect();
using (var cmd = sshClient.CreateCommand("ls -l"))
{
var res = cmd.Execute();
Console.Write(res);
}
}
使用sftp客户端上传文件:
1
2
3
4
5
using (var sftpClient = new SftpClient("host", port,"username", "password"))
{
sftpClient.Connect();
sftpClient.UploadFile(File.Open(@"D:\index.html", FileMode.Open),"/root/index.html");
}
下载文件:
1
2
3
4
5
6
7
8
using (var sftpClient = new SftpClient("host", port,"username", "password"))
{
sftpClient.Connect();
using (var stream = File.Open(@"F:\index.html", FileMode.OpenOrCreate))
{
sftpClient.DownloadFile("/root/index.html", stream);
}
}

System.NullReferenceException: WebApi.Startup.ConfigureServices(IServiceCollection services)

参考: asp.net core - System.NullReferenceException at Startup.ConfigureServices(IServiceCollection services) in windows server - Stack Overflow
1
2
3
4
5
6
7
8
9
10
11
12
13
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at WebApi.Startup.ConfigureServices(IServiceCollection services) in F:\Com\me\Repos\OneTree\src\Framework\Presentation\WebApi\Startup.cs:line 58
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.<Invoke>g__Startup|0(IServiceCollection serviceCollection)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass8_0.<Build>b__0(IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass12_0.<UseStartup>b__0(HostBuilderContext context, IServiceCollection services)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at WebApi.Program.Main(String[] args) in F:\Com\me\Repos\OneTree\src\Framework\Presentation\WebApi\Program.cs:line 31
发现,当前不在 WebApi.dll 所处目录,通过 绝对路径 方式运行,会由于找不到 appsettings.json,而导致 _configuration 为 null 解决: 前往 WebApi.dll 所处目录,运行 dotnet WebApi.dll即可

MySql.Data.EntityFrameworkCore 8.0.22 仅与 Microsoft.EntityFrameworkCore 3.1 兼容

参考: c# - Issue with scaffolding a MySql database with EF Core - Method not found: Void Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping - Stack Overflow MySql.Data.EntityFrameworkCore 8.0.22 仅与 Microsoft.EntityFrameworkCore 3.1 兼容。将所有 Microsoft.EntityFramework 包从 5.0.0 降级到 3.1.10 以修复错误。 随着 MySQL Connector/NET 8.0.23 于 2021 年 1 月 18 日的发布,Oracle 现在发布了一个与 Microsoft.EntityFramework 5.0 兼容的不同 NuGet 包:MySql.EntityFrameworkCore。 请参阅此处的版本兼容性表。 或者,您可以尝试切换到Pomelo.EntityFrameworkCore.MySql 5.0.0-alpha.2(或更高版本);请参阅其兼容包版本表

插件系统

参考: c# - ASP .NET Core MVC 2.1 mvc Views in plugin - Stack Overflow xfrogcn/Xfrogcn.PluginFactory: .net core插件框架 如何在 .NET Core 中使用和调试程序集可卸载性 | Microsoft Docs 使用插件创建 .NET Core 应用程序 - .NET | Microsoft Docs 如何在 .NET Core 中使用和调试程序集可卸载性 | Microsoft Docs dotnetcore/Natasha: 基于 Roslyn 的 C# 动态程序集构建库,该库允许开发者在运行时使用 C# 代码构建域 / 程序集 / 类 / 结构体 / 枚举 / 接口 / 方法等,使得程序在运行的时候可以增加新的模块及功能。Natasha 集成了域管理/插件管理,可以实现域隔离,域卸载,热拔插等功能。 该库遵循完整的编译流程,提供完整的错误提示, 可自动添加引用,完善的数据结构构建模板让开发者只专注于程序集脚本的编写,兼容 stanadard2.0 / netcoreapp3.0+, 跨平台,统一、简便的链式 API。 且我们会尽快修复您的问题及回复您的 issue. weikio/PluginFramework: Everything is a Plugin in .NET 加载 Controller 与 View
1
2
3
var assembly = ...;
services.AddMvc()
.AddApplicationPart(assembly)
View
1
2
3
4
5
services.AddMvc().ConfigureApplicationPartManager(apm =>
{
foreach (var b in new CompiledRazorAssemblyApplicationPartFactory().GetApplicationParts(AssemblyLoadContext.Default.LoadFromAssemblyPath(".../ViewAssembypath/file.Views.dll")))
apm.ApplicationParts.Add(b);
});

合并dll

参考: .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖 - walterlv

WPF 集成 ASP.NET Core

参考: 使用asp.net core webapi 与 vue 搭建桌面客户端的新尝试 - 知乎

WebView

参考: 【译】来看看 WebWindow,一个跨平台的 .NET Core webview 库 - 知乎 kklldog/AServer: AServer是基于asp.net core Kestrel封装的一个超迷你http服务器

进程管理

参考: C#在窗体程序中运行控制台程序并管理其进程_madonghyu的博客-CSDN博客_c#调用进程之后管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public void FrpStart()
{
if (p != null)
{
MessageBox.Show("进程已存在");
return;
}
p = new Process
{
// Configure the process using the StartInfo properties.
StartInfo =
{
//调用的程序名称,比如windows下的cmd,linux下的sh或者bash,即这里要填写控制台程序的路径
FileName = Utils.GetTempPath() + "/frpc.exe",
//参数,MainConfig为配置文件路径
Arguments = "-c " + MainConfig,
//控制台程序所在的路径
WorkingDirectory = Utils.GetTempPath(),
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
CreateNoWindow = true,
//重定向输入输出
RedirectStandardInput = true,
RedirectStandardOutput = true
}
};
//监听控制台的输出
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
// Prepend line numbers to each line of the output.
if (!string.IsNullOrEmpty(e.Data))
{
append(e.Data);
}
});
p.Start();
p.BeginOutputReadLine();
}
注意上面新建进程的参数UseShellExecute = false,如果这里设置为false,那么FileName这个参数中控制台程序的只能用绝对路径,即WorkingDirectory参数无效。
如果不设置UseShellExecute为false,则无法重定向输出。
如果UseShellExecute = true,则FileName可以直接使用控制台程序的名字,前提是WorkingDirectory里面的路径是正确的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public void FrpStart()
{
//检测是否存在残留的线程,并将其关闭
Process[] existingPrivoxy = Process.GetProcessesByName("frpc");
foreach (Process p in existingPrivoxy)
{
KillProcess(p);
}
..........
p.Start();
p.BeginOutputReadLine();
//将其加入Job
//Job的初始化省略了,可以在构造函数初始化,使用单例模式
Job.AddProcess(p.Handle);
}

private static void KillProcess(Process p)
{
try
{
p.CloseMainWindow();
p.WaitForExit(100);
if (!p.HasExited)
{
p.Kill();
p.WaitForExit();
}
}
catch (Exception e)
{
}
}

HttpClient 上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public int UpSound_Request(string address, string fileNamePath, string saveName, ProgressBar progressBar)
{
int returnValue = 0;
//要上传的文件
FileStream fs = new FileStream(fileNamePath, FileMode.Open, FileAccess.Read);
//二进制对象
BinaryReader r = new BinaryReader(fs);
//时间戳
string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");
byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + strBoundary + "\r\n");
//请求的头部信息
StringBuilder sb = new StringBuilder();
sb.Append("--");
sb.Append(strBoundary);
sb.Append("\r\n");
sb.Append("Content-Disposition: form-data; name=\"");
sb.Append("file");
sb.Append("\"; filename=\"");
sb.Append(saveName);
sb.Append("\";");
sb.Append("\r\n");
sb.Append("Content-Type: ");
sb.Append("application/octet-stream");
sb.Append("\r\n");
sb.Append("\r\n");
string strPostHeader = sb.ToString();
byte[] postHeaderBytes = Encoding.UTF8.GetBytes(strPostHeader);
// 根据uri创建HttpWebRequest对象
HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(address));
httpReq.Method = "POST";
//对发送的数据不使用缓存
httpReq.AllowWriteStreamBuffering = false;
//设置获得响应的超时时间(300秒)
httpReq.Timeout = 300000;
httpReq.ContentType = "multipart/form-data; boundary=" + strBoundary;
long length = fs.Length + postHeaderBytes.Length + boundaryBytes.Length;
long fileLength = fs.Length;
httpReq.ContentLength = length;
try
{
progressBar.Maximum = int.MaxValue;
progressBar.Minimum = 0;
progressBar.Value = 0;
//每次上传400k
int bufferLength = 409600;
byte[] buffer = new byte[bufferLength]; //已上传的字节数
long offset = 0; //开始上传时间
DateTime startTime = DateTime.Now;
int size = r.Read(buffer, 0, bufferLength);
Stream postStream = httpReq.GetRequestStream(); //发送请求头部消息
postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);
while (size > 0)
{
postStream.Write(buffer, 0, size);
offset += size;
progressBar.Value = (int)(offset * (int.MaxValue / length));
TimeSpan span = DateTime.Now - startTime;
double second = span.TotalSeconds;
labTime.Text = "已用时:" + second.ToString("F2") + "秒";
if (second > 0.001)
{
labSpeed.Text = "平均速度:" + (offset / 1024 / second).ToString("0.00") + "KB/秒";
}
else
{
labSpeed.Text = " 正在连接…";
}
labState.Text = "已上传:" + (offset * 100.0 / length).ToString("F2") + "%";
labSize.Text = (offset / 1048576.0).ToString("F2") + "M/" + (fileLength / 1048576.0).ToString("F2") + "M";
Application.DoEvents();
size = r.Read(buffer, 0, bufferLength);
}
//添加尾部的时间戳
postStream.Write(boundaryBytes, 0, boundaryBytes.Length);
postStream.Close();
//获取服务器端的响应
WebResponse webRespon = httpReq.GetResponse();
Stream s = webRespon.GetResponseStream();
//读取服务器端返回的消息
StreamReader sr = new StreamReader(s);
String sReturnString = sr.ReadLine();
s.Close();
sr.Close();
if (sReturnString == "Success")
{
returnValue = 1;
}
else if (sReturnString == "Error")
{
returnValue = 0;
}
}
catch
{
returnValue = 0;
}
finally
{
fs.Close();
r.Close();
}
return returnValue;
}

通过流式传输上传大型文件

参考: 在 ASP.NET Core 中上传文件 | Microsoft Docs

解析 nuget 的 nupkg

参考: sdk/NuGetExeRestoreCommand.cs · dotnet/sdk

自定义模板

参考: dotnet new 自定义模板 - .NET CLI | Microsoft Docs 自定义.NET Core项目模板 - 知乎 从壹开始前后端分离 39 || 想创建自己的dotnet模板么?看这里 - 老张的哲学 - 博客园 使用 .net core 自定义项目模板_沐雪大神-CSDN博客

JavaScript 拦截请求

参考: 使用 JavaScript 拦截和跟踪浏览器中的 HTTP 请求 - Guide2IT - 博客园

创建 nuget 源代码 、符号包

参考: 从零开始制作 NuGet 源代码包及个人总结(全面支持 .NET Core / .NET Framework / WPF 项目) - jack_Meng - 博客园 如何使用新的符号包格式“.snupkg”发布 NuGet 符号包 | Microsoft Docs 让你发布的nuget包支持源代码调试 - czd890 - 博客园 良好的调试体验依赖于调试符号的存在,因为它们提供了一些关键信息,例如已编译的代码与源代码之间的关联、局部变量的名称、堆栈跟踪等。 你可以使用符号包 (.snupkg) 来分发这些符号,并改善 NuGet 包的调试体验。 请注意,符号包并不是使调试符号可用于库使用者的唯一策略。 还可以通过以下项目属性在 dll 或 exe 中 embed 它们:<DebugType>embedded</DebugType>

创建符号包

如果使用 dotnet CLI 或 MSBuild,则除 .nupkg 文件外,还需要设置 IncludeSymbols 和 SymbolPackageFormat 属性以创建 .snupkg 文件。 要么将以下属性添加到 .csproj 文件:
1
2
3
4
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
要么在命令行上指定这些属性:
.NET CLI
1
dotnet pack MyPackage.csproj -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
1
msbuild MyPackage.csproj /t:pack /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg
如果使用 NuGet.exe,除 .nupkg 文件外,可以使用以下命令创建一个 .snupkg 文件:
1
2
3
nuget pack MyPackage.nuspec -Symbols -SymbolPackageFormat snupkg

nuget pack MyPackage.csproj -Symbols -SymbolPackageFormat snupkg

发布符号包

    为方便起见,首先使用 NuGet 保存 API 密钥(请参阅发布包)。
    1
    nuget SetApiKey Your-API-Key
    将主包发布到 nuget.org 后,按如下方式推送符号包。
    1
    nuget push MyPackage.snupkg
    还可以 使用以下命令__同时推送主包和符号包__。 当前文件夹中必须同时有 .nupkg 和 .snupkg 文件。
    1
    nuget push MyPackage.nupkg
NuGet 会将两个包发布到 nuget.org。MyPackage.nupkg 先发布,随后 MyPackage.snupkg 发布。 备注 如果没有发布符号包,请检查是否已将 NuGet.org 源配置为 https://api.nuget.org/v3/index.json。 只有 NuGet V3 API 才支持符号包发布。

WebTerm

参考: webssh-xterm.js的简单使用 - 简书

拦截方法

参考: C# 方法拦截器_lishuangquan1987的博客-CSDN博客_c# 拦截器

下载文件

参考: 使用 C# 下载文件的十八般武艺 - Soar、毅 - 博客园 bezzad/Downloader: Fast and reliable multipart downloader with asynchronous progress events for .NET applications. WELL-E/AutoUpdater: WPF AutoUpdater

Web 在线代理

参考: jabbany/knProxy: Lightweight, PHP-based Web Proxy that can utilize whatever remote connecting ablities your server has to offer. It should work out of the box. No configuration needed. For educational purposes. NicheOffice/php-web-proxy: Online Web Proxy Website Script Written in PHP

WebAPI 在线文档

Swashbuckle 和 ASP.NET Core 入门 | Microsoft Docs

Swashbuckle

参考: asp.net core使用Swashbuckle.AspNetCore(swagger)生成接口文档_weixin_33907511的博客-CSDN博客
1
Install-Package Swashbuckle.AspNetCore -Version 6.2.3
Startup.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Startup
{

public void ConfigureServices(IServiceCollection services)
{
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
services.AddEndpointsApiExplorer();
services.AddSwaggerGen(options =>
{
// https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-6.0&tabs=visual-studio
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Version = "v1",
Title = "爱发电 Badge",
Description = "爱发电 Badge - 由 Afdian.Server 构建",
TermsOfService = new Uri("https://github.com/yiyungent/Afdian.Sdk"),
Contact = new Microsoft.OpenApi.Models.OpenApiContact
{
Name = "Contact",
Url = new Uri("https://github.com/yiyungent/Afdian.Sdk/issues")
},
License = new Microsoft.OpenApi.Models.OpenApiLicense
{
Name = "MIT License",
Url = new Uri("https://github.com/yiyungent/Afdian.Sdk/blob/main/LICENSE")
},
//Extensions = new Microsoft.OpenApi.Models.OpenApiExtensibleDictionary<string, string>() { }
});

var xmlFilename = {{hbeSeoContent}}quot;{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI();

// Others
app.UseRouting();
app.UseCors();

app.UseDefaultFiles();
app.UseStaticFiles();

app.UseAuthorization();
// ...
}

}

Swashbuckle.AspNetCore请求上的 Authorization

参考: Swashbuckle.AspNetCore请求上的空授权标头 - IT屋-程序员软件开发技术分享社区
1
string bearerToken = Request.Headers["Authorization"];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "Employee Navigator",
Description = "Authorization Key: Z29vZEtleQ==",
});
c.AddSecurityDefinition("Bearer", new ApiKeyScheme
{
Name = "Authorization",
In = "header",
Type = "apiKey",
Description = "Authorization Key: Z29vZEtleQ=="
});
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new[] { "readAccess", "writeAccess" }
});

});

多 API Version

参考: asp.net core使用Swashbuckle.AspNetCore(swagger)生成接口文档_weixin_33907511的博客-CSDN博客

补充

如何忽略一个接口

为 Controller 或者 Action 方法上添加特性标记 [ApiExplorerSettings(IgnoreApi =true)] 即可

.NET6 手动配置启动项地址

1
2
3
4
5
6
7
8
9
10
11
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.WebHost.ConfigureKestrel((context, options) =>
{
options.Listen(IPAddress.Any, 5001, listenOptions =>
{
listenOptions.UseHttps();
});
});
var app = builder.Build();
## Kestrel 请求体最大为 50MB
1
2
3
4
5
6
7
8
9
// 设置应用服务器Kestrel请求体最大为50MB 52428800
builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = null);

// 设置即重置文件上传的大小限制
builder.Services.Configure<FormOptions>(o =>
{
o.MultipartBodyLengthLimit = long.MaxValue;
});

前端发送 自定义 header 名规范

当然更规范应该用 X- 开头, 并使用 X-Aaa-Bbb 命名 注意: 实测, ASP.NET Core 在发布到 Docker 后,
以 Release "ASPNETCORE_ENVIRONMENT": "Production" 不能接收 到 hexo_cloud_extensions_hello,
但能接收到 hexo-cloud-extensions-hello,
不要用 _ 作为名, 而使用 -,
但在本地 测试却可以接收(无论使用 Release "ASPNETCORE_ENVIRONMENT": "Production" 还是 Debug Development 等组合),
> 同一套代码,目前不知道原因
1
2
3
4
foreach (var item in _httpContextAccessor.HttpContext.Request.Headers)
{
Console.WriteLine({{hbeSeoContent}}quot;Key: {item.Key} Value: {item.Value}");
}
TODO: 真是巨坑 参考 感谢帮助!