From acdc447bb4cdfca33020e14bc4d3482c4813d4bd Mon Sep 17 00:00:00 2001 From: root <295172551@qq.com> Date: Tue, 10 Jun 2025 23:28:33 +0800 Subject: [PATCH] Initial commit with .gitignore and project files --- .gitignore | 45 +++++++ .../Configurations/api-versioning.json | 12 ++ CoreAgent.API/Configurations/logger.json | 30 +++++ .../request-logging.Development.json | 17 +++ .../Configurations/request-logging.json | 17 +++ .../Controllers/WeatherForecastController.cs | 45 +++++++ CoreAgent.API/CoreAgent.API.csproj | 28 +++++ CoreAgent.API/CoreAgent.API.http | 6 + CoreAgent.API/Program.cs | 67 ++++++++++ CoreAgent.API/Properties/launchSettings.json | 41 ++++++ CoreAgent.API/Startup.cs | 118 ++++++++++++++++++ CoreAgent.API/appsettings.Development.json | 8 ++ CoreAgent.API/appsettings.json | 9 ++ CoreAgent.API/logs/log-20250610.txt | 98 +++++++++++++++ .../CoreAgent.Application.csproj | 9 ++ CoreAgent.Domain/CoreAgent.Domain.csproj | 13 ++ .../Exceptions/NotFoundException.cs | 14 +++ .../Exceptions/ValidationException.cs | 17 +++ CoreAgent.Domain/Models/ErrorResponse.cs | 23 ++++ .../CoreAgent.Infrastructure.csproj | 28 +++++ ...CoreAgent.Infrastructure.csproj.Backup.tmp | 13 ++ .../Extensions/ApiVersioningExtensions.cs | 44 +++++++ .../ExceptionMiddlewareExtensions.cs | 13 ++ .../Extensions/LoggingExtensions.cs | 27 ++++ .../Extensions/RequestLoggingExtensions.cs | 24 ++++ .../Logging/ApplicationLogger.cs | 65 ++++++++++ .../Middleware/ExceptionMiddleware.cs | 75 +++++++++++ .../Middleware/RequestLoggingMiddleware.cs | 110 ++++++++++++++++ .../Middleware/ResponseLoggingMiddleware.cs | 103 +++++++++++++++ .../Options/ApiVersioningSettings.cs | 45 +++++++ .../Options/RequestLoggingOptions.cs | 35 ++++++ CoreAgent.sln | 40 ++++++ 32 files changed, 1239 insertions(+) create mode 100644 .gitignore create mode 100644 CoreAgent.API/Configurations/api-versioning.json create mode 100644 CoreAgent.API/Configurations/logger.json create mode 100644 CoreAgent.API/Configurations/request-logging.Development.json create mode 100644 CoreAgent.API/Configurations/request-logging.json create mode 100644 CoreAgent.API/Controllers/WeatherForecastController.cs create mode 100644 CoreAgent.API/CoreAgent.API.csproj create mode 100644 CoreAgent.API/CoreAgent.API.http create mode 100644 CoreAgent.API/Program.cs create mode 100644 CoreAgent.API/Properties/launchSettings.json create mode 100644 CoreAgent.API/Startup.cs create mode 100644 CoreAgent.API/appsettings.Development.json create mode 100644 CoreAgent.API/appsettings.json create mode 100644 CoreAgent.API/logs/log-20250610.txt create mode 100644 CoreAgent.Application/CoreAgent.Application.csproj create mode 100644 CoreAgent.Domain/CoreAgent.Domain.csproj create mode 100644 CoreAgent.Domain/Exceptions/NotFoundException.cs create mode 100644 CoreAgent.Domain/Exceptions/ValidationException.cs create mode 100644 CoreAgent.Domain/Models/ErrorResponse.cs create mode 100644 CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj create mode 100644 CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp create mode 100644 CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs create mode 100644 CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs create mode 100644 CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs create mode 100644 CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs create mode 100644 CoreAgent.Infrastructure/Logging/ApplicationLogger.cs create mode 100644 CoreAgent.Infrastructure/Middleware/ExceptionMiddleware.cs create mode 100644 CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs create mode 100644 CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs create mode 100644 CoreAgent.Infrastructure/Options/ApiVersioningSettings.cs create mode 100644 CoreAgent.Infrastructure/Options/RequestLoggingOptions.cs create mode 100644 CoreAgent.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed0ace4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# .NET Core +bin/ +obj/ +*.user +*.userosscache +*.suo +*.userprefs +.vs/ +.vscode/ +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn + +# Visual Studio +.vs/ +*.ncrunchsolution +*.ncrunchproject +*.DotSettings +*.DotSettings.user \ No newline at end of file diff --git a/CoreAgent.API/Configurations/api-versioning.json b/CoreAgent.API/Configurations/api-versioning.json new file mode 100644 index 0000000..ad44831 --- /dev/null +++ b/CoreAgent.API/Configurations/api-versioning.json @@ -0,0 +1,12 @@ +{ + "ApiVersioning": { + "DefaultVersion": "1.0", + "ShowVersionInHeader": true, + "ShowVersionInResponse": true, + "ShowVersionInUrl": true, + "ShowVersionInQueryString": true, + "ShowVersionInMediaType": true, + "ReportApiVersions": true, + "AssumeDefaultVersionWhenUnspecified": true + } +} \ No newline at end of file diff --git a/CoreAgent.API/Configurations/logger.json b/CoreAgent.API/Configurations/logger.json new file mode 100644 index 0000000..0219f73 --- /dev/null +++ b/CoreAgent.API/Configurations/logger.json @@ -0,0 +1,30 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "logs/log-.txt", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}", + "shared": true, + "flushToDiskInterval": "00:00:01" + } + } + ], + "Enrich": [ "FromLogContext" ] + } +} \ No newline at end of file diff --git a/CoreAgent.API/Configurations/request-logging.Development.json b/CoreAgent.API/Configurations/request-logging.Development.json new file mode 100644 index 0000000..e6f98e2 --- /dev/null +++ b/CoreAgent.API/Configurations/request-logging.Development.json @@ -0,0 +1,17 @@ +{ + "RequestLogging": { + "LogRequestBody": true, + "LogResponseBody": true, + "MaxRequestBodySize": 2097152, + "MaxResponseBodySize": 2097152, + "ExcludePaths": [ + "/health", + "/metrics", + "/swagger", + "/favicon.ico" + ], + "ExcludeMethods": [ + "OPTIONS" + ] + } +} \ No newline at end of file diff --git a/CoreAgent.API/Configurations/request-logging.json b/CoreAgent.API/Configurations/request-logging.json new file mode 100644 index 0000000..7e06fc7 --- /dev/null +++ b/CoreAgent.API/Configurations/request-logging.json @@ -0,0 +1,17 @@ +{ + "RequestLogging": { + "LogRequestBody": true, + "LogResponseBody": true, + "MaxRequestBodySize": 1048576, + "MaxResponseBodySize": 1048576, + "ExcludePaths": [ + "/health", + "/metrics", + "/swagger", + "/favicon.ico" + ], + "ExcludeMethods": [ + "OPTIONS" + ] + } +} \ No newline at end of file diff --git a/CoreAgent.API/Controllers/WeatherForecastController.cs b/CoreAgent.API/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..dce0dd1 --- /dev/null +++ b/CoreAgent.API/Controllers/WeatherForecastController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.API.Controllers +{ + [ApiController] + [ApiVersion("1.0")] + [Route("api/v{version:apiVersion}/[controller]")] + public class WeatherForecastController : ControllerBase + { + private readonly ILogger _logger; + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public IActionResult Get() + { + _logger.LogInformation("Weather forecast requested"); + + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + ( + DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + Random.Shared.Next(-20, 55), + Summaries[Random.Shared.Next(Summaries.Length)] + )) + .ToArray(); + + _logger.LogDebug("Generated forecast for {Count} days", forecast.Length); + return Ok(forecast); + } + } + + public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) + { + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} \ No newline at end of file diff --git a/CoreAgent.API/CoreAgent.API.csproj b/CoreAgent.API/CoreAgent.API.csproj new file mode 100644 index 0000000..7809c42 --- /dev/null +++ b/CoreAgent.API/CoreAgent.API.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/CoreAgent.API/CoreAgent.API.http b/CoreAgent.API/CoreAgent.API.http new file mode 100644 index 0000000..230fd15 --- /dev/null +++ b/CoreAgent.API/CoreAgent.API.http @@ -0,0 +1,6 @@ +@CoreAgent.API_HostAddress = http://localhost:5228 + +GET {{CoreAgent.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/CoreAgent.API/Program.cs b/CoreAgent.API/Program.cs new file mode 100644 index 0000000..179b8ec --- /dev/null +++ b/CoreAgent.API/Program.cs @@ -0,0 +1,67 @@ +using CoreAgent.API; +using CoreAgent.Infrastructure.Logging; +using Serilog; +using System; +using System.Text.Json; + +var builder = WebApplication.CreateBuilder(args); + +try +{ + // 第一步:添加所有配置 + builder.AddConfigurations(); + + // 第二步:创建Startup实例 + var startup = builder.CreateStartup(); + + // 第三步:配置服务 + startup.ConfigureServices(builder.Services); + + var app = builder.Build(); + + // 配置中间件 + startup.Configure(app); + + // 配置Swagger + app.ConfigureSwagger(); + + Log.Information("Application startup completed"); + + // 注册应用程序关闭事件 + var lifetime = app.Lifetime; + lifetime.ApplicationStopping.Register(() => + { + Log.Information("Application is stopping..."); + // 在这里可以添加清理代码,比如关闭数据库连接等 + }); + + lifetime.ApplicationStopped.Register(() => + { + Log.Information("Application has stopped."); + // 确保所有日志都被写入 + Log.CloseAndFlush(); + }); + + app.Run(); +} +catch (Exception ex) +{ + Log.Fatal(ex, "Application failed to start: {Message}", ex.Message); + + // 记录详细的启动失败信息 + var errorDetails = new + { + Message = ex.Message, + StackTrace = ex.StackTrace, + Source = ex.Source, + InnerException = ex.InnerException?.Message + }; + + Log.Fatal("Startup failure details: {Details}", JsonSerializer.Serialize(errorDetails)); + + // 确保所有日志都被写入 + Log.CloseAndFlush(); + + // 使用非零退出码表示错误 + Environment.Exit(1); +} \ No newline at end of file diff --git a/CoreAgent.API/Properties/launchSettings.json b/CoreAgent.API/Properties/launchSettings.json new file mode 100644 index 0000000..dabb228 --- /dev/null +++ b/CoreAgent.API/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:3074", + "sslPort": 44378 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5228", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7133;http://localhost:5228", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/CoreAgent.API/Startup.cs b/CoreAgent.API/Startup.cs new file mode 100644 index 0000000..e41eee5 --- /dev/null +++ b/CoreAgent.API/Startup.cs @@ -0,0 +1,118 @@ +using CoreAgent.Infrastructure.Extensions; +using CoreAgent.Infrastructure.Logging; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; + +namespace CoreAgent.API +{ + public class Startup + { + public IConfiguration Configuration { get; } + public IHostEnvironment Environment { get; } + + public Startup(IConfiguration configuration, IHostEnvironment environment) + { + Configuration = configuration; + Environment = environment; + } + + public void ConfigureServices(IServiceCollection services) + { + + // 添加基础设施服务 + services.AddInfrastructure(Configuration); + + // 添加控制器 + services.AddControllers(); + + // 添加API文档 + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(); + } + + public void Configure(IApplicationBuilder app) + { + // 配置开发环境中间件 + if (Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + Log.Debug("Swagger UI enabled in development environment"); + } + + // 使用基础设施中间件 + app.UseInfrastructure(Configuration); + + // 配置控制器路由 + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } + + public static class StartupExtensions + { + public static WebApplicationBuilder AddConfigurations(this WebApplicationBuilder builder) + { + const string configurationsDirectory = "Configurations"; + var env = builder.Environment; + + // 基础配置 + builder.Configuration + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables(); + + // 基础设施配置 + builder.Configuration + .AddJsonFile($"{configurationsDirectory}/logger.json", optional: false, reloadOnChange: true) + .AddJsonFile($"{configurationsDirectory}/logger.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) + .AddJsonFile($"{configurationsDirectory}/request-logging.json", optional: false, reloadOnChange: true) + .AddJsonFile($"{configurationsDirectory}/request-logging.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) + .AddJsonFile($"{configurationsDirectory}/api-versioning.json", optional: false, reloadOnChange: true) + .AddJsonFile($"{configurationsDirectory}/api-versioning.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); + + return builder; + } + + public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration config) + { + return services + .AddLoggingServices(config) + .AddApiVersioningExtension(config) + .AddRequestLogging(config) + .AddAuthorization() + .AddSwaggerGen(); + } + + public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app, IConfiguration config) + { + return app + .UseHttpsRedirection() + .UseExceptionMiddleware() + .UseAuthorization() + .UseRequestLogging(config) + .UseApiVersioningExtension(); + } + + public static void ConfigureSwagger(this WebApplication app) + { + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + } + + // 新增:创建Startup实例的扩展方法 + public static Startup CreateStartup(this WebApplicationBuilder builder) + { + return new Startup(builder.Configuration, builder.Environment); + } + } +} \ No newline at end of file diff --git a/CoreAgent.API/appsettings.Development.json b/CoreAgent.API/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/CoreAgent.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/CoreAgent.API/appsettings.json b/CoreAgent.API/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/CoreAgent.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/CoreAgent.API/logs/log-20250610.txt b/CoreAgent.API/logs/log-20250610.txt new file mode 100644 index 0000000..2c49096 --- /dev/null +++ b/CoreAgent.API/logs/log-20250610.txt @@ -0,0 +1,98 @@ +2025-06-10 23:09:05.046 +08:00 [INF] Logger initialized successfully +2025-06-10 23:09:05.092 +08:00 [INF] Application starting... +2025-06-10 23:09:06.063 +08:00 [FTL] Application failed to start: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'.) +System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'.) + ---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'. + ---> System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'. + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, Int32 slot) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(ServiceDescriptor serviceDescriptor, CallSiteChain callSiteChain) + at Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) + --- End of inner exception stack trace --- + at Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) + at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) + --- End of inner exception stack trace --- + at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) + at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options) + at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build() + at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build() + at Program.
$(String[] args) in D:\HistoryCode\CoreAgent\src\CoreAgent.API\Program.cs:line 17 +2025-06-10 23:09:06.162 +08:00 [FTL] Startup failure details: {"Message":"Some services are not able to be constructed (Error while validating the service descriptor \u0027ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027: Unable to resolve service for type \u0027Microsoft.AspNetCore.Http.RequestDelegate\u0027 while attempting to activate \u0027CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027.)","StackTrace":" at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection\u00601 serviceDescriptors, ServiceProviderOptions options)\r\n at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)\r\n at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build()\r\n at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build()\r\n at Program.\u003CMain\u003E$(String[] args) in D:\\HistoryCode\\CoreAgent\\src\\CoreAgent.API\\Program.cs:line 17","Source":"Microsoft.Extensions.DependencyInjection","InnerException":"Error while validating the service descriptor \u0027ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027: Unable to resolve service for type \u0027Microsoft.AspNetCore.Http.RequestDelegate\u0027 while attempting to activate \u0027CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027."} +2025-06-10 23:09:40.611 +08:00 [INF] Logger initialized successfully +2025-06-10 23:09:40.653 +08:00 [INF] Application starting... +2025-06-10 23:09:40.752 +08:00 [FTL] Application failed to start: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'.) +System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'.) + ---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'. + ---> System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'. + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, Int32 slot) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(ServiceDescriptor serviceDescriptor, CallSiteChain callSiteChain) + at Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) + --- End of inner exception stack trace --- + at Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) + at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) + --- End of inner exception stack trace --- + at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) + at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options) + at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build() + at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build() + at Program.
$(String[] args) in D:\HistoryCode\CoreAgent\src\CoreAgent.API\Program.cs:line 17 +2025-06-10 23:09:40.820 +08:00 [FTL] Startup failure details: {"Message":"Some services are not able to be constructed (Error while validating the service descriptor \u0027ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027: Unable to resolve service for type \u0027Microsoft.AspNetCore.Http.RequestDelegate\u0027 while attempting to activate \u0027CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027.)","StackTrace":" at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection\u00601 serviceDescriptors, ServiceProviderOptions options)\r\n at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)\r\n at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build()\r\n at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build()\r\n at Program.\u003CMain\u003E$(String[] args) in D:\\HistoryCode\\CoreAgent\\src\\CoreAgent.API\\Program.cs:line 17","Source":"Microsoft.Extensions.DependencyInjection","InnerException":"Error while validating the service descriptor \u0027ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027: Unable to resolve service for type \u0027Microsoft.AspNetCore.Http.RequestDelegate\u0027 while attempting to activate \u0027CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027."} +2025-06-10 23:10:25.939 +08:00 [INF] Logger initialized successfully +2025-06-10 23:10:25.975 +08:00 [INF] Application starting... +2025-06-10 23:10:26.021 +08:00 [FTL] Application failed to start: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'.) +System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'.) + ---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware': Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'. + ---> System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Http.RequestDelegate' while attempting to activate 'CoreAgent.Infrastructure.Middleware.ExceptionMiddleware'. + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, Int32 slot) + at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(ServiceDescriptor serviceDescriptor, CallSiteChain callSiteChain) + at Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) + --- End of inner exception stack trace --- + at Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) + at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) + --- End of inner exception stack trace --- + at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options) + at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options) + at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build() + at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build() + at Program.
$(String[] args) in D:\HistoryCode\CoreAgent\src\CoreAgent.API\Program.cs:line 17 +2025-06-10 23:10:26.056 +08:00 [FTL] Startup failure details: {"Message":"Some services are not able to be constructed (Error while validating the service descriptor \u0027ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027: Unable to resolve service for type \u0027Microsoft.AspNetCore.Http.RequestDelegate\u0027 while attempting to activate \u0027CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027.)","StackTrace":" at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection\u00601 serviceDescriptors, ServiceProviderOptions options)\r\n at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)\r\n at Microsoft.Extensions.Hosting.HostApplicationBuilder.Build()\r\n at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build()\r\n at Program.\u003CMain\u003E$(String[] args) in D:\\HistoryCode\\CoreAgent\\src\\CoreAgent.API\\Program.cs:line 17","Source":"Microsoft.Extensions.DependencyInjection","InnerException":"Error while validating the service descriptor \u0027ServiceType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware Lifetime: Scoped ImplementationType: CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027: Unable to resolve service for type \u0027Microsoft.AspNetCore.Http.RequestDelegate\u0027 while attempting to activate \u0027CoreAgent.Infrastructure.Middleware.ExceptionMiddleware\u0027."} +2025-06-10 23:11:14.432 +08:00 [INF] Logger initialized successfully +2025-06-10 23:11:14.474 +08:00 [INF] Application starting... +2025-06-10 23:11:14.527 +08:00 [FTL] Application failed to start: Unable to find the required services. Please add all the required services by calling 'IServiceCollection.AddAuthorization' in the application startup code. +System.InvalidOperationException: Unable to find the required services. Please add all the required services by calling 'IServiceCollection.AddAuthorization' in the application startup code. + at Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.VerifyServicesRegistered(IApplicationBuilder app) + at Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization(IApplicationBuilder app) + at CoreAgent.API.Startup.UseInfrastructure(IApplicationBuilder app, IConfiguration config) in D:\HistoryCode\CoreAgent\src\CoreAgent.API\Startup.cs:line 45 + at Program.
$(String[] args) in D:\HistoryCode\CoreAgent\src\CoreAgent.API\Program.cs:line 28 +2025-06-10 23:11:14.601 +08:00 [FTL] Startup failure details: {"Message":"Unable to find the required services. Please add all the required services by calling \u0027IServiceCollection.AddAuthorization\u0027 in the application startup code.","StackTrace":" at Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.VerifyServicesRegistered(IApplicationBuilder app)\r\n at Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization(IApplicationBuilder app)\r\n at CoreAgent.API.Startup.UseInfrastructure(IApplicationBuilder app, IConfiguration config) in D:\\HistoryCode\\CoreAgent\\src\\CoreAgent.API\\Startup.cs:line 45\r\n at Program.\u003CMain\u003E$(String[] args) in D:\\HistoryCode\\CoreAgent\\src\\CoreAgent.API\\Program.cs:line 28","Source":"Microsoft.AspNetCore.Authorization.Policy","InnerException":null} +2025-06-10 23:14:51.039 +08:00 [INF] Logger initialized successfully +2025-06-10 23:14:51.069 +08:00 [INF] Application starting... +2025-06-10 23:14:51.124 +08:00 [FTL] Application failed to start: Unable to find the required services. Please add all the required services by calling 'IServiceCollection.AddAuthorization' in the application startup code. +System.InvalidOperationException: Unable to find the required services. Please add all the required services by calling 'IServiceCollection.AddAuthorization' in the application startup code. + at Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.VerifyServicesRegistered(IApplicationBuilder app) + at Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization(IApplicationBuilder app) + at CoreAgent.API.Startup.UseInfrastructure(IApplicationBuilder app, IConfiguration config) in D:\HistoryCode\CoreAgent\src\CoreAgent.API\Startup.cs:line 45 + at Program.
$(String[] args) in D:\HistoryCode\CoreAgent\src\CoreAgent.API\Program.cs:line 28 +2025-06-10 23:14:51.149 +08:00 [FTL] Startup failure details: {"Message":"Unable to find the required services. Please add all the required services by calling \u0027IServiceCollection.AddAuthorization\u0027 in the application startup code.","StackTrace":" at Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.VerifyServicesRegistered(IApplicationBuilder app)\r\n at Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization(IApplicationBuilder app)\r\n at CoreAgent.API.Startup.UseInfrastructure(IApplicationBuilder app, IConfiguration config) in D:\\HistoryCode\\CoreAgent\\src\\CoreAgent.API\\Startup.cs:line 45\r\n at Program.\u003CMain\u003E$(String[] args) in D:\\HistoryCode\\CoreAgent\\src\\CoreAgent.API\\Program.cs:line 28","Source":"Microsoft.AspNetCore.Authorization.Policy","InnerException":null} +2025-06-10 23:15:27.651 +08:00 [INF] Logger initialized successfully +2025-06-10 23:15:27.682 +08:00 [INF] Application starting... +2025-06-10 23:15:27.735 +08:00 [INF] Application startup completed +2025-06-10 23:23:44.793 +08:00 [INF] Logger initialized successfully +2025-06-10 23:23:44.837 +08:00 [INF] Application starting... +2025-06-10 23:23:45.207 +08:00 [INF] Application startup completed +2025-06-10 23:23:51.434 +08:00 [INF] Request started: GET /api/v1/WeatherForecast +2025-06-10 23:23:51.497 +08:00 [INF] Weather forecast requested +2025-06-10 23:23:51.530 +08:00 [INF] Response for GET /api/v1/WeatherForecast: 200 - [{"date":"2025-06-11","temperatureC":-19,"summary":"Scorching","temperatureF":-2},{"date":"2025-06-12","temperatureC":-11,"summary":"Bracing","temperatureF":13},{"date":"2025-06-13","temperatureC":10,"summary":"Chilly","temperatureF":49},{"date":"2025-06-14","temperatureC":7,"summary":"Mild","temperatureF":44},{"date":"2025-06-15","temperatureC":35,"summary":"Warm","temperatureF":94}] +2025-06-10 23:23:51.547 +08:00 [INF] Request completed: GET /api/v1/WeatherForecast in 119ms with status code 200 +2025-06-10 23:24:10.107 +08:00 [INF] Logger initialized successfully +2025-06-10 23:24:10.148 +08:00 [INF] Application starting... +2025-06-10 23:24:10.527 +08:00 [INF] Application startup completed +2025-06-10 23:24:23.522 +08:00 [INF] Request started: GET /api/v1/WeatherForecast +2025-06-10 23:24:23.584 +08:00 [INF] Weather forecast requested +2025-06-10 23:24:23.612 +08:00 [INF] Response for GET /api/v1/WeatherForecast: 200 - [{"date":"2025-06-11","temperatureC":24,"summary":"Warm","temperatureF":75},{"date":"2025-06-12","temperatureC":4,"summary":"Bracing","temperatureF":39},{"date":"2025-06-13","temperatureC":54,"summary":"Chilly","temperatureF":129},{"date":"2025-06-14","temperatureC":-6,"summary":"Freezing","temperatureF":22},{"date":"2025-06-15","temperatureC":-20,"summary":"Sweltering","temperatureF":-3}] +2025-06-10 23:24:23.632 +08:00 [INF] Request completed: GET /api/v1/WeatherForecast in 120ms with status code 200 diff --git a/CoreAgent.Application/CoreAgent.Application.csproj b/CoreAgent.Application/CoreAgent.Application.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/CoreAgent.Application/CoreAgent.Application.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/CoreAgent.Domain/CoreAgent.Domain.csproj b/CoreAgent.Domain/CoreAgent.Domain.csproj new file mode 100644 index 0000000..812f847 --- /dev/null +++ b/CoreAgent.Domain/CoreAgent.Domain.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/CoreAgent.Domain/Exceptions/NotFoundException.cs b/CoreAgent.Domain/Exceptions/NotFoundException.cs new file mode 100644 index 0000000..f3e9b67 --- /dev/null +++ b/CoreAgent.Domain/Exceptions/NotFoundException.cs @@ -0,0 +1,14 @@ +namespace CoreAgent.Domain.Exceptions +{ + public class NotFoundException : Exception + { + public NotFoundException(string message) : base(message) + { + } + + public NotFoundException(string name, object key) + : base($"Entity \"{name}\" ({key}) was not found.") + { + } + } +} \ No newline at end of file diff --git a/CoreAgent.Domain/Exceptions/ValidationException.cs b/CoreAgent.Domain/Exceptions/ValidationException.cs new file mode 100644 index 0000000..0c8c039 --- /dev/null +++ b/CoreAgent.Domain/Exceptions/ValidationException.cs @@ -0,0 +1,17 @@ +namespace CoreAgent.Domain.Exceptions +{ + public class ValidationException : Exception + { + public IDictionary Errors { get; } + + public ValidationException(string message) : base(message) + { + Errors = new Dictionary(); + } + + public ValidationException(string message, IDictionary errors) : base(message) + { + Errors = errors; + } + } +} \ No newline at end of file diff --git a/CoreAgent.Domain/Models/ErrorResponse.cs b/CoreAgent.Domain/Models/ErrorResponse.cs new file mode 100644 index 0000000..6700242 --- /dev/null +++ b/CoreAgent.Domain/Models/ErrorResponse.cs @@ -0,0 +1,23 @@ +namespace CoreAgent.Domain.Models +{ + /// + /// ʾAPIӦģ + /// + public class ErrorResponse + { + /// + /// Ψһٱʶ + /// + public string TraceId { get; set; } + + /// + /// Ϣ + /// + public string Message { get; set; } + + /// + /// ֤󼯺ϣΪֵֶΪϢ + /// + public IDictionary Errors { get; set; } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj b/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj new file mode 100644 index 0000000..5090aed --- /dev/null +++ b/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + diff --git a/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp b/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp new file mode 100644 index 0000000..5a8ebfc --- /dev/null +++ b/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs b/CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs new file mode 100644 index 0000000..1d93824 --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs @@ -0,0 +1,44 @@ +using CoreAgent.Infrastructure.Options; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace CoreAgent.Infrastructure.Extensions +{ + public static class ApiVersioningExtensions + { + public static IServiceCollection AddApiVersioningExtension(this IServiceCollection services, IConfiguration config) + { + var settings = config.GetSection("ApiVersioning").Get() ?? new ApiVersioningSettings(); + + services.AddApiVersioning(options => + { + options.DefaultApiVersion = ApiVersion.Parse(settings.DefaultVersion); + options.AssumeDefaultVersionWhenUnspecified = settings.AssumeDefaultVersionWhenUnspecified; + options.ReportApiVersions = settings.ReportApiVersions; + options.ApiVersionReader = ApiVersionReader.Combine( + settings.ShowVersionInUrl ? new UrlSegmentApiVersionReader() : null, + settings.ShowVersionInQueryString ? new QueryStringApiVersionReader("api-version") : null, + settings.ShowVersionInHeader ? new HeaderApiVersionReader("api-version") : null, + settings.ShowVersionInMediaType ? new MediaTypeApiVersionReader("version") : null + ); + }); + + services.AddVersionedApiExplorer(options => + { + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + return services; + } + + public static IApplicationBuilder UseApiVersioningExtension(this IApplicationBuilder app) + { + // 可以在这里添加版本控制相关的中间件 + return app; + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs b/CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs new file mode 100644 index 0000000..0e8ff5e --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs @@ -0,0 +1,13 @@ +using CoreAgent.Infrastructure.Middleware; +using Microsoft.AspNetCore.Builder; + +namespace CoreAgent.Infrastructure.Extensions +{ + public static class ExceptionMiddlewareExtensions + { + public static IApplicationBuilder UseExceptionMiddleware(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs b/CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs new file mode 100644 index 0000000..bd3dd64 --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs @@ -0,0 +1,27 @@ +using CoreAgent.Infrastructure.Logging; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Serilog; + +namespace CoreAgent.Infrastructure.Extensions +{ + public static class LoggingExtensions + { + public static IServiceCollection AddLoggingServices(this IServiceCollection services, IConfiguration configuration) + { + // 初始化应用程序日志记录器 + ApplicationLogger.Initialize(configuration); + Log.Information("Application starting..."); + + // 添加 Serilog 到依赖注入容器 + services.AddLogging(loggingBuilder => + { + loggingBuilder.ClearProviders(); + loggingBuilder.AddSerilog(dispose: true); + }); + + return services; + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs b/CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs new file mode 100644 index 0000000..335d4c4 --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs @@ -0,0 +1,24 @@ +using CoreAgent.Infrastructure.Middleware; +using CoreAgent.Infrastructure.Options; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace CoreAgent.Infrastructure.Extensions +{ + public static class RequestLoggingExtensions + { + public static IServiceCollection AddRequestLogging(this IServiceCollection services, IConfiguration config) + { + services.Configure(config.GetSection("RequestLogging")); + return services; + } + + public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app, IConfiguration config) + { + app.UseMiddleware(); + app.UseMiddleware(); + return app; + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Logging/ApplicationLogger.cs b/CoreAgent.Infrastructure/Logging/ApplicationLogger.cs new file mode 100644 index 0000000..4205762 --- /dev/null +++ b/CoreAgent.Infrastructure/Logging/ApplicationLogger.cs @@ -0,0 +1,65 @@ +using Microsoft.Extensions.Configuration; +using Serilog; +using System; +using System.IO; + +namespace CoreAgent.Infrastructure.Logging +{ + public static class ApplicationLogger + { + private static readonly object _lock = new object(); + private static bool _isInitialized; + + public static void Initialize(IConfiguration configuration) + { + ArgumentNullException.ThrowIfNull(configuration, nameof(configuration)); + + if (_isInitialized) + { + return; + } + + lock (_lock) + { + if (_isInitialized) + { + return; + } + + try + { + // 确保日志目录存在 + var logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"); + Directory.CreateDirectory(logDirectory); + + // 从配置文件初始化 + Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .CreateLogger(); + + _isInitialized = true; + Log.Information("Logger initialized successfully"); + } + catch (Exception ex) + { + // 如果初始化失败,创建一个基本的控制台日志记录器 + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .CreateLogger(); + + _isInitialized = true; + Log.Error(ex, "Failed to initialize logger with full configuration"); + } + } + } + + public static void EnsureInitialized() + { + if (!_isInitialized) + { + throw new InvalidOperationException("Logger has not been initialized. Call Initialize() first."); + } + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Middleware/ExceptionMiddleware.cs b/CoreAgent.Infrastructure/Middleware/ExceptionMiddleware.cs new file mode 100644 index 0000000..c858abf --- /dev/null +++ b/CoreAgent.Infrastructure/Middleware/ExceptionMiddleware.cs @@ -0,0 +1,75 @@ +using CoreAgent.Domain.Exceptions; +using CoreAgent.Domain.Models; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System.Net; +using System.Text.Json; + +namespace CoreAgent.Infrastructure.Middleware +{ + public class ExceptionMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public ExceptionMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + private async Task HandleExceptionAsync(HttpContext context, Exception exception) + { + var response = context.Response; + response.ContentType = "application/json"; + + var errorResponse = new ErrorResponse + { + TraceId = context.TraceIdentifier, + Message = "An error occurred while processing your request." + }; + + switch (exception) + { + case ValidationException validationEx: + response.StatusCode = (int)HttpStatusCode.BadRequest; + errorResponse.Message = validationEx.Message; + errorResponse.Errors = validationEx.Errors; + _logger.LogWarning("Validation error: {Message}", validationEx.Message); + break; + + case NotFoundException notFoundEx: + response.StatusCode = (int)HttpStatusCode.NotFound; + errorResponse.Message = notFoundEx.Message; + _logger.LogWarning("Resource not found: {Message}", notFoundEx.Message); + break; + + case UnauthorizedAccessException: + response.StatusCode = (int)HttpStatusCode.Unauthorized; + errorResponse.Message = "You are not authorized to access this resource."; + _logger.LogWarning("Unauthorized access attempt"); + break; + + default: + response.StatusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(exception, "Unhandled exception occurred"); + break; + } + + var result = JsonSerializer.Serialize(errorResponse); + await response.WriteAsync(result); + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs b/CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs new file mode 100644 index 0000000..54f8328 --- /dev/null +++ b/CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs @@ -0,0 +1,110 @@ +using CoreAgent.Infrastructure.Options; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System.Diagnostics; +using System.Text; + +namespace CoreAgent.Infrastructure.Middleware +{ + public class RequestLoggingMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly RequestLoggingOptions _options; + + public RequestLoggingMiddleware( + RequestDelegate next, + ILogger logger, + IOptions options) + { + _next = next; + _logger = logger; + _options = options.Value; + } + + public async Task InvokeAsync(HttpContext context) + { + if (ShouldSkipLogging(context)) + { + await _next(context); + return; + } + + var sw = Stopwatch.StartNew(); + var requestBody = string.Empty; + + try + { + if (_options.LogRequestBody && context.Request.Body != null) + { + requestBody = await ReadRequestBodyAsync(context.Request); + } + + _logger.LogInformation( + "Request started: {Method} {Path} {QueryString} {RequestBody}", + context.Request.Method, + context.Request.Path, + context.Request.QueryString, + requestBody); + + await _next(context); + } + finally + { + sw.Stop(); + _logger.LogInformation( + "Request completed: {Method} {Path} in {ElapsedMilliseconds}ms with status code {StatusCode}", + context.Request.Method, + context.Request.Path, + sw.ElapsedMilliseconds, + context.Response.StatusCode); + } + } + + private bool ShouldSkipLogging(HttpContext context) + { + if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) + { + return true; + } + + if (_options.ExcludeMethods.Contains(context.Request.Method)) + { + return true; + } + + return false; + } + + private async Task ReadRequestBodyAsync(HttpRequest request) + { + if (!request.Body.CanRead || !request.Body.CanSeek) + { + return string.Empty; + } + + var originalPosition = request.Body.Position; + request.Body.Position = 0; + + try + { + using var reader = new StreamReader( + request.Body, + encoding: Encoding.UTF8, + detectEncodingFromByteOrderMarks: false, + bufferSize: 1024 * 45, + leaveOpen: true); + + var body = await reader.ReadToEndAsync(); + return body.Length > _options.MaxRequestBodySize + ? body.Substring(0, _options.MaxRequestBodySize) + "..." + : body; + } + finally + { + request.Body.Position = originalPosition; + } + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs b/CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs new file mode 100644 index 0000000..196c2e0 --- /dev/null +++ b/CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs @@ -0,0 +1,103 @@ +using CoreAgent.Infrastructure.Options; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System.Text; + +namespace CoreAgent.Infrastructure.Middleware +{ + public class ResponseLoggingMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly RequestLoggingOptions _options; + + public ResponseLoggingMiddleware( + RequestDelegate next, + ILogger logger, + IOptions options) + { + _next = next; + _logger = logger; + _options = options.Value; + } + + public async Task InvokeAsync(HttpContext context) + { + if (ShouldSkipLogging(context)) + { + await _next(context); + return; + } + + // 保存原始响应流 + var originalBodyStream = context.Response.Body; + + try + { + // 创建一个新的内存流来捕获响应 + using var responseBody = new MemoryStream(); + context.Response.Body = responseBody; + + // 继续处理请求 + await _next(context); + + // 读取响应内容 + responseBody.Seek(0, SeekOrigin.Begin); + var response = await ReadResponseBodyAsync(responseBody); + + // 记录响应 + _logger.LogInformation( + "Response for {Method} {Path}: {StatusCode} - {Response}", + context.Request.Method, + context.Request.Path, + context.Response.StatusCode, + response); + + // 将响应写回原始流 + responseBody.Seek(0, SeekOrigin.Begin); + await responseBody.CopyToAsync(originalBodyStream); + } + finally + { + // 确保响应体被恢复 + context.Response.Body = originalBodyStream; + } + } + + private bool ShouldSkipLogging(HttpContext context) + { + if (!_options.LogResponseBody) + { + return true; + } + + if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) + { + return true; + } + + if (_options.ExcludeMethods.Contains(context.Request.Method)) + { + return true; + } + + return false; + } + + private async Task ReadResponseBodyAsync(Stream responseBody) + { + using var reader = new StreamReader( + responseBody, + encoding: Encoding.UTF8, + detectEncodingFromByteOrderMarks: false, + bufferSize: 1024 * 45, + leaveOpen: true); + + var body = await reader.ReadToEndAsync(); + return body.Length > _options.MaxResponseBodySize + ? body.Substring(0, _options.MaxResponseBodySize) + "..." + : body; + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Options/ApiVersioningSettings.cs b/CoreAgent.Infrastructure/Options/ApiVersioningSettings.cs new file mode 100644 index 0000000..e8fbea0 --- /dev/null +++ b/CoreAgent.Infrastructure/Options/ApiVersioningSettings.cs @@ -0,0 +1,45 @@ +namespace CoreAgent.Infrastructure.Options +{ + public class ApiVersioningSettings + { + /// + /// 默认 API 版本 + /// + public string DefaultVersion { get; set; } = "1.0"; + + /// + /// 是否在响应头中显示版本信息 + /// + public bool ShowVersionInHeader { get; set; } = true; + + /// + /// 是否在响应中显示版本信息 + /// + public bool ShowVersionInResponse { get; set; } = true; + + /// + /// 是否在 URL 中显示版本信息 + /// + public bool ShowVersionInUrl { get; set; } = true; + + /// + /// 是否在查询字符串中显示版本信息 + /// + public bool ShowVersionInQueryString { get; set; } = true; + + /// + /// 是否在媒体类型中显示版本信息 + /// + public bool ShowVersionInMediaType { get; set; } = true; + + /// + /// 是否报告 API 版本 + /// + public bool ReportApiVersions { get; set; } = true; + + /// + /// 是否假设默认版本 + /// + public bool AssumeDefaultVersionWhenUnspecified { get; set; } = true; + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Options/RequestLoggingOptions.cs b/CoreAgent.Infrastructure/Options/RequestLoggingOptions.cs new file mode 100644 index 0000000..08462c6 --- /dev/null +++ b/CoreAgent.Infrastructure/Options/RequestLoggingOptions.cs @@ -0,0 +1,35 @@ +namespace CoreAgent.Infrastructure.Options +{ + public class RequestLoggingOptions + { + /// + /// 是否记录请求体 + /// + public bool LogRequestBody { get; set; } = true; + + /// + /// 是否记录响应体 + /// + public bool LogResponseBody { get; set; } = true; + + /// + /// 最大请求体大小(字节) + /// + public int MaxRequestBodySize { get; set; } = 1024 * 1024; // 1MB + + /// + /// 最大响应体大小(字节) + /// + public int MaxResponseBodySize { get; set; } = 1024 * 1024; // 1MB + + /// + /// 要排除的路径列表 + /// + public string[] ExcludePaths { get; set; } = Array.Empty(); + + /// + /// 要排除的HTTP方法列表 + /// + public string[] ExcludeMethods { get; set; } = Array.Empty(); + } +} \ No newline at end of file diff --git a/CoreAgent.sln b/CoreAgent.sln new file mode 100644 index 0000000..aa995c0 --- /dev/null +++ b/CoreAgent.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreAgent.API", "CoreAgent.API\CoreAgent.API.csproj", "{DC3259F5-9A7E-1AEA-FA79-13C78AA9FDAC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreAgent.Application", "CoreAgent.Application\CoreAgent.Application.csproj", "{D74FF074-0A13-BF0E-A637-41E48C0FB9AB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreAgent.Domain", "CoreAgent.Domain\CoreAgent.Domain.csproj", "{3269E8BF-134E-9410-E1DA-A62044875E72}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreAgent.Infrastructure", "CoreAgent.Infrastructure\CoreAgent.Infrastructure.csproj", "{3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DC3259F5-9A7E-1AEA-FA79-13C78AA9FDAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC3259F5-9A7E-1AEA-FA79-13C78AA9FDAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC3259F5-9A7E-1AEA-FA79-13C78AA9FDAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC3259F5-9A7E-1AEA-FA79-13C78AA9FDAC}.Release|Any CPU.Build.0 = Release|Any CPU + {D74FF074-0A13-BF0E-A637-41E48C0FB9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D74FF074-0A13-BF0E-A637-41E48C0FB9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D74FF074-0A13-BF0E-A637-41E48C0FB9AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D74FF074-0A13-BF0E-A637-41E48C0FB9AB}.Release|Any CPU.Build.0 = Release|Any CPU + {3269E8BF-134E-9410-E1DA-A62044875E72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3269E8BF-134E-9410-E1DA-A62044875E72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3269E8BF-134E-9410-E1DA-A62044875E72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3269E8BF-134E-9410-E1DA-A62044875E72}.Release|Any CPU.Build.0 = Release|Any CPU + {3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal