using System; using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Configuration; using Serilog; using System.Threading.Tasks; using MassTransit; using MassTransit.NewIdProviders; using System.IO; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infra.EFCore; using IRaCIS.Core.Application.Helper; using System.Runtime.InteropServices; using Microsoft.AspNetCore.Builder; using IRaCIS.Core.API; using Autofac; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using IRaCIS.Core.Application.Filter; using Microsoft.AspNetCore.HttpOverrides; using IRaCIS.Application.Services.BackGroundJob; using LogDashboard; using FellowOakDicom.Network; using IRaCIS.Core.Application.Service.ImageAndDoc; using IP2Region.Net.Abstractions; using IP2Region.Net.XDB; using IRaCIS.Core.Application.BusinessFilter; using Microsoft.AspNetCore.Http; using IRaCIS.Core.Infrastructure.Extention; using Newtonsoft.Json; using Microsoft.AspNetCore.Diagnostics; using IRaCIS.Core.Application.MassTransit.Command; using IRaCIS.Core.Application.MassTransit.Consumer; AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true); #region 获取环境变量 //以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数) var config = new ConfigurationBuilder() .AddEnvironmentVariables() .Build(); var enviromentName = config["ASPNETCORE_ENVIRONMENT"]; if (string.IsNullOrWhiteSpace(enviromentName)) { var index = Array.IndexOf(args, "--env"); enviromentName = index > -1 ? args[index + 1] : "Development"; } #endregion var builder = WebApplication.CreateBuilder(new WebApplicationOptions { EnvironmentName = enviromentName }); #region 兼容windows 服务命令行的方式 //foreach (var arg in args) //{ // Console.WriteLine(arg); //} int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--urls")); if (urlsIndex > -1) { var url = args[urlsIndex].Substring("--urls=".Length); Console.WriteLine(url); builder.WebHost.UseUrls(url); } #endregion #region 主机配置 NewId.SetProcessIdProvider(new CurrentProcessIdProvider()); builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true) .AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true); builder.Host .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureContainer(containerBuilder => { containerBuilder.RegisterModule(); }) .UseWindowsService().UseSerilog(); #endregion #region 配置服务 var _configuration = builder.Configuration; //健康检查 builder.Services.AddHealthChecks(); //本地化 builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources"); // 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim() builder.Services.AddControllers(options => { //options.Filters.Add(); options.Filters.Add(); options.Filters.Add(); options.Filters.Add(); options.Filters.Add(10); options.Filters.Add(); }) .AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理 builder.Services.AddOptions().Configure(_configuration.GetSection("SystemEmailSendConfig")); builder.Services.AddOptions().Configure(_configuration.GetSection("BasicSystemConfig")); builder.Services.AddOptions().Configure(_configuration.GetSection("AliyunOSS")); builder.Services.AddOptions().Configure(_configuration.GetSection("ObjectStoreService")); builder.Services.AddOptions().Configure(_configuration.GetSection("EncrypteResponseConfig")); builder.Services.AddOptions().Configure(_configuration.GetSection("SystemPacsConfig")); //动态WebApi + UnifiedApiResultFilter 省掉控制器代码 builder.Services.AddDynamicWebApiSetup(); //AutoMapper builder.Services.AddAutoMapperSetup(); //EF ORM QueryWithNoLock builder.Services.AddEFSetup(_configuration,enviromentName); //Http 响应压缩 builder.Services.AddResponseCompressionSetup(); //Swagger Api 文档 builder.Services.AddSwaggerSetup(); //JWT Token 验证 builder.Services.AddJWTAuthSetup(_configuration); // MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系 //builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining()); #region MassTransit //masstransit组件 也支持MediatR 中介者模式,但是支持分布式,考虑后续,所以在次替代MediatR builder.Services.AddMediator(cfg => { cfg.AddConsumer(); }); builder.Services.AddMassTransit(cfg => { cfg.UsingInMemory(); }); #endregion #region FusionCache builder.Services.AddFusionCache(); #endregion // EasyCaching 缓存 builder.Services.AddEasyCachingSetup(_configuration); // hangfire 定时任务框架 有界面,更友好~ builder.Services.AddhangfireSetup(_configuration); //Serilog 日志可视化 LogDashboard日志 builder.Services.AddLogDashboardSetup(); builder.Services.AddJsonConfigSetup(_configuration); //转发头设置 获取真实IP builder.Services.Configure(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; }); //Dicom影像渲染图片 跨平台 builder.Services.AddDicomSetup(); // 实时应用 builder.Services.AddSignalR(); builder.Services.AddSingleton(); builder.Services.AddSingleton(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb"))); //builder.Services.AddExceptionHandler(); //builder.Services.AddProblemDetails(); #region 历史废弃配置 //builder.Services.AddMemoryCache(); ////上传限制 配置 //builder.Services.Configure(options => //{ // options.MultipartBodyLengthLimit = int.MaxValue; // options.ValueCountLimit = int.MaxValue; // options.ValueLengthLimit = int.MaxValue; //}); //IP 限流 可设置白名单 或者黑名单 //services.AddIpPolicyRateLimitSetup(_configuration); // 用户类型 策略授权 //services.AddAuthorizationPolicySetup(_configuration); #endregion #endregion var app = builder.Build(); var env = app.Environment; #region 配置中间件 // Configure the HTTP request pipeline. #region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码 //app.UseStatusCodePagesWithReExecute("/Error/{0}"); app.UseStatusCodePages(async context => { var code = context.HttpContext.Response.StatusCode; context.HttpContext.Response.ContentType = "application/json"; if (code < 500) { await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"Client error, actual request error status code({code})"))); } else { //ResultFilter 里面的异常并不会到这里 await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject((ResponseOutput.NotOk($"Server error , actual request error status code({code})")))); } }); //app.UseExceptionHandler(o => { }); //这里没生效,原因未知,官方文档也是这种写法,也用了GlobalExceptionHandler 尝试,还是不行,怀疑框架bug //app.UseExceptionHandler(configure => //{ // configure.Run(async context => // { // var exceptionHandlerPathFeature = context.Features.Get(); //var ex = exceptionHandlerPathFeature?.Error; //context.Response.ContentType = "application/json"; //if (ex != null) //{ // var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : ""); // await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}"))); // Log.Logger.Error(errorInfo); //} // }); //}); #endregion //本地化 app.UseLocalization(); app.UseForwardedHeaders(); //响应压缩 app.UseResponseCompression(); //不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication. app.UseStaticFiles(); //LogDashboard app.UseLogDashboard("/LogDashboard"); //hangfire app.UseHangfireConfig(env); #region 暂时废弃 //app.UseMiddleware(); ////限流 中间件 //app.UseIpRateLimiting(); //if (env.IsDevelopment()) //{ // app.UseDeveloperExceptionPage(); //} //else //{ // //app.UseHsts(); //} #endregion SwaggerSetup.Configure(app, env); ////serilog 记录请求的用户信息 app.UseSerilogConfig(env); app.UseRouting(); app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); //app.UseIRacisHostStaticFileStore(env); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapHub("/UploadHub"); app.MapHealthChecks("/health"); // Serilog SerilogExtension.AddSerilogSetup(enviromentName, app.Services); var hangfireJobService = app.Services.GetRequiredService(); await hangfireJobService.InitHangfireJobTaskAsync(); #endregion try { #region 运行环境 部署平台 Log.Logger.Warning($"当前环境:{enviromentName}"); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Log.Logger.Warning($"当前部署平台环境:windows"); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Log.Logger.Warning($"当前部署平台环境:linux"); } else { Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD"); } #endregion Log.Logger.Warning($"ContentRootPath:{env.ContentRootPath}"); string parentDirectory = Path.GetFullPath(Path.Combine(env.ContentRootPath, "..")); Log.Logger.Warning($"ContentRootPath——parentDirectory:{parentDirectory}"); //Log.Logger.Warning($"ContentRootPath——GetParent:{Directory.GetParent(env.ContentRootPath).Parent.FullName}"); //Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}"); app.Run(); } catch (Exception e) { Log.Logger.Error(e.InnerException is null ? e.Message + e.StackTrace : e.InnerException?.Message + e.InnerException?.StackTrace); }