using System.Text.Json.Serialization;
using System.Threading.RateLimiting;
using AgileConfig.Client;
using Dpz.Core.Hangfire;
using Dpz.Core.Infrastructure.Configuration;
using Dpz.Core.WebApi.Security;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.OpenApi;
using Scalar.AspNetCore;
const string originsName = "Open";
Log.Logger = new LoggerConfiguration().Enrich.FromLogContext().CreateBootstrapLogger();
try
{
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseAgileConfig(new ConfigClient(builder.Configuration));
// 开发环境下重新加载本地配置,覆盖配置中心的特定节点
if (builder.Environment.IsDevelopment())
{
builder.Configuration.AddJsonFile(
"appsettings.Development.json",
optional: true,
reloadOnChange: true
);
}
var configuration = builder.Configuration;
var seq = configuration.GetSection("LogSeq").Get<LogSeq>();
builder.Host.ConfigurationLog(seq);
#region services
var services = builder.Services;
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownIPNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter(
"comment",
opt =>
{
opt.AutoReplenishment = true;
opt.PermitLimit = 3;
opt.Window = TimeSpan.FromMinutes(1);
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 2;
}
);
});
services
.AddControllers()
// .AddXmlDataContractSerializerFormatters()
//.AddXmlSerializerFormatters()
.AddMessagePackSerializerFormatters()
.AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.Converters.Add(new TimeSpanConverter());
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
services.AddResponseCompression(options =>
{
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat([
"application/font-woff2",
"image/svg+xml",
"text/plain",
"application/lrc",
]);
});
services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Optimal;
});
services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Optimal;
});
services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = int.MaxValue;
});
services.AddEndpointsApiExplorer();
services.AddOutputCache(options =>
{
options.AddBasePolicy(policy => policy.Expire(TimeSpan.FromMinutes(10)));
});
services.AddOpenApi(options =>
{
options.AddDocumentTransformer(
async (doc, _, cancelToken) =>
{
var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
var description = "";
if (!string.IsNullOrEmpty(basePath))
{
var readmePath = Path.Combine(basePath, "README.md");
if (File.Exists(readmePath))
{
description = await File.ReadAllTextAsync(readmePath, cancelToken);
}
}
doc.Info.Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString();
doc.Info.Title = "Dpz.Core.WebApi | v" + doc.Info.Version;
doc.Info.Description = description;
doc.Components ??= new OpenApiComponents();
doc.Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>();
doc.Components.SecuritySchemes.TryAdd(
"Bearer",
new OpenApiSecurityScheme
{
Description = "使用Bearer方案的JWT授权标头(示例:“ Bearer JWT_Token”)",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.OAuth2,
Scheme = "Bearer",
BearerFormat = "JWT",
}
);
doc.Security ??= new List<OpenApiSecurityRequirement>();
doc.Security.Add(
new OpenApiSecurityRequirement
{
{
new OpenApiSecuritySchemeReference("Bearer")
{
Reference = new OpenApiReferenceWithDescription
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer",
},
},
[]
},
}
);
}
);
});
// IoC
services.AddBusinessServices(configuration);
services
.AddDefaultServices(configuration)
.AddProjectServices()
.AddMessageQueueServices(configuration);
// Cors
services.AddCors(options =>
{
options.AddPolicy(
originsName,
cfg =>
{
var origins = configuration.GetSection("Origins").Get<string[]>() ?? [];
cfg.WithOrigins(origins)
.WithMethods("GET", "PUT", "POST", "DELETE", "PATCH")
.AllowAnyHeader();
}
);
});
var issuer = configuration["Server:Issuer"] ?? throw new InvalidConfigurationException();
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = issuer;
options.Audience = "resource_server";
options.RequireHttpsMetadata = true;
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
Log.Error(context.Exception, "Failed to authenticate");
return Task.CompletedTask;
},
};
});
services.AddPermissionAuthorization();
services.AddRazorPages();
var webApiHangfireCollectionPrefix =
configuration["WebApiHangfireCollectionPrefix"]
?? throw new InvalidConfigurationException();
services.AddHangfireService(configuration, webApiHangfireCollectionPrefix);
#endregion
var app = builder.Build();
#region configuration
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseForwardedHeaders();
app.UseSerilogRequestLogging(options =>
{
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
diagnosticContext.Set(
"UserAgent",
httpContext.Request.Headers["User-Agent"].ToString()
);
diagnosticContext.Set("IpAddress", httpContext.Request.GetIpAddress());
};
});
app.UseResponseHeaders().UseRequestRecord().UseDateTimeLocalFormat();
app.UseResponseCompression();
app.MapOpenApi().CacheOutput();
app.MapScalarApiReference(options =>
{
options.Title = "Dpz.Core.WebApi";
options.Favicon = "https://dpangzi.com/favicon.ico";
options.Theme = ScalarTheme.Saturn;
options.HideDarkModeToggle = false;
options.HideClientButton = true;
});
app.GetHostingEnvironment();
// //强制HTTPS
// app.UseHttpsRedirection();
app.UseRouting();
app.MapRazorPages();
app.UseCors(originsName);
//身份认证
app.UseAuthentication();
//身份授权
app.UseAuthorization();
app.MapControllers();
//app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
#if DEBUG
app.HangfireSetting();
#endif
#endregion
app.MapGet("/ping", () => "pong");
app.MapGet(
"/datetime",
() => $"{DateTime.Now:yyyy/Mm/dd HH:mm:ss}\r\n{Environment.MachineName}"
);
Log.Information("Starting web api host,current process id:{ID}", Environment.ProcessId);
app.Run();
}
catch (Exception e)
{
Console.Error.WriteLine(e);
Log.Fatal(e, "Host terminated unexpectedly");
}
/// <summary>
///
/// </summary>
public partial class Program
{
/// <summary>
/// 身份认证Cookie名称
/// </summary>
public const string AuthorizeCookieName = "Dpz.Web.Api.Server.Authoriza";
/// <summary>
/// 账号Cookie名称
/// </summary>
public const string AccountCookieName = "Dpz.Web.Api.Server.Account";
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个基于ASP.NET Core的Web API应用程序的启动配置代码。让我为你详细解释各个部分:
主要功能概述
这是一个企业级的Web API项目,集成了多种中间件和服务,包括配置中心、日志记录、身份认证、限流、缓存等功能。
代码结构分析
1. 初始化和配置
const string originsName = "Open";
Log.Logger = new LoggerConfiguration().Enrich.FromLogContext().CreateBootstrapLogger();
- 定义CORS策略名称
- 初始化Serilog日志记录器
2. 配置中心集成
builder.Host.UseAgileConfig(new ConfigClient(builder.Configuration));
- 使用AgileConfig作为配置中心,实现配置的集中管理
3. 开发环境特殊处理
if (builder.Environment.IsDevelopment())
{
builder.Configuration.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true);
}
- 开发环境下重新加载本地配置文件,可覆盖配置中心的设置
4. 核心服务配置
反向代理支持
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownIPNetworks.Clear();
options.KnownProxies.Clear();
});
限流服务
services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("comment", opt =>
{
opt.AutoReplenishment = true;
opt.PermitLimit = 3;
opt.Window = TimeSpan.FromMinutes(1);
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 2;
});
});
- 配置名为"comment"的固定窗口限流器
- 每分钟最多3个请求,队列最多2个请求
控制器和序列化
services.AddControllers()
.AddMessagePackSerializerFormatters()
.AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.Converters.Add(new TimeSpanConverter());
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
- 添加MessagePack序列化支持
- 配置JSON序列化选项,支持TimeSpan和枚举转换
响应压缩
services.AddResponseCompression(options =>
{
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat([
"application/font-woff2",
"image/svg+xml",
"text/plain",
"application/lrc",
]);
});
- 支持Brotli和Gzip压缩
- 扩展了默认的MIME类型支持
OpenAPI文档
services.AddOpenApi(options =>
{
options.AddDocumentTransformer(async (doc, _, cancelToken) =>
{
// 读取README.md作为API描述
// 配置JWT Bearer认证
// 设置版本信息
});
});
- 自动生成API文档
- 集成JWT Bearer认证方案
- 从README.md读取API描述
身份认证和授权
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = issuer;
options.Audience = "resource_server";
options.RequireHttpsMetadata = true;
});
- 使用JWT Bearer认证
- 配置OAuth2/OpenID Connect
CORS配置
services.AddCors(options =>
{
options.AddPolicy(originsName, cfg =>
{
var origins = configuration.GetSection("Origins").Get<string[]>() ?? [];
cfg.WithOrigins(origins)
.WithMethods("GET", "PUT", "POST", "DELETE", "PATCH")
.AllowAnyHeader();
});
});
- 从配置中读取允许的源地址
- 支持常见的HTTP方法
5. 中间件管道配置
按顺序配置了以下中间件:
- 开发环境异常页面
- 转发头处理
- Serilog请求日志记录
- 响应头、请求记录、日期时间格式化
- 响应压缩
- OpenAPI和Scalar文档
- 路由
- CORS
- 身份认证和授权
- 控制器映射
6. 简单的健康检查端点
app.MapGet("/ping", () => "pong");
app.MapGet("/datetime", () => $"{DateTime.Now:yyyy/Mm/dd HH:mm:ss}\r\n{Environment.MachineName}");
7. 常量定义
public partial class Program
{
public const string AuthorizeCookieName = "Dpz.Web.Api.Server.Authoriza";
public const string AccountCookieName = "Dpz.Web.Api.Server.Account";
}
- 定义了认证和账户相关的Cookie名称
总结
这是一个功能完整的企业级Web API项目模板,具备:
- 配置中心集成
- 结构化日志记录
- API文档自动生成
- 身份认证授权
- 请求限流
- 响应压缩
- CORS支持
- 缓存机制
- 消息队列集成(Hangfire)
代码结构清晰,遵循了ASP.NET Core的最佳实践。
评论加载中...