diff --git a/Directory.Build.props b/Directory.Build.props index 9674e88..3b034a5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ 1 - 10 + 11 0 $(VersionMajor).$(VersionMinor).$(VersionPatch) develop diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..c72916f --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,19 @@ + + + true + true + + + + + + + + + + + + + + + diff --git a/build.cake b/build.cake deleted file mode 100644 index 44b4bc3..0000000 --- a/build.cake +++ /dev/null @@ -1,165 +0,0 @@ -using System.Collections.Generic; -/////////////////////////////////////////////////////////////////////////////// -// ARGUMENTS -/////////////////////////////////////////////////////////////////////////////// - -var apiKey = Argument("apiKey", ""); -var stable = Argument("stable", "false"); -var target = Argument("target", "Default"); -var configuration = Argument("configuration", "Release"); - -var solutionPath = "./WeihanLi.Web.Extensions.sln"; -var srcProjects = GetFiles("./src/**/*.csproj"); -var packProjects = GetFiles("./src/**/*.csproj"); -var testProjects = GetFiles("./test/**/*.csproj"); - -var artifacts = "./artifacts/packages"; -var isWindowsAgent = (EnvironmentVariable("Agent_OS") ?? "Windows_NT") == "Windows_NT"; -var branchName = EnvironmentVariable("BUILD_SOURCEBRANCHNAME") ?? "local"; - -/////////////////////////////////////////////////////////////////////////////// -// SETUP / TEARDOWN -/////////////////////////////////////////////////////////////////////////////// - -Setup(ctx => -{ - // Executed BEFORE the first task. - Information("Running tasks..."); - PrintBuildInfo(); -}); - -Teardown(ctx => -{ - // Executed AFTER the last task. - Information("Finished running tasks."); -}); - -/////////////////////////////////////////////////////////////////////////////// -// TASKS -/////////////////////////////////////////////////////////////////////////////// - -Task("clean") - .Description("Clean") - .Does(() => - { - var deleteSetting = new DeleteDirectorySettings() - { - Force = true, - Recursive = true - }; - if (DirectoryExists(artifacts)) - { - DeleteDirectory(artifacts, deleteSetting); - } - }); - -Task("restore") - .Description("Restore") - .Does(() => - { - foreach(var project in srcProjects) - { - DotNetRestore(project.FullPath); - } - }); - -Task("build") - .Description("Build") - .IsDependentOn("clean") - .IsDependentOn("restore") - .Does(() => - { - var buildSetting = new DotNetBuildSettings{ - NoRestore = true, - Configuration = configuration - }; - foreach(var project in srcProjects) - { - DotNetBuild(project.FullPath, buildSetting); - } - }); - -Task("test") - .Description("Test") - .IsDependentOn("build") - .Does(() => - { - var testSettings = new DotNetTestSettings{ - NoRestore = true, - Configuration = configuration - }; - foreach(var project in testProjects) - { - DotNetTest(project.FullPath, testSettings); - } - }); - -Task("pack") - .Description("Pack package") - .IsDependentOn("build") - .Does((context) => - { - var settings = new DotNetPackSettings - { - Configuration = configuration, - OutputDirectory = artifacts, - VersionSuffix = "", - NoRestore = true, - NoBuild = true - }; - if(branchName != "master" && stable != "true"){ - settings.VersionSuffix = $"preview-{DateTime.UtcNow:yyyyMMdd-HHmmss}"; - } - foreach (var project in packProjects) - { - DotNetPack(project.FullPath, settings); - } - PublishArtifacts(context); - }); - -bool PublishArtifacts(ICakeContext context) -{ - if (context.Environment.Platform.IsUnix()) - { - return false; - } - var publishBranches = new HashSet() - { - "local", - "main", - "master", - "preview" - }; - - if (string.IsNullOrEmpty(apiKey) && publishBranches.Contains(branchName)) - { - apiKey = EnvironmentVariable("Nuget__ApiKey"); - } - if (!string.IsNullOrEmpty(apiKey)) - { - var pushSetting = new DotNetNuGetPushSettings - { - Source = "https://api.nuget.org/v3/index.json", - ApiKey = apiKey, - SkipDuplicate = true - }; - var packages = GetFiles($"{artifacts}/*.nupkg"); - foreach (var package in packages) - { - DotNetNuGetPush(package.FullPath, pushSetting); - } - return true; - } - return false; -} - -void PrintBuildInfo(){ - Information($@"branch:{branchName}, agentOs={EnvironmentVariable("Agent_OS")} - BuildID:{EnvironmentVariable("BUILD_BUILDID")},BuildNumber:{EnvironmentVariable("BUILD_BUILDNUMBER")},BuildReason:{EnvironmentVariable("BUILD_REASON")} - "); -} - -Task("Default") - .IsDependentOn("pack"); - -RunTarget(target); \ No newline at end of file diff --git a/build/build.cs b/build/build.cs index 30541b2..12b0793 100644 --- a/build/build.cs +++ b/build/build.cs @@ -1,7 +1,7 @@ -var target = CommandLineParser.Val("target", "Default", args); -var apiKey = CommandLineParser.Val("apiKey", "", args); -var stable = CommandLineParser.Val("stable", null, args).ToBoolean(); -var noPush = CommandLineParser.Val("noPush", null, args).ToBoolean(); +var target = CommandLineParser.Val("target", args, "Default"); +var apiKey = CommandLineParser.Val("apiKey", args); +var stable = CommandLineParser.BooleanVal("stable", args); +var noPush = CommandLineParser.BooleanVal("noPush", args); var branchName = EnvHelper.Val("BUILD_SOURCEBRANCHNAME", "local"); var solutionPath = "./WeihanLi.Web.Extensions.sln"; @@ -114,4 +114,4 @@ async Task ExecuteCommandAsync(string commandText, KeyValuePair[ var result = await CommandExecutor.ExecuteCommandAndOutputAsync(commandText); result.EnsureSuccessExitCode(); Console.WriteLine(); -} \ No newline at end of file +} diff --git a/docfx.json b/docfx.json index 212bb9c..27f9491 100644 --- a/docfx.json +++ b/docfx.json @@ -3,60 +3,43 @@ { "src": [ { + "src": "./src", "files": [ - "src/**.csproj", - "src/WeihanLi.Web.Extensions/*.cs", - "src/**/*.cs" - ], - "exclude": [ "**/bin/**", "**/obj/**" ] + "**/*.csproj" + ] } ], - "dest": "docs/api", - "disableGitFeatures": false, - "disableDefaultFilter": false + "dest": "docs/api" } ], "build": { - "content": [ + "content": [ { "files": [ - "docs/api/**.md", - "docs/api/**.yml", - "docs/api/**/toc.yml", - "toc.yml", - "*.md" + "**/*.{md,yml}" + ], + "exclude": [ + "_site/**" ] } ], "resource": [ { "files": [ - "docs/images" - ] - } - ], - "overwrite": [ - { - "files": [ - "docs/api/**.md" - ], - "exclude": [ - "obj/**", - "_site/**" + "images/**" ] } ], - "dest": "_site", - "globalMetadataFiles": [], - "fileMetadataFiles": [], + "output": "_site", "template": [ - "default" + "default", + "modern" ], - "postProcessors": [], - "markdownEngineName": "markdig", - "noLangKeyword": false, - "keepFileLink": false, - "cleanupCacheHistory": false, - "disableGitFeatures": false + "globalMetadata": { + "_appName": "WeihanLi.Web.Extensions", + "_appTitle": "WeihanLi.Web.Extensions", + "_enableSearch": true, + "pdf": false + } } } \ No newline at end of file diff --git a/samples/WeihanLi.Web.Extensions.Samples/Program.cs b/samples/WeihanLi.Web.Extensions.Samples/Program.cs index db96781..466a199 100644 --- a/samples/WeihanLi.Web.Extensions.Samples/Program.cs +++ b/samples/WeihanLi.Web.Extensions.Samples/Program.cs @@ -4,6 +4,7 @@ using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using System.Reflection; +using System.Text; using System.Text.Json.Serialization; using WeihanLi.Common.Aspect; using WeihanLi.Common.Models; @@ -41,7 +42,7 @@ options.KeyLocation = KeyLocation.HeaderOrQuery; }) ; -builder.Services.AddJwtTokenServiceWithJwtBearerAuth(options => +builder.Services.AddJwtServiceWithJwtBearerAuth(options => { options.SecretKey = Guid.NewGuid().ToString(); options.Issuer = "https://id.weihanli.xyz"; @@ -137,10 +138,55 @@ options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); }); -app.MapConfigInspector().ShortCircuit(); - app.UseAuthentication(); app.UseAuthorization(); + +// app.MapConfigInspector(optionsConfigure: options => +// { +// options.ConfigRenderer = async (context, configs) => +// { +// var htmlStart = """ +// +// +// Config Inspector +// +// +// +// +// +// +// +// +// +// +// +// +// """; +// var htmlEnd = "
ProviderKeyValueActive
"; +// var tbody = new StringBuilder(); +// foreach (var config in configs) +// { +// tbody.Append($"{config.Provider}"); +// foreach (var item in config.Items) +// { +// tbody.Append( +// $$"""{{item.Key}}{{item.Value}}"""); +// } +// +// tbody.AppendLine(""); +// } +// +// var responseText = $"{htmlStart}{tbody}{htmlEnd}"; +// await context.Response.WriteAsync(responseText); +// }; +// }); + +app.MapConfigInspector() + // .RequireAuthorization(x => x + // .AddAuthenticationSchemes("ApiKey") + // .RequireAuthenticatedUser() + // ) + ; app.MapControllers(); await app.RunAsync(); diff --git a/samples/WeihanLi.Web.Extensions.Samples/WeihanLi.Web.Extensions.Samples.csproj b/samples/WeihanLi.Web.Extensions.Samples/WeihanLi.Web.Extensions.Samples.csproj index 27e092f..36a1de4 100644 --- a/samples/WeihanLi.Web.Extensions.Samples/WeihanLi.Web.Extensions.Samples.csproj +++ b/samples/WeihanLi.Web.Extensions.Samples/WeihanLi.Web.Extensions.Samples.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -11,6 +11,6 @@ - + \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 285474c..533b9eb 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,7 +1,8 @@ - + + enable true $(NoWarn);1591 README.md diff --git a/src/WeihanLi.Web.Extensions/Authentication/ApiKeyAuthentication/ApiKeyAuthenticationHandler.cs b/src/WeihanLi.Web.Extensions/Authentication/ApiKeyAuthentication/ApiKeyAuthenticationHandler.cs index d14244e..fed359a 100644 --- a/src/WeihanLi.Web.Extensions/Authentication/ApiKeyAuthentication/ApiKeyAuthenticationHandler.cs +++ b/src/WeihanLi.Web.Extensions/Authentication/ApiKeyAuthentication/ApiKeyAuthenticationHandler.cs @@ -36,20 +36,19 @@ protected override async Task HandleAuthenticateAsync() return AuthenticateResult.NoResult(); var validator = Options.ApiKeyValidator ?? ((_, keyValue) => Task.FromResult(string.Equals(Options.ApiKey, keyValue))); - if (await validator.Invoke(Context, keyValues.ToString())) + if (!await validator.Invoke(Context, keyValues.ToString())) + return AuthenticateResult.Fail("Invalid ApiKey"); + + var claims = new[] { - var claims = new[] - { - new Claim("issuer", ClaimsIssuer), - }.Union(Options.ClaimsGenerator?.Invoke(Context, Options) ?? Array.Empty()); - return AuthenticateResult.Success( - new AuthenticationTicket( - new ClaimsPrincipal(new[] - { - new ClaimsIdentity(claims, Scheme.Name) - }), Scheme.Name) - ); - } - return AuthenticateResult.Fail("Invalid ApiKey"); + new Claim("issuer", ClaimsIssuer), + }.Union(Options.ClaimsGenerator?.Invoke(Context, Options) ?? []); + return AuthenticateResult.Success( + new AuthenticationTicket( + new ClaimsPrincipal(new[] + { + new ClaimsIdentity(claims, Scheme.Name) + }), Scheme.Name) + ); } } diff --git a/src/WeihanLi.Web.Extensions/Authentication/HeaderAuthentication/HeaderAuthenticationHandler.cs b/src/WeihanLi.Web.Extensions/Authentication/HeaderAuthentication/HeaderAuthenticationHandler.cs index 62cfe96..cf81163 100644 --- a/src/WeihanLi.Web.Extensions/Authentication/HeaderAuthentication/HeaderAuthenticationHandler.cs +++ b/src/WeihanLi.Web.Extensions/Authentication/HeaderAuthentication/HeaderAuthenticationHandler.cs @@ -37,7 +37,7 @@ protected override async Task HandleAuthenticateAsync() if (Request.Headers.TryGetValue(Options.UserRolesHeaderName, out var userRolesValues)) { var userRoles = userRolesValues.ToString() - .Split(new[] { Options.Delimiter }, StringSplitOptions.RemoveEmptyEntries); + .Split([Options.Delimiter], StringSplitOptions.RemoveEmptyEntries); claims.AddRange(userRoles.Select(r => new Claim(ClaimTypes.Role, r))); } @@ -47,7 +47,7 @@ protected override async Task HandleAuthenticateAsync() { if (Request.Headers.TryGetValue(headerToClaim.Key, out var headerValues)) { - foreach (var val in headerValues.ToString().Split(new[] { Options.Delimiter }, StringSplitOptions.RemoveEmptyEntries)) + foreach (var val in headerValues.ToString().Split([Options.Delimiter], StringSplitOptions.RemoveEmptyEntries)) { claims.Add(new Claim(headerToClaim.Value, val)); } diff --git a/src/WeihanLi.Web.Extensions/Authentication/QueryAuthentication/QueryAuthenticationHandler.cs b/src/WeihanLi.Web.Extensions/Authentication/QueryAuthentication/QueryAuthenticationHandler.cs index 67b3bbe..472fa93 100644 --- a/src/WeihanLi.Web.Extensions/Authentication/QueryAuthentication/QueryAuthenticationHandler.cs +++ b/src/WeihanLi.Web.Extensions/Authentication/QueryAuthentication/QueryAuthenticationHandler.cs @@ -38,7 +38,7 @@ protected override async Task HandleAuthenticateAsync() if (Request.Query.TryGetValue(Options.UserRolesQueryKey, out var userRolesValues)) { var userRoles = userRolesValues.ToString() - .Split(new[] { Options.Delimiter }, StringSplitOptions.RemoveEmptyEntries); + .Split([Options.Delimiter], StringSplitOptions.RemoveEmptyEntries); claims.AddRange(userRoles.Select(r => new Claim(ClaimTypes.Role, r))); } if (Options.AdditionalQueryToClaims.Count > 0) @@ -47,7 +47,7 @@ protected override async Task HandleAuthenticateAsync() { if (Request.Query.TryGetValue(queryToClaim.Key, out var queryValues)) { - foreach (var val in queryValues.ToString().Split(new[] { Options.Delimiter }, StringSplitOptions.RemoveEmptyEntries)) + foreach (var val in queryValues.ToString().Split([Options.Delimiter], StringSplitOptions.RemoveEmptyEntries)) { claims.Add(new Claim(queryToClaim.Value, val)); } diff --git a/src/WeihanLi.Web.Extensions/Authorization/Jwt/DependencyInjectionExtensions.cs b/src/WeihanLi.Web.Extensions/Authorization/Jwt/DependencyInjectionExtensions.cs index dce2828..f76e003 100644 --- a/src/WeihanLi.Web.Extensions/Authorization/Jwt/DependencyInjectionExtensions.cs +++ b/src/WeihanLi.Web.Extensions/Authorization/Jwt/DependencyInjectionExtensions.cs @@ -10,17 +10,17 @@ namespace WeihanLi.Web.Authorization.Jwt; public static class DependencyInjectionExtensions { - public static IServiceCollection AddJwtTokenService(this IServiceCollection serviceCollection, Action optionsAction) + public static IServiceCollection AddJwtService(this IServiceCollection serviceCollection, Action optionsAction) { Guard.NotNull(serviceCollection); Guard.NotNull(optionsAction); serviceCollection.Configure(optionsAction); - serviceCollection.TryAddSingleton(); - serviceCollection.ConfigureOptions(); + serviceCollection.TryAddSingleton(); + serviceCollection.ConfigureOptions(); return serviceCollection; } - public static IServiceCollection AddJwtTokenServiceWithJwtBearerAuth(this IServiceCollection serviceCollection, Action optionsAction, Action jwtBearerOptionsSetup = null) + public static IServiceCollection AddJwtServiceWithJwtBearerAuth(this IServiceCollection serviceCollection, Action optionsAction, Action jwtBearerOptionsSetup = null) { Guard.NotNull(serviceCollection); Guard.NotNull(optionsAction); @@ -29,6 +29,6 @@ public static IServiceCollection AddJwtTokenServiceWithJwtBearerAuth(this IServi serviceCollection.Configure(jwtBearerOptionsSetup); } serviceCollection.ConfigureOptions(); - return serviceCollection.AddJwtTokenService(optionsAction); + return serviceCollection.AddJwtService(optionsAction); } } diff --git a/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenOptions.cs b/src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenOptions.cs similarity index 99% rename from src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenOptions.cs rename to src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenOptions.cs index 2054e6d..efc2b96 100644 --- a/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenOptions.cs +++ b/src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenOptions.cs @@ -6,7 +6,7 @@ namespace WeihanLi.Web.Authorization.Jwt; -public sealed class JwtTokenOptions +public sealed class JsonWebTokenOptions { /// /// "iss" (Issuer) Claim diff --git a/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenOptionsSetup.cs b/src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenOptionsSetup.cs similarity index 76% rename from src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenOptionsSetup.cs rename to src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenOptionsSetup.cs index 3e3b08e..eaf381b 100644 --- a/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenOptionsSetup.cs +++ b/src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenOptionsSetup.cs @@ -2,14 +2,13 @@ // Licensed under the MIT license. using Microsoft.IdentityModel.Tokens; -using WeihanLi.Common; using WeihanLi.Extensions; namespace WeihanLi.Web.Authorization.Jwt; -internal sealed class JwtTokenOptionsSetup : IPostConfigureOptions +internal sealed class JsonWebTokenOptionsSetup : IPostConfigureOptions { - public void PostConfigure(string name, JwtTokenOptions options) + public void PostConfigure(string name, JsonWebTokenOptions options) { if (options.SigningCredentialsFactory is null) { @@ -18,8 +17,8 @@ public void PostConfigure(string name, JwtTokenOptions options) options.SigningCredentialsFactory = () => new SigningCredentials(new SymmetricSecurityKey(options.SecretKey.GetBytes()), SecurityAlgorithms.HmacSha256); } } - Guard.NotNull(options.SigningCredentialsFactory); - options.SigningCredentials = options.SigningCredentialsFactory(); + ArgumentNullException.ThrowIfNull(options.SigningCredentialsFactory); + options.SigningCredentials = options.SigningCredentialsFactory.Invoke(); options.RefreshTokenSigningCredentials = options.RefreshTokenSigningCredentials is null ? options.SigningCredentials : options.RefreshTokenSigningCredentialsFactory() diff --git a/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenService.cs b/src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenService.cs similarity index 94% rename from src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenService.cs rename to src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenService.cs index 50ba755..6f89722 100644 --- a/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtTokenService.cs +++ b/src/WeihanLi.Web.Extensions/Authorization/Jwt/JsonWebTokenService.cs @@ -10,17 +10,17 @@ namespace WeihanLi.Web.Authorization.Jwt; -public class JwtTokenService : ITokenService +public class JsonWebTokenService : ITokenService { private readonly IHttpContextAccessor _httpContextAccessor; private readonly JwtSecurityTokenHandler _tokenHandler = new(); - private readonly JwtTokenOptions _tokenOptions; + private readonly JsonWebTokenOptions _tokenOptions; private readonly Lazy _lazyTokenValidationParameters, _lazyRefreshTokenValidationParameters; - public JwtTokenService(IHttpContextAccessor httpContextAccessor, IOptions tokenOptions) + public JsonWebTokenService(IHttpContextAccessor httpContextAccessor, IOptions tokenOptions) { _httpContextAccessor = httpContextAccessor; _tokenOptions = tokenOptions.Value; @@ -60,7 +60,7 @@ public virtual async Task RefreshToken(string refreshToken) protected virtual Task GetRefreshToken(Claim[] claims, string jti) { - var claimList = new List((claims ?? Array.Empty()) + var claimList = new List((claims ?? []) .Where(c => c.Type != _tokenOptions.RefreshTokenOwnerClaimType) .Union(new[] { new Claim(_tokenOptions.RefreshTokenOwnerClaimType, jti) }) ); @@ -83,14 +83,14 @@ protected virtual Task GetRefreshToken(Claim[] claims, string jti) return encodedJwt.WrapTask(); } - private static readonly HashSet JwtInternalClaimTypes = new() - { + private static readonly HashSet JwtInternalClaimTypes = + [ "iss", "exp", "aud", "nbf", "iat" - }; + ]; private async Task GenerateTokenInternal(bool refreshToken, Claim[] claims) { diff --git a/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtBearerOptionsPostSetup.cs b/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtBearerOptionsPostSetup.cs index 3733faa..5294665 100644 --- a/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtBearerOptionsPostSetup.cs +++ b/src/WeihanLi.Web.Extensions/Authorization/Jwt/JwtBearerOptionsPostSetup.cs @@ -8,9 +8,9 @@ namespace WeihanLi.Web.Authorization.Jwt; internal sealed class JwtBearerOptionsPostSetup : IPostConfigureOptions { - private readonly IOptions _options; + private readonly IOptions _options; - public JwtBearerOptionsPostSetup(IOptions options) + public JwtBearerOptionsPostSetup(IOptions options) { _options = options; } diff --git a/src/WeihanLi.Web.Extensions/DataProtection/ParamsProtection/ParamsProtectionOptions.cs b/src/WeihanLi.Web.Extensions/DataProtection/ParamsProtection/ParamsProtectionOptions.cs index 07f9446..bbdef0c 100644 --- a/src/WeihanLi.Web.Extensions/DataProtection/ParamsProtection/ParamsProtectionOptions.cs +++ b/src/WeihanLi.Web.Extensions/DataProtection/ParamsProtection/ParamsProtectionOptions.cs @@ -9,7 +9,7 @@ namespace WeihanLi.Web.DataProtection.ParamsProtection; public sealed class ParamsProtectionOptions { - private string[] _protectParams = Array.Empty(); + private string[] _protectParams = []; /// /// ProtectorPurpose diff --git a/src/WeihanLi.Web.Extensions/Extensions/EndpointExtensions.cs b/src/WeihanLi.Web.Extensions/Extensions/EndpointExtensions.cs index 76ef4c9..8dd869e 100644 --- a/src/WeihanLi.Web.Extensions/Extensions/EndpointExtensions.cs +++ b/src/WeihanLi.Web.Extensions/Extensions/EndpointExtensions.cs @@ -14,18 +14,14 @@ public static IEndpointConventionBuilder MapRuntimeInfo(this IEndpointRouteBuild return endpointRouteBuilder.MapGet(path, () => ApplicationHelper.RuntimeInfo); } - public static IEndpointConventionBuilder MapConfigInspector(this IEndpointRouteBuilder endpointRouteBuilder, string path = "/config-inspector", - Action optionsConfigure = null) + public static IEndpointConventionBuilder MapConfigInspector(this IEndpointRouteBuilder endpointRouteBuilder, + string path = "/config-inspector", + Action? optionsConfigure = null + ) { ArgumentNullException.ThrowIfNull(endpointRouteBuilder); var app = endpointRouteBuilder.CreateApplicationBuilder(); - var pipeline = app.UseConfigInspector(Configure).Build(); - return endpointRouteBuilder.MapGet(path, pipeline); - - void Configure(ConfigInspectorOptions options) - { - options.Path = path; - optionsConfigure?.Invoke(options); - } + var pipeline = app.UseConfigInspector(optionsConfigure).Build(); + return endpointRouteBuilder.MapGet($"{path}/{{configKey?}}", pipeline); } } diff --git a/src/WeihanLi.Web.Extensions/Extensions/MiddlewareExtension.cs b/src/WeihanLi.Web.Extensions/Extensions/MiddlewareExtension.cs index 8d17680..5461a47 100644 --- a/src/WeihanLi.Web.Extensions/Extensions/MiddlewareExtension.cs +++ b/src/WeihanLi.Web.Extensions/Extensions/MiddlewareExtension.cs @@ -14,7 +14,7 @@ public static class MiddlewareExtension /// public static IApplicationBuilder UseCustomExceptionHandler(this IApplicationBuilder applicationBuilder) { - applicationBuilder.UseMiddleware(); + applicationBuilder.UseMiddleware(); return applicationBuilder; } @@ -87,8 +87,8 @@ public static IApplicationBuilder UseWhenFeatureEnabled( /// /// Use ConfigInspector to inspect config when necessary /// - public static IApplicationBuilder UseConfigInspector(this IApplicationBuilder app, - Action optionsConfigure = null) + internal static IApplicationBuilder UseConfigInspector(this IApplicationBuilder app, + Action? optionsConfigure = null) { ArgumentNullException.ThrowIfNull(app); if (optionsConfigure is not null) diff --git a/src/WeihanLi.Web.Extensions/Extensions/ResultExtensions.cs b/src/WeihanLi.Web.Extensions/Extensions/ResultExtensions.cs index 33b170e..fd4a720 100644 --- a/src/WeihanLi.Web.Extensions/Extensions/ResultExtensions.cs +++ b/src/WeihanLi.Web.Extensions/Extensions/ResultExtensions.cs @@ -21,11 +21,10 @@ public static IActionResult GetRestResult(this T result, ResultStatus status if (result is null) return new NoContentResult(); -#pragma warning disable CS0618 // Type or member is obsolete return status switch { - ResultStatus.RequestError or ResultStatus.BadRequest => new BadRequestObjectResult(result), - ResultStatus.ResourceNotFound or ResultStatus.NotFound => new NotFoundObjectResult(result), + ResultStatus.BadRequest => new BadRequestObjectResult(result), + ResultStatus.NotFound => new NotFoundObjectResult(result), ResultStatus.MethodNotAllowed => new ObjectResult(result) { StatusCode = (int)HttpStatusCode.MethodNotAllowed @@ -34,7 +33,6 @@ public static IActionResult GetRestResult(this T result, ResultStatus status ResultStatus.NoPermission or ResultStatus.Forbidden => new ObjectResult(result) { StatusCode = (int)HttpStatusCode.Forbidden }, _ => new OkObjectResult(result) }; -#pragma warning restore CS0618 // Type or member is obsolete } public static IActionResult GetRestResult(this Result result) diff --git a/src/WeihanLi.Web.Extensions/Middleware/ConfigInspectorMiddleware.cs b/src/WeihanLi.Web.Extensions/Middleware/ConfigInspectorMiddleware.cs index 13bb769..e5d02ce 100644 --- a/src/WeihanLi.Web.Extensions/Middleware/ConfigInspectorMiddleware.cs +++ b/src/WeihanLi.Web.Extensions/Middleware/ConfigInspectorMiddleware.cs @@ -5,21 +5,20 @@ namespace WeihanLi.Web.Middleware; public sealed class ConfigInspectorOptions { - public string Path { get; set; } = "/config-inspector"; public bool IncludeEmptyProviders { get; set; } - public Func ConfigRenderer { get; set; } + public Func? ConfigRenderer { get; set; } } public sealed class ConfigModel { - public string Provider { get; set; } - public ConfigItemModel[] Items { get; set; } + public string Provider { get; set; } = default!; + public ConfigItemModel[] Items { get; set; } = []; } public sealed class ConfigItemModel { - public string Key { get; set; } - public string Value { get; set; } + public string Key { get; set; } = default!; + public string? Value { get; set; } public bool Active { get; set; } } @@ -27,11 +26,6 @@ internal sealed class ConfigInspectorMiddleware(RequestDelegate next) { public Task InvokeAsync(HttpContext httpContext, IOptions inspectorOptions) { - if (httpContext.Request.Path.ToString() != inspectorOptions.Value.Path) - { - return next(httpContext); - } - var configuration = httpContext.RequestServices.GetRequiredService(); if (configuration is not IConfigurationRoot configurationRoot) { @@ -40,17 +34,43 @@ public Task InvokeAsync(HttpContext httpContext, IOptions 0 } configKeyName) + { + configKey = configKeyName; + } + + var configs = GetConfig(configurationRoot, inspectorOptionsValue, configKey); if (inspectorOptionsValue.ConfigRenderer is null) return httpContext.Response.WriteAsJsonAsync(configs); return inspectorOptionsValue.ConfigRenderer.Invoke(httpContext, configs); } - private static ConfigModel[] GetConfig(IConfigurationRoot configurationRoot, ConfigInspectorOptions options) + private static ConfigModel[] GetConfig(IConfigurationRoot configurationRoot, ConfigInspectorOptions options, + string configKey) { var allKeys = configurationRoot.AsEnumerable() .ToDictionary(x => x.Key, _ => false); + + var hasConfigKeyFilter = !string.IsNullOrEmpty(configKey); + if (hasConfigKeyFilter) + { + if (allKeys.TryGetValue(configKey, out _)) + { + allKeys = new() + { + { configKey, false } + }; + } + else + { + return []; + } + } + var providers = GetConfigProviders(configurationRoot); var config = new ConfigModel[providers.Count]; @@ -74,27 +94,30 @@ private static ConfigModel[] GetConfig(IConfigurationRoot configurationRoot, Con private static List GetConfigProviders(IConfigurationRoot configurationRoot) { +#if NET7_0_OR_GREATER var providers = new List(); foreach (var provider in configurationRoot.Providers) { + if (provider is not ChainedConfigurationProvider chainedConfigurationProvider) { providers.Add(provider); continue; } -#if NET7_0_OR_GREATER if (chainedConfigurationProvider.Configuration is not IConfigurationRoot chainsConfigurationRoot) { continue; } providers.AddRange(GetConfigProviders(chainsConfigurationRoot)); -#endif } return providers; +#else + return configurationRoot.Providers.ToList(); +#endif } private static IEnumerable GetConfig(IConfigurationProvider provider, diff --git a/src/WeihanLi.Web.Extensions/Pager/PagedListModel.cs b/src/WeihanLi.Web.Extensions/Pager/PagedListModel.cs index c1c89e9..e5495c5 100644 --- a/src/WeihanLi.Web.Extensions/Pager/PagedListModel.cs +++ b/src/WeihanLi.Web.Extensions/Pager/PagedListModel.cs @@ -19,7 +19,7 @@ internal sealed class PagedListModel : IPagedListModel public PagedListModel(IEnumerable data, IPagerModel pager) { - Data = data?.ToArray() ?? Array.Empty(); + Data = data?.ToArray() ?? []; Pager = pager; } diff --git a/src/WeihanLi.Web.Extensions/WeihanLi.Web.Extensions.csproj b/src/WeihanLi.Web.Extensions/WeihanLi.Web.Extensions.csproj index 99c05c8..b0507b1 100644 --- a/src/WeihanLi.Web.Extensions/WeihanLi.Web.Extensions.csproj +++ b/src/WeihanLi.Web.Extensions/WeihanLi.Web.Extensions.csproj @@ -10,12 +10,8 @@ WeihanLi.Web - - - - - - + + diff --git a/test/WeihanLi.Web.Extensions.UnitTest/WeihanLi.Web.Extensions.UnitTest.csproj b/test/WeihanLi.Web.Extensions.UnitTest/WeihanLi.Web.Extensions.UnitTest.csproj index bb6a085..0b69daa 100644 --- a/test/WeihanLi.Web.Extensions.UnitTest/WeihanLi.Web.Extensions.UnitTest.csproj +++ b/test/WeihanLi.Web.Extensions.UnitTest/WeihanLi.Web.Extensions.UnitTest.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -7,9 +7,9 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all