commit
acdc447bb4
32 changed files with 1239 additions and 0 deletions
@ -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 |
@ -0,0 +1,12 @@ |
|||
{ |
|||
"ApiVersioning": { |
|||
"DefaultVersion": "1.0", |
|||
"ShowVersionInHeader": true, |
|||
"ShowVersionInResponse": true, |
|||
"ShowVersionInUrl": true, |
|||
"ShowVersionInQueryString": true, |
|||
"ShowVersionInMediaType": true, |
|||
"ReportApiVersions": true, |
|||
"AssumeDefaultVersionWhenUnspecified": true |
|||
} |
|||
} |
@ -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" ] |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
{ |
|||
"RequestLogging": { |
|||
"LogRequestBody": true, |
|||
"LogResponseBody": true, |
|||
"MaxRequestBodySize": 2097152, |
|||
"MaxResponseBodySize": 2097152, |
|||
"ExcludePaths": [ |
|||
"/health", |
|||
"/metrics", |
|||
"/swagger", |
|||
"/favicon.ico" |
|||
], |
|||
"ExcludeMethods": [ |
|||
"OPTIONS" |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
{ |
|||
"RequestLogging": { |
|||
"LogRequestBody": true, |
|||
"LogResponseBody": true, |
|||
"MaxRequestBodySize": 1048576, |
|||
"MaxResponseBodySize": 1048576, |
|||
"ExcludePaths": [ |
|||
"/health", |
|||
"/metrics", |
|||
"/swagger", |
|||
"/favicon.ico" |
|||
], |
|||
"ExcludeMethods": [ |
|||
"OPTIONS" |
|||
] |
|||
} |
|||
} |
@ -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<WeatherForecastController> _logger; |
|||
private static readonly string[] Summaries = new[] |
|||
{ |
|||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" |
|||
}; |
|||
|
|||
public WeatherForecastController(ILogger<WeatherForecastController> 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); |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<InvariantGlobalization>true</InvariantGlobalization> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" /> |
|||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" /> |
|||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\CoreAgent.Infrastructure\CoreAgent.Infrastructure.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Update="Configurations\*.json"> |
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|||
</None> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,6 @@ |
|||
@CoreAgent.API_HostAddress = http://localhost:5228 |
|||
|
|||
GET {{CoreAgent.API_HostAddress}}/weatherforecast/ |
|||
Accept: application/json |
|||
|
|||
### |
@ -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); |
|||
} |
@ -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" |
|||
} |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,8 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft.AspNetCore": "Warning" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,9 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft.AspNetCore": "Warning" |
|||
} |
|||
}, |
|||
"AllowedHosts": "*" |
|||
} |
@ -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.<Main>$(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.<Main>$(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.<Main>$(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.<Main>$(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.<Main>$(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 |
@ -0,0 +1,9 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,13 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
@ -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.") |
|||
{ |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
namespace CoreAgent.Domain.Exceptions |
|||
{ |
|||
public class ValidationException : Exception |
|||
{ |
|||
public IDictionary<string, string[]> Errors { get; } |
|||
|
|||
public ValidationException(string message) : base(message) |
|||
{ |
|||
Errors = new Dictionary<string, string[]>(); |
|||
} |
|||
|
|||
public ValidationException(string message, IDictionary<string, string[]> errors) : base(message) |
|||
{ |
|||
Errors = errors; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
namespace CoreAgent.Domain.Models |
|||
{ |
|||
/// <summary>
|
|||
/// 表示API错误响应的领域模型
|
|||
/// </summary>
|
|||
public class ErrorResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 请求的唯一跟踪标识符
|
|||
/// </summary>
|
|||
public string TraceId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 错误消息
|
|||
/// </summary>
|
|||
public string Message { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 验证错误集合,键为字段名,值为错误消息数组
|
|||
/// </summary>
|
|||
public IDictionary<string, string[]> Errors { get; set; } |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" /> |
|||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" /> |
|||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" /> |
|||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> |
|||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> |
|||
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0" /> |
|||
<PackageReference Include="Serilog" Version="3.1.1" /> |
|||
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" /> |
|||
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" /> |
|||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" /> |
|||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\CoreAgent.Domain\CoreAgent.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
@ -0,0 +1,13 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Serilog" Version="4.3.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
@ -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<ApiVersioningSettings>() ?? 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; |
|||
} |
|||
} |
|||
} |
@ -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<ExceptionMiddleware>(); |
|||
} |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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<RequestLoggingOptions>(config.GetSection("RequestLogging")); |
|||
return services; |
|||
} |
|||
|
|||
public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app, IConfiguration config) |
|||
{ |
|||
app.UseMiddleware<RequestLoggingMiddleware>(); |
|||
app.UseMiddleware<ResponseLoggingMiddleware>(); |
|||
return app; |
|||
} |
|||
} |
|||
} |
@ -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."); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -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<ExceptionMiddleware> _logger; |
|||
|
|||
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> 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); |
|||
} |
|||
} |
|||
} |
@ -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<RequestLoggingMiddleware> _logger; |
|||
private readonly RequestLoggingOptions _options; |
|||
|
|||
public RequestLoggingMiddleware( |
|||
RequestDelegate next, |
|||
ILogger<RequestLoggingMiddleware> logger, |
|||
IOptions<RequestLoggingOptions> 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<string> 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; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -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<ResponseLoggingMiddleware> _logger; |
|||
private readonly RequestLoggingOptions _options; |
|||
|
|||
public ResponseLoggingMiddleware( |
|||
RequestDelegate next, |
|||
ILogger<ResponseLoggingMiddleware> logger, |
|||
IOptions<RequestLoggingOptions> 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<string> 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; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
namespace CoreAgent.Infrastructure.Options |
|||
{ |
|||
public class ApiVersioningSettings |
|||
{ |
|||
/// <summary>
|
|||
/// 默认 API 版本
|
|||
/// </summary>
|
|||
public string DefaultVersion { get; set; } = "1.0"; |
|||
|
|||
/// <summary>
|
|||
/// 是否在响应头中显示版本信息
|
|||
/// </summary>
|
|||
public bool ShowVersionInHeader { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// 是否在响应中显示版本信息
|
|||
/// </summary>
|
|||
public bool ShowVersionInResponse { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// 是否在 URL 中显示版本信息
|
|||
/// </summary>
|
|||
public bool ShowVersionInUrl { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// 是否在查询字符串中显示版本信息
|
|||
/// </summary>
|
|||
public bool ShowVersionInQueryString { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// 是否在媒体类型中显示版本信息
|
|||
/// </summary>
|
|||
public bool ShowVersionInMediaType { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// 是否报告 API 版本
|
|||
/// </summary>
|
|||
public bool ReportApiVersions { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// 是否假设默认版本
|
|||
/// </summary>
|
|||
public bool AssumeDefaultVersionWhenUnspecified { get; set; } = true; |
|||
} |
|||
} |
@ -0,0 +1,35 @@ |
|||
namespace CoreAgent.Infrastructure.Options |
|||
{ |
|||
public class RequestLoggingOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 是否记录请求体
|
|||
/// </summary>
|
|||
public bool LogRequestBody { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// 是否记录响应体
|
|||
/// </summary>
|
|||
public bool LogResponseBody { get; set; } = true; |
|||
|
|||
/// <summary>
|
|||
/// 最大请求体大小(字节)
|
|||
/// </summary>
|
|||
public int MaxRequestBodySize { get; set; } = 1024 * 1024; // 1MB
|
|||
|
|||
/// <summary>
|
|||
/// 最大响应体大小(字节)
|
|||
/// </summary>
|
|||
public int MaxResponseBodySize { get; set; } = 1024 * 1024; // 1MB
|
|||
|
|||
/// <summary>
|
|||
/// 要排除的路径列表
|
|||
/// </summary>
|
|||
public string[] ExcludePaths { get; set; } = Array.Empty<string>(); |
|||
|
|||
/// <summary>
|
|||
/// 要排除的HTTP方法列表
|
|||
/// </summary>
|
|||
public string[] ExcludeMethods { get; set; } = Array.Empty<string>(); |
|||
} |
|||
} |
@ -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 |
Loading…
Reference in new issue