优化启动参数解析方式
This commit is contained in:
parent
5090f7e7a6
commit
9743cf1460
15 changed files with 320 additions and 65 deletions
26
src/Attributes/CommandLineArgumentAttribute.cs
Normal file
26
src/Attributes/CommandLineArgumentAttribute.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
namespace iFileProxy.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
||||
public class CommandLineArgumentAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 长参数名 如: --dev-logger
|
||||
/// </summary>
|
||||
public string FullArgumentName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 短参数名 如: -d
|
||||
/// </summary>
|
||||
public string ShortArgumentName { get; set; } = new string(' ',4);
|
||||
|
||||
/// <summary>
|
||||
/// 参数帮助文本
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 是否需要参数值
|
||||
/// </summary>
|
||||
public bool NeedValue { get; set; } = false;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
using iFileProxy.Config;
|
||||
using iFileProxy.Helpers;
|
||||
using iFileProxy.Services;
|
||||
namespace iFileProxy.Handlers
|
||||
{
|
||||
public class CmdArgsHandler
|
||||
{
|
||||
public static void ParseArgs(string[] args, IServiceProvider serviceProvider)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
return;
|
||||
Dictionary<string, string> helpStrings = [];
|
||||
helpStrings.Add("--init-db", "初始化数据库");
|
||||
helpStrings.Add("--dev-logger", "启用开发者logger");
|
||||
helpStrings.Add("--disable-startup-check", "禁用启动时程序配置自检");
|
||||
helpStrings.Add("--url=<Url>", "设置程序app.run() Url 如: http://0.0.0.0:1145");
|
||||
|
||||
if (args.Contains("-h") || args.Contains("--help"))
|
||||
{
|
||||
Console.WriteLine("命令行参数帮助: ");
|
||||
foreach (var hs in helpStrings)
|
||||
{
|
||||
Console.WriteLine($"\t{hs.Key}\t{hs.Value}");
|
||||
}
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
CommandLineArgsHelper cmdlineHelper = new(args);
|
||||
|
||||
if (cmdlineHelper.GetBooleanValue("--init-db"))
|
||||
{
|
||||
serviceProvider.GetRequiredService<DatabaseGateService>().TryInitialDB();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
src/Handlers/CommandLine/CustomBindUrl.cs
Normal file
15
src/Handlers/CommandLine/CustomBindUrl.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using iFileProxy.Attributes;
|
||||
using iFileProxy.Interfaces;
|
||||
|
||||
namespace iFileProxy.Handlers.CommandLine
|
||||
{
|
||||
[CommandLineArgument(FullArgumentName = "--bind-url", ShortArgumentName = "-B", Description = "自定义绑定URL", NeedValue = true)]
|
||||
public class CustomBindUrl(string[] args) : ICommandLineHandler
|
||||
{
|
||||
public void Process()
|
||||
{
|
||||
Console.WriteLine("当前正使用自定义URL绑定");
|
||||
// 具体的实现在Program.cs中 此处仅作提示
|
||||
}
|
||||
}
|
||||
}
|
15
src/Handlers/CommandLine/DisablePreStartupCheck.cs
Normal file
15
src/Handlers/CommandLine/DisablePreStartupCheck.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using iFileProxy.Attributes;
|
||||
using iFileProxy.Interfaces;
|
||||
|
||||
namespace iFileProxy.Handlers.CommandLine
|
||||
{
|
||||
[CommandLineArgument(FullArgumentName = "--disable-startup-check", ShortArgumentName = "-D", Description = "禁用配置文件启动前校验")]
|
||||
public class DisablePreStartupCheck(string[] args) : ICommandLineHandler
|
||||
{
|
||||
public void Process()
|
||||
{
|
||||
Console.WriteLine("已禁用启动前配置文件校验");
|
||||
// 具体的实现在Program.cs中 此处仅作提示
|
||||
}
|
||||
}
|
||||
}
|
15
src/Handlers/CommandLine/EnableDevLogger.cs
Normal file
15
src/Handlers/CommandLine/EnableDevLogger.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using iFileProxy.Attributes;
|
||||
using iFileProxy.Interfaces;
|
||||
|
||||
namespace iFileProxy.Handlers.CommandLine
|
||||
{
|
||||
[CommandLineArgument(FullArgumentName = "--dev-logging", ShortArgumentName = "-d", Description = "启用dev级别日志记录")]
|
||||
public class EnableDevLogger(string[] args) : ICommandLineHandler
|
||||
{
|
||||
public void Process()
|
||||
{
|
||||
Console.WriteLine("developer logging is enabled.");
|
||||
// 具体的实现在SerilogConfig.cs中 此处仅作提示
|
||||
}
|
||||
}
|
||||
}
|
39
src/Handlers/CommandLine/HelpHandler.cs
Normal file
39
src/Handlers/CommandLine/HelpHandler.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using iFileProxy.Attributes;
|
||||
using iFileProxy.Helpers;
|
||||
using iFileProxy.Interfaces;
|
||||
using iFileProxy.Services;
|
||||
using System.Reflection;
|
||||
|
||||
namespace iFileProxy.Handlers.CommandLine
|
||||
{
|
||||
[CommandLineArgument(FullArgumentName ="--help",ShortArgumentName = "-h", Description = "显示帮助文本")]
|
||||
public class HelpHandler : ICommandLineHandler
|
||||
{
|
||||
string[] _args = [];
|
||||
public HelpHandler(string[] args) {
|
||||
_args = args;
|
||||
}
|
||||
|
||||
public void Process()
|
||||
{
|
||||
Dictionary<string,string> helpText = CommandLineArgumentDispatcher.GetHelpTextDict();
|
||||
// 找到最长的键长度
|
||||
int maxKeyLength = helpText.Keys.Max(key => key.Length);
|
||||
|
||||
// 打印表头
|
||||
Console.WriteLine("参数(短参数, 长参数)\t解释");
|
||||
|
||||
// 打印每一行的帮助信息,确保对齐
|
||||
foreach (var ht in helpText)
|
||||
{
|
||||
Console.WriteLine($" {ht.Key}{new string(' ', maxKeyLength - ht.Key.Length)}\t{ht.Value}");
|
||||
}
|
||||
Console.WriteLine(new string('\n',2));
|
||||
|
||||
Console.WriteLine($"程序编译日期(UTC+8): {MasterHelper.GetBuildTime(Assembly.GetExecutingAssembly())}");
|
||||
|
||||
Console.WriteLine();
|
||||
Environment.Exit(0 );
|
||||
}
|
||||
}
|
||||
}
|
21
src/Handlers/CommandLine/InitDBHandler.cs
Normal file
21
src/Handlers/CommandLine/InitDBHandler.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using iFileProxy.Attributes;
|
||||
using iFileProxy.Interfaces;
|
||||
using iFileProxy.Services;
|
||||
|
||||
namespace iFileProxy.Handlers.CommandLine
|
||||
{
|
||||
[CommandLineArgument(FullArgumentName = "--init-db", ShortArgumentName = "-i", Description = "初始化数据库表结构")]
|
||||
public class InitDBHandler : ICommandLineHandler
|
||||
{
|
||||
string[] _cmdArgs = [];
|
||||
public InitDBHandler(string[] args) {
|
||||
_cmdArgs = args;
|
||||
}
|
||||
public void Process()
|
||||
{
|
||||
DatabaseGateService gateService = new(new AppConfigService());
|
||||
gateService.TryInitialDB();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
}
|
90
src/Handlers/CommandLineArgumentDispatcher.cs
Normal file
90
src/Handlers/CommandLineArgumentDispatcher.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
using iFileProxy.Attributes;
|
||||
using iFileProxy.Config;
|
||||
using iFileProxy.Helpers;
|
||||
using iFileProxy.Interfaces;
|
||||
using Serilog;
|
||||
using System.Reflection;
|
||||
|
||||
namespace iFileProxy.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// 命令行参数调度器
|
||||
/// </summary>
|
||||
public class CommandLineArgumentDispatcher
|
||||
{
|
||||
private readonly static Serilog.ILogger _logger = Log.Logger.ForContext<CommandLineArgumentDispatcher>();
|
||||
|
||||
// 获取当前执行的程序集
|
||||
static Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// 获取程序集中所有的类型
|
||||
static Type[] types = [];
|
||||
|
||||
public CommandLineArgumentDispatcher()
|
||||
{
|
||||
LoadAssemblyData();
|
||||
}
|
||||
|
||||
static void LoadAssemblyData()
|
||||
{
|
||||
assembly = Assembly.GetExecutingAssembly();
|
||||
types = assembly.GetTypes();
|
||||
}
|
||||
|
||||
|
||||
public static void Parse(string[] args)
|
||||
{
|
||||
LoadAssemblyData();
|
||||
// 遍历所有类型,查找带有 CommandLineArgumentAttribute 的类
|
||||
foreach (Type type in types)
|
||||
{
|
||||
// 检查类型是否具有 CommandLineArgumentAttribute
|
||||
var attribute = type.GetCustomAttribute<CommandLineArgumentAttribute>();
|
||||
if (attribute != null && (args.Contains(attribute.FullArgumentName) || args.Contains(attribute.ShortArgumentName)))
|
||||
{
|
||||
var commandLineHandler = (ICommandLineHandler?)Activator.CreateInstance(type, [args]);
|
||||
commandLineHandler?.Process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> GetHelpTextDict()
|
||||
{
|
||||
static string GetHelpText(CommandLineArgumentAttribute attribute)
|
||||
{
|
||||
if (attribute.NeedValue && attribute.FullArgumentName.Trim() == string.Empty) // 需要值且没有长参数
|
||||
{
|
||||
return $"{attribute.ShortArgumentName}=<value>";
|
||||
}
|
||||
else if (attribute.NeedValue && attribute.ShortArgumentName.Trim() == string.Empty)// 需要值且没有短参数
|
||||
{
|
||||
return $"{attribute.ShortArgumentName} {attribute.FullArgumentName}=<value>";
|
||||
}
|
||||
else if (attribute.NeedValue && attribute.FullArgumentName.Trim() != "" && attribute.ShortArgumentName.Trim() != "") // 都需要
|
||||
{
|
||||
return $"{attribute.ShortArgumentName}=<value>,{attribute.FullArgumentName}=<value>";
|
||||
}
|
||||
else if (attribute.FullArgumentName.Trim() == "" && attribute.ShortArgumentName.Trim() == "") // 都没有
|
||||
{
|
||||
throw new ArgumentException("该参数长短参数名均为空 请联系软件开发者反馈");
|
||||
}
|
||||
else if (!attribute.NeedValue && attribute.FullArgumentName.Trim() == string.Empty)
|
||||
return attribute.ShortArgumentName ;
|
||||
else if (!attribute.NeedValue && attribute.ShortArgumentName.Trim() == string.Empty)
|
||||
return $"{attribute.ShortArgumentName} {attribute.FullArgumentName}";
|
||||
else
|
||||
return $"{attribute.ShortArgumentName},{attribute.FullArgumentName}";
|
||||
}
|
||||
Dictionary<string, string> r = [];
|
||||
foreach (var handler in types)
|
||||
{
|
||||
var h = handler.GetCustomAttribute<CommandLineArgumentAttribute>();
|
||||
if (h != null)
|
||||
{
|
||||
r.Add(GetHelpText(h), h.Description);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,13 +23,13 @@ namespace iFileProxy.Helpers
|
|||
var parts = arg.Split(['='], 2);
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
parameters[parts[0].TrimStart('-')] = parts[1];
|
||||
parameters[parts[0]] = parts[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果参数是没有值的标志性参数(比如 --dev-logger)
|
||||
parameters[arg.TrimStart('-')] = "true";
|
||||
parameters[arg] = "true";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,12 +39,34 @@ namespace iFileProxy.Helpers
|
|||
// 获取指定参数的值,若无值则返回null
|
||||
public string? GetStringValue(string key)
|
||||
{
|
||||
return _parameters.ContainsKey(key) ? _parameters[key] : null;
|
||||
// 如果 key 包含多个等效键(用 | 分隔),则拆分为数组
|
||||
string[] keys = key.Contains("|")
|
||||
? key.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
: [key];
|
||||
|
||||
foreach (var k in keys)
|
||||
{
|
||||
if (_parameters.TryGetValue(k, out string? value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有任何键匹配,默认返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取指定参数的布尔值,若参数值是 "true" 则返回true,否则返回false
|
||||
public bool GetBooleanValue(string key)
|
||||
{
|
||||
string[] args = [];
|
||||
if (key.Contains("|"))
|
||||
args = key.Split('|',StringSplitOptions.RemoveEmptyEntries|StringSplitOptions.TrimEntries);
|
||||
foreach (var arg in args)
|
||||
{
|
||||
if (_parameters.ContainsKey(arg) && _parameters[arg].Equals("true", StringComparison.CurrentCultureIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return _parameters.ContainsKey(key) && _parameters[key].Equals("true", StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using iFileProxy.Config;
|
||||
using iFileProxy.Models;
|
||||
using Serilog;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -228,5 +230,25 @@ namespace iFileProxy.Helpers
|
|||
return exp1.IsMatch(url) || exp2.IsMatch(url) || exp3.IsMatch(url) || exp4.IsMatch(url) || exp5.IsMatch(url);
|
||||
}
|
||||
|
||||
// copy from https://summerlight.com/archives/184
|
||||
public static DateTime GetBuildTime(Assembly assembly)
|
||||
{
|
||||
const string buildVersionMetadataPrefix = "+build";
|
||||
|
||||
var attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
if (attribute?.InformationalVersion != null)
|
||||
{
|
||||
var value = attribute.InformationalVersion;
|
||||
var index = value.IndexOf(buildVersionMetadataPrefix, StringComparison.Ordinal);
|
||||
if (index > 0)
|
||||
{
|
||||
value = value[(index + buildVersionMetadataPrefix.Length)..];
|
||||
if (DateTime.TryParseExact(value, "yyyyMMddHHmmssZ", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
9
src/Interfaces/ICommandLineHandler.cs
Normal file
9
src/Interfaces/ICommandLineHandler.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using iFileProxy.Services;
|
||||
|
||||
namespace iFileProxy.Interfaces
|
||||
{
|
||||
public interface ICommandLineHandler
|
||||
{
|
||||
public void Process();
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using Serilog;
|
|||
using System.Text;
|
||||
using MySql.Data.MySqlClient;
|
||||
using iFileProxy.Middlewares;
|
||||
using System.Reflection;
|
||||
|
||||
namespace iFileProxy
|
||||
{
|
||||
|
@ -23,9 +24,24 @@ namespace iFileProxy
|
|||
|
||||
CommandLineArgsHelper argsHelper = new(args);
|
||||
|
||||
// 解析命令行参数
|
||||
CommandLineArgumentDispatcher.Parse(args);
|
||||
|
||||
if (argsHelper.GetBooleanValue("--version") || argsHelper.GetBooleanValue("-v"))
|
||||
{
|
||||
var buildTime = MasterHelper.GetBuildTime(Assembly.GetEntryAssembly());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine(new string('=', 10));
|
||||
sb.AppendLine($"Build DateTime:\t{buildTime}");
|
||||
sb.AppendLine(new string('=', 10));
|
||||
|
||||
Console.WriteLine(sb.ToString());
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
Console.Write(" "); // 补全日志第一行开头的空白
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
// CORS配置
|
||||
builder.Services.AddCors(options =>
|
||||
|
@ -81,14 +97,11 @@ namespace iFileProxy
|
|||
// 初始化缓存管理服务
|
||||
LocalCacheManager localCacheManager = new(app.Services);
|
||||
|
||||
// 解析命令行参数
|
||||
CmdArgsHandler.ParseArgs(args, app.Services);
|
||||
|
||||
// 开启配置热重载
|
||||
app.Services.GetRequiredService<AppConfigService>().EnableHotReload();
|
||||
|
||||
|
||||
if (!argsHelper.GetBooleanValue("disable-startup-check"))
|
||||
if (!argsHelper.GetBooleanValue("--disable-startup-check|-D"))
|
||||
// 初始化验证配置文件
|
||||
AppConfig.CheckAppConfig(app.Services);
|
||||
|
||||
|
@ -141,8 +154,8 @@ namespace iFileProxy
|
|||
var dbGateService = app.Services.GetRequiredService<DatabaseGateService>();
|
||||
SerilogConfig.CreateLogger(dbGateService, args);
|
||||
|
||||
if (!argsHelper.GetStringValue("url").IsNullOrEmpty())
|
||||
app.Run(argsHelper.GetStringValue("url"));
|
||||
if (!argsHelper.GetStringValue("--bind-url|-B").IsNullOrEmpty())
|
||||
app.Run(argsHelper.GetStringValue("--bind-url|-B"));
|
||||
else
|
||||
app.Run();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
|||
<_TargetId>Folder</_TargetId>
|
||||
<SiteUrlToLaunchAfterPublish />
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ProjectGuid>e343bd8a-27ed-47e2-b50d-e3000730e65e</ProjectGuid>
|
||||
<SelfContained>false</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
var loggerConfiguration = new LoggerConfiguration();
|
||||
|
||||
|
||||
if (args != null && new CommandLineArgsHelper(args).GetBooleanValue("dev-logger"))
|
||||
if (args != null && new CommandLineArgsHelper(args).GetBooleanValue("--dev-logging|-d"))
|
||||
{
|
||||
loggerConfiguration.MinimumLevel.Debug();
|
||||
}
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MySql.Data" Version="9.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Serilog" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<SourceRevisionId>build$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmssZ"))</SourceRevisionId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MySql.Data" Version="9.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Serilog" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
Loading…
Reference in a new issue