参考:
注入对相同接口的实现: 不一定会替换,如果通过 TryAddxxxx 注册,那么后面注册的无效(会判断是否已经存在实现,若有则不再添加)。 如果使用Addxxx注册,相同的接口将放在一个字典中,然后解析服务的时候解析最后一个。 但是之前注册的依然还在,可以通过遍历Services可以获取所有注册的接口。
依赖注入
Q:
A:
参考:
参考:
参考:
参考:
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
参考:
同一源 如果两个 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] 属性与命名策略一起使用在限制支持 CORS 的终结点方面提供了最佳控制。
警告
UseCors 必须按正确的顺序调用 。 有关详细信息,请参阅 中间件顺序。 例如, UseCors
在使用 之前必须 UseResponseCaching 调用 UseResponseCaching
。
UseCors
添加 CORS 中间件。
UseCors
调用必须位于 之后 UseRouting
,但在 之前 UseAuthorization
。 有关详细信息,请参阅 中间件顺序。app.UseRouting();
app.UseCors();
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();
补充:
使用默认策略, 允许任意跨域
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();
}
参考:
对于某些 CORS 请求,浏览器会在发出实际请求之前发送额外的 OPTIONS 请求。 此请求称为 预检请求。 如果满足以下 所有 条件,浏览器可以跳过预检请求:
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 :
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();
使用 基于每个终结点启用 CORS RequireCors
目前 不支持自动预检请求。
请考虑以下代码,该代码使用终结点路由来启用 CORS:
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}
这时就无法自动响应预检请求,于是需要显式 响应 OPTIONS
参考:
使用ssh客户端连接远程主机执行命令,并拿到输出结果:
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客户端上传文件:
using (var sftpClient = new SftpClient("host", port,"username", "password"))
{
sftpClient.Connect();
sftpClient.UploadFile(File.Open(@"D:\index.html", FileMode.Open),"/root/index.html");
}
下载文件:
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);
}
}
参考:
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 兼容。将所有 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(或更高版本);请参阅其兼容包版本表。
参考:
加载 Controller
与 View
var assembly = ...;
services.AddMvc()
.AddApplicationPart(assembly)
View
services.AddMvc().ConfigureApplicationPartManager(apm =>
{
foreach (var b in new CompiledRazorAssemblyApplicationPartFactory().GetApplicationParts(AssemblyLoadContext.Default.LoadFromAssemblyPath(".../ViewAssembypath/file.Views.dll")))
apm.ApplicationParts.Add(b);
});
参考:
参考:
参考:
参考:
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里面的路径是正确的。
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)
{
}
}
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;
}
参考:
参考:
参考:
参考:
参考:
良好的调试体验依赖于调试符号的存在,因为它们提供了一些关键信息,例如已编译的代码与源代码之间的关联、局部变量的名称、堆栈跟踪等。 你可以使用符号包 (.snupkg) 来分发这些符号,并改善 NuGet 包的调试体验。 请注意,符号包并不是使调试符号可用于库使用者的唯一策略。 还可以通过以下项目属性在
dll
或exe
中embed
它们:<DebugType>embedded</DebugType>
如果使用 dotnet CLI 或 MSBuild,则除 .nupkg 文件外,还需要设置 IncludeSymbols
和 SymbolPackageFormat
属性以创建 .snupkg 文件。
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
dotnet pack MyPackage.csproj -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
或
msbuild MyPackage.csproj /t:pack /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg
如果使用 NuGet.exe,除 .nupkg 文件外,可以使用以下命令创建一个 .snupkg 文件:
nuget pack MyPackage.nuspec -Symbols -SymbolPackageFormat snupkg
nuget pack MyPackage.csproj -Symbols -SymbolPackageFormat snupkg
nuget SetApiKey Your-API-Key
nuget push MyPackage.snupkg
nuget push MyPackage.nupkg
NuGet 会将两个包发布到 nuget.org。MyPackage.nupkg
先发布,随后 MyPackage.snupkg
发布。
备注 如果没有发布符号包,请检查是否已将 NuGet.org 源配置为
https://api.nuget.org/v3/index.json
。 只有 NuGet V3 API 才支持符号包发布。
参考:
参考:
参考:
参考:
参考:
Install-Package Swashbuckle.AspNetCore -Version 6.2.3
Startup.cs
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 = $"{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();
// ...
}
}
参考:
string bearerToken = Request.Headers["Authorization"];
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" }
});
});
参考:
如何忽略一个接口 为 Controller 或者 Action 方法上添加特性标记
[ApiExplorerSettings(IgnoreApi =true)]
即可
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
// 设置应用服务器Kestrel请求体最大为50MB 52428800
builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = null);
// 设置即重置文件上传的大小限制
builder.Services.Configure<FormOptions>(o =>
{
o.MultipartBodyLengthLimit = long.MaxValue;
});
感谢帮助!