using System.Threading.RateLimiting;
using Dpz.Core.Infrastructure.RateLimiting;
using Dpz.Core.MessageQueue.Extensions;
using Dpz.Core.Public.ViewModel.Messages;
using Dpz.Core.Web.EventHandlers;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.Extensions.Primitives;
using OpenIddict.Client.AspNetCore;
using Serilog.Events;
using StackExchange.Redis;
const string corsScheme = "Open-Dpz-Core";
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;
LibraryHost =
configuration["LibraryHost"]
?? throw new InvalidConfigurationException(
"configuration node LibraryHost is null or empty"
);
AssetsHost =
configuration["AssetsHost"]
?? throw new InvalidConfigurationException(
"configuration node AssetsHost is null or empty"
);
var logSeq = configuration.GetSection("LogSeq").Get<LogSeq>();
// ReSharper disable once RedundantAssignment
Func<LogEvent, bool>? filter = null;
#if DEBUG
filter = x =>
{
if (
!x.Properties.TryGetValue("RequestPath", out var requestPath)
|| requestPath is not ScalarValue pathValue
)
{
return false;
}
var pathPrefixes = new[] { "/js", "/css" };
var path = pathValue.Value?.ToString();
return path != null
&& pathPrefixes.Any(p => path.StartsWith(p, StringComparison.OrdinalIgnoreCase));
};
#endif
builder.Host.ConfigurationLog(logSeq, filter);
builder
.WebHost.UseKestrel(
(_, option) =>
{
option.AddServerHeader = false;
option.ConfigureHttpsDefaults(x =>
x.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
);
}
)
.UseIIS()
.UseIISIntegration();
#region services
var services = builder.Services;
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownIPNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddMvc(x =>
{
x.Filters.Add(typeof(GlobalFilter));
x.Filters.Add(new TypeFilterAttribute(typeof(ExceptionHandleAttribute)));
});
//压缩
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",
]);
options.EnableForHttps = true;
});
services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Optimal;
});
services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Optimal;
});
#if DEBUG
services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = int.MaxValue;
});
#endif
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.AddDefaultServices(configuration).AddInject();
// 注册消息消费者
services.AddMessageConsumer<ClearCacheMessage, ClearCacheMessageHandler>();
services
.AddWebMarkupMin(x =>
{
x.AllowMinificationInDevelopmentEnvironment = true;
x.AllowCompressionInDevelopmentEnvironment = true;
})
.AddHtmlMinification(x =>
{
var settings = x.MinificationSettings;
settings.RemoveOptionalEndTags = false;
})
.AddXmlMinification()
.AddHttpCompression(option =>
{
option.CompressorFactories = new List<ICompressorFactory>
{
new BuiltInBrotliCompressorFactory(
new BuiltInBrotliCompressionSettings { Level = CompressionLevel.Optimal }
),
new DeflateCompressorFactory(
new DeflateCompressionSettings { Level = CompressionLevel.Optimal }
),
new GZipCompressorFactory(
new GZipCompressionSettings { Level = CompressionLevel.Optimal }
),
};
});
// 响应式服务
services.AddResponsive();
// 设备识别
services.AddDetection();
// .AddDevice().AddBrowser()
// .AddPlatform()
// .AddEngine()
// .AddCrawler();
//注册 响应服务
services.AddResponsive();
//services.AddHostedService<TimedHostedService>();
services.AddControllersWithViews();
//注册 缓存中间件
services.AddResponseCaching();
var redisConnectionString =
configuration.GetConnectionString("redis")
?? throw new Exception("configuration node redis is null or empty");
services.AddBusinessServices(configuration);
// 注册限流服务
services.AddIpRateLimit();
// Cors
services.AddCors(options =>
{
options.AddPolicy(
corsScheme,
cfg =>
{
cfg.WithOrigins(configuration.GetSection("Origins").Get<string[]>() ?? [])
.WithMethods("GET", "PUT", "POST", "DELETE", "PATCH", "OPTIONS")
.AllowAnyHeader();
}
);
});
//注册 身份认证服务
services
.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = AuthorizeCookieName;
x.DefaultSignInScheme = AuthorizeCookieName;
x.DefaultChallengeScheme = OpenIddictClientAspNetCoreDefaults.AuthenticationScheme;
})
.AddCookie(
AuthorizeCookieName,
options =>
{
options.LoginPath = "/connect/oidc";
options.AccessDeniedPath = "/connect/oidc";
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Lax;
}
);
//services.AddAuthorization(x => )
services
.AddIdentityCore<VmUserInfo>(x =>
x.ClaimsIdentity.SecurityStampClaimType = AuthorizeCookieName
)
.AddUserStore<UserStore>()
.AddSignInManager<ApplicationSignInManager>();
services.AddDataProtection().SetApplicationName(AuthorizeCookieName);
services
.AddOptions<KeyManagementOptions>()
.Configure<IServiceScopeFactory>(
(options, factory) =>
{
options.XmlRepository = new XmlRepositoryService(
factory,
AuthorizeCookieName + ".Key"
);
}
);
services.AddOpenIddictClient(configuration);
//register SignalR
services
.AddSignalR(x =>
{
x.HandshakeTimeout = TimeSpan.FromMinutes(2);
x.MaximumReceiveMessageSize = 32768;
})
.AddStackExchangeRedis(
redisConnectionString,
options =>
{
options.Configuration.ChannelPrefix = RedisChannel.Literal("Dpz.Core.signalR");
}
);
// 添加Hangfire 服务
services.HangfireService(configuration);
#endregion
var app = builder.Build();
#region configuration
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseRouteDebugger();
}
app.UseForwardedHeaders();
var supportedCultures = new[] { new CultureInfo("zh-CN") };
app.UseRequestLocalization(
new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("zh-CN"),
// Formatting numbers, dates, etc.
SupportedCultures = supportedCultures,
// UI strings that we have localized.
SupportedUICultures = supportedCultures,
}
);
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.UserAgent.ToString());
diagnosticContext.Set("IpAddress", httpContext.Request.GetIpAddress());
var referer = httpContext.Request.Headers.Referer;
if (!StringValues.IsNullOrEmpty(referer))
{
diagnosticContext.Set("Referer", referer);
}
};
});
//启用压缩
app.UseResponseCompression();
// 添加IP限流中间件
app.UseIpRateLimit();
// 拒绝爬虫
app.UseRejectBots();
// app.UseHsts();
// app.UseHttpsRedirection();
//启用静态文件
app.UseStaticFiles(
new StaticFileOptions()
{
OnPrepareResponse = ctx =>
{
// 如果访问静态资源的图片,那么设置缓存
if (
ctx.Context.Response.ContentType?.StartsWith(
"image/",
StringComparison.CurrentCultureIgnoreCase
) == true
)
{
ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=2592000");
ctx.Context.Response.Headers.Append(
"Expires",
DateTime.UtcNow.AddDays(30).ToString("R", CultureInfo.InvariantCulture)
);
}
},
}
);
app.UseWebMarkupMin();
//启用缓存中间件
app.UseResponseCaching();
//启用响应服务
app.UseResponsive();
app.UseCors(corsScheme);
app.UseDetection();
app.UseRouting().UseMiddleware<HttpResponseHeaderHandel>().UseRecordRequest();
//启用身份认证
app.UseAuthentication();
//启用身份授权
app.UseAuthorization();
// 路由
app.ConfigurationRoute();
//系统初始化
app.Init();
// 使用 hangfire UI,及周期性任务调度
app.HangfireSetting();
app.MapGet(
"/ping",
requestDelegate: async context =>
{
await context.Response.WriteAsJsonAsync(context.Request.Headers);
}
);
#endregion
Log.Information("Starting web core host");
app.Run();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
Log.Fatal(ex, "Host terminated unexpectedly");
}
public partial class Program
{
/// <summary>
/// 身份认证Cookie名称
/// </summary>
public const string AuthorizeCookieName = "Dpz.Web.Core.Authoriza";
/// <summary>
/// library host
/// </summary>
public static string LibraryHost { get; private set; } = "";
/// <summary>
/// assets host
/// </summary>
public static string AssetsHost { get; private set; } = "";
/// <summary>
/// 版本号
/// </summary>
public static string Version =>
Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "_version";
/// <summary>
/// 任务调度面板地址
/// </summary>
public const string HangfireDashboardPath = "/runtask";
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个使用 ASP.NET Core 构建的 Web 应用程序的启动配置文件(Program.cs)。让我详细解释一下代码的各个部分:
整体结构
这是一个现代化的 ASP.NET Core 应用程序,使用了最小 API 托管模型,包含了丰富的中间件和服务配置。
主要功能模块
1. 日志配置
Log.Logger = new LoggerConfiguration().Enrich.FromLogContext().CreateBootstrapLogger();
使用 Serilog 进行日志记录,支持结构化日志和 LogSeq 集成。
2. 配置管理
- 使用 AgileConfig 作为配置中心
- 支持开发环境本地配置覆盖
- 验证必要的配置节点(LibraryHost、AssetsHost)
3. 服务注册(Services)
核心服务:
- MVC 服务配置,包含全局过滤器
- 响应压缩(Brotli 和 Gzip)
- 限流服务(固定窗口限流器)
- 依赖注入容器
Web 优化:
- HTML/XML 压缩和最小化
- 响应式设计支持
- 设备检测功能
- 响应缓存
安全与认证:
- CORS 跨域配置
- Cookie 身份认证
- OpenIddict 客户端集成
- 数据保护和密钥管理
实时通信:
- SignalR 配置,使用 Redis 作为后端存储
后台任务:
- Hangfire 任务调度服务
4. 中间件管道(Pipeline)
按执行顺序配置的中间件:
- 开发环境异常页面
- 转发头处理
- 本地化支持(中文)
- Serilog 请求日志记录
- 响应压缩
- IP 限流
- 反爬虫保护
- 静态文件服务(带缓存策略)
- 标记压缩
- 响应缓存
- CORS 处理
- 路由和自定义中间件
- 身份认证和授权
5. 特殊功能
缓存策略:
- 图片静态资源设置 30 天缓存
- 响应缓存中间件
安全措施:
- IP 限流(评论功能每分钟 3 次)
- 反爬虫中间件
- HTTPS 强制和安全 Cookie
监控和调试:
- 详细的请求日志记录(IP、User-Agent、Referer 等)
/ping端点用于健康检查- Hangfire 仪表板
配置常量
程序定义了几个重要常量:
AuthorizeCookieName: 身份认证 Cookie 名称LibraryHost和AssetsHost: 外部资源主机地址HangfireDashboardPath: 任务调度面板路径
总结
这是一个功能完整的企业级 Web 应用程序配置,包含了:
- 完整的身份认证授权体系
- 性能优化(压缩、缓存、CDN)
- 安全防护(限流、反爬虫、CORS)
- 可观测性(日志、监控)
- 实时通信能力
- 后台任务调度
代码结构清晰,遵循了 ASP.NET Core 的最佳实践,适合作为现代 Web 应用程序的起始模板。
评论加载中...