diff --git a/src/ThingsGateway.Admin.Application/Extensions/StartupExtensions.cs b/src/ThingsGateway.Admin.Application/Extensions/StartupExtensions.cs index 8f3ffdaa8..8603e00fb 100644 --- a/src/ThingsGateway.Admin.Application/Extensions/StartupExtensions.cs +++ b/src/ThingsGateway.Admin.Application/Extensions/StartupExtensions.cs @@ -12,7 +12,9 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileSystemGlobbing; using Microsoft.Extensions.Hosting; using System.Collections.Concurrent; @@ -26,6 +28,110 @@ public static class StartupExtensions { private static ConcurrentBag AppStartups = new(); + /// + /// 排除的配置文件前缀 + /// + private static readonly string[] excludeJsonPrefixs = new[] { "appsettings", "bundleconfig", "compilerconfig" }; + + /// + /// 排除运行时 Json 后缀 + /// + private static readonly string[] runtimeJsonSuffixs = new string[] + { + "deps.json", + "runtimeconfig.dev.json", + "runtimeconfig.prod.json", + "runtimeconfig.json", + "staticwebassets.runtime.json" + }; + + /// + /// 对配置文件名进行分组 + /// + /// + /// + private static IEnumerable> SplitConfigFileNameToGroups(IEnumerable configFiles) + { + // 分组 + return configFiles.GroupBy(Function); + + // 本地函数 + static string Function(string file) + { + // 根据 . 分隔 + var fileNameParts = Path.GetFileName(file).Split('.', StringSplitOptions.RemoveEmptyEntries); + if (fileNameParts.Length == 2) return fileNameParts[0]; + + return string.Join('.', fileNameParts.Take(fileNameParts.Length - 2)); + } + } + + /// + /// 加载自定义 .json 配置文件 + /// + /// + /// + internal static void AddJsonFiles(IConfigurationBuilder configurationBuilder, IHostEnvironment hostEnvironment) + { + // 获取根配置 +#if !NET5_0 + var configuration = configurationBuilder is ConfigurationManager + ? (configurationBuilder as ConfigurationManager) + : configurationBuilder.Build(); +#else + var configuration = configurationBuilder.Build(); +#endif + + // 获取程序执行目录 + var executeDirectory = AppContext.BaseDirectory; + + // 获取自定义配置扫描目录 + var configurationScanDirectories = (configuration.GetSection("ConfigurationScanDirectories") + .Get() + ?? Array.Empty()).Select(u => Path.Combine(executeDirectory, u)); + + // 扫描执行目录及自定义配置目录下的 *.json 文件 + var jsonFiles = new[] { executeDirectory } + .Concat(configurationScanDirectories) + .SelectMany(u => + Directory.GetFiles(u, "*.json", SearchOption.TopDirectoryOnly)); + + // 如果没有配置文件,中止执行 + if (!jsonFiles.Any()) return; + + // 获取环境变量名,如果没找到,则读取 NETCORE_ENVIRONMENT 环境变量信息识别(用于非 Web 环境) + var envName = hostEnvironment?.EnvironmentName ?? Environment.GetEnvironmentVariable("NETCORE_ENVIRONMENT") ?? "Unknown"; + + // 读取忽略的配置文件 + var ignoreConfigurationFiles = (configuration.GetSection("IgnoreConfigurationFiles") + .Get() + ?? Array.Empty()); + + // 处理控制台应用程序 + var _excludeJsonPrefixs = hostEnvironment == default ? excludeJsonPrefixs.Where(u => !u.Equals("appsettings")) : excludeJsonPrefixs; + + // 将所有文件进行分组 + var jsonFilesGroups = SplitConfigFileNameToGroups(jsonFiles) + .Where(u => !_excludeJsonPrefixs.Contains(u.Key, StringComparer.OrdinalIgnoreCase) && !u.Any(c => runtimeJsonSuffixs.Any(z => c.EndsWith(z, StringComparison.OrdinalIgnoreCase)) || ignoreConfigurationFiles.Contains(Path.GetFileName(c), StringComparer.OrdinalIgnoreCase) || ignoreConfigurationFiles.Any(i => new Matcher().AddInclude(i).Match(Path.GetFileName(c)).HasMatches))); + + // 遍历所有配置分组 + foreach (var group in jsonFilesGroups) + { + // 限制查找的 json 文件组 + var limitFileNames = new[] { $"{group.Key}.json", $"{group.Key}.{envName}.json" }; + + // 查找默认配置和环境配置 + var files = group.Where(u => limitFileNames.Contains(Path.GetFileName(u), StringComparer.OrdinalIgnoreCase)) + .OrderBy(u => Path.GetFileName(u).Length); + + // 循环加载 + foreach (var jsonFile in files) + { + configurationBuilder.AddJsonFile(jsonFile, optional: true, reloadOnChange: true); + } + } + } + /// /// 反射获取所有AppStartup的继承类,执行名称为第一个参数是的方法 /// @@ -49,6 +155,8 @@ public static class StartupExtensions internal static void AddStartups(this WebApplicationBuilder builder) { App.Configuration = builder.Configuration; + AddJsonFiles(builder.Configuration, builder.Environment); + if (builder.Environment is IWebHostEnvironment webHostEnvironment) App.WebRootPath = webHostEnvironment.WebRootPath; App.ContentRootPath = builder.Environment.ContentRootPath; diff --git a/src/ThingsGateway.Server/Dockerfile b/src/ThingsGateway.Server/Dockerfile index c8c3079f6..6468d4067 100644 --- a/src/ThingsGateway.Server/Dockerfile +++ b/src/ThingsGateway.Server/Dockerfile @@ -1,5 +1,8 @@ #See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. +#构建:docker build -t diego2098/thingsgateway:latest . + + #aspnetcore6.0环境 FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base COPY . /app diff --git a/src/ThingsGateway.Server/appsettings.Development.json b/src/ThingsGateway.Server/appsettings.Development.json index 711271929..bff3aa29d 100644 --- a/src/ThingsGateway.Server/appsettings.Development.json +++ b/src/ThingsGateway.Server/appsettings.Development.json @@ -3,6 +3,7 @@ "DetailedErrors": true, + "ConfigurationScanDirectories": [ "" ], //冗余配置 "ManagementOptions": { "PrimaryUri": "127.0.0.1:7101", //主(备)站IP diff --git a/src/ThingsGateway.Server/appsettings.json b/src/ThingsGateway.Server/appsettings.json index f5671f7f4..35611eb64 100644 --- a/src/ThingsGateway.Server/appsettings.json +++ b/src/ThingsGateway.Server/appsettings.json @@ -1,6 +1,7 @@ { //"urls": "http://*:7100", + "ConfigurationScanDirectories": [ "" ], //冗余配置 "ManagementOptions": { "PrimaryUri": "127.0.0.1:7101", //主(备)站IP