HostApp.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. using AspNetCoreRateLimit;
  2. using Autofac;
  3. using IdentityServer4.AccessTokenValidation;
  4. using Microsoft.AspNetCore.Authentication;
  5. using Microsoft.AspNetCore.Authentication.JwtBearer;
  6. using Microsoft.AspNetCore.Builder;
  7. using Microsoft.AspNetCore.Hosting;
  8. using Microsoft.AspNetCore.Http;
  9. using Microsoft.Extensions.Configuration;
  10. using Microsoft.Extensions.DependencyInjection;
  11. using Microsoft.Extensions.DependencyInjection.Extensions;
  12. using Microsoft.Extensions.Hosting;
  13. using Microsoft.Extensions.DependencyModel;
  14. using Microsoft.IdentityModel.Tokens;
  15. using Microsoft.OpenApi.Models;
  16. using Newtonsoft.Json;
  17. using Newtonsoft.Json.Serialization;
  18. using System;
  19. using System.Collections.Generic;
  20. using System.IdentityModel.Tokens.Jwt;
  21. using System.Linq;
  22. using System.Reflection;
  23. using System.Text;
  24. using Mapster;
  25. using Yitter.IdGenerator;
  26. using FluentValidation;
  27. using FluentValidation.AspNetCore;
  28. using ZhonTai.Admin.Core.Auth;
  29. using ZhonTai.Admin.Tools.Cache;
  30. using ZhonTai.Common.Helpers;
  31. using ZhonTai.Admin.Core.Db;
  32. using ZhonTai.Admin.Core.Extensions;
  33. using ZhonTai.Admin.Core.Filters;
  34. using ZhonTai.Admin.Core.Logs;
  35. using ZhonTai.Admin.Core.RegisterModules;
  36. using System.IO;
  37. using Microsoft.OpenApi.Any;
  38. using Microsoft.AspNetCore.Mvc.Controllers;
  39. using ZhonTai.Admin.Core.Attributes;
  40. using ZhonTai.Admin.Core.Configs;
  41. using ZhonTai.Admin.Core.Consts;
  42. using MapsterMapper;
  43. using ZhonTai.DynamicApi;
  44. using NLog.Web;
  45. using Autofac.Extensions.DependencyInjection;
  46. using Microsoft.AspNetCore.Mvc;
  47. using ZhonTai.Admin.Core.Startup;
  48. using ZhonTai.Admin.Core.Conventions;
  49. using FreeSql;
  50. using ZhonTai.Admin.Services.User;
  51. using ZhonTai.Admin.Core.Middlewares;
  52. using ZhonTai.Admin.Core.Dto;
  53. namespace ZhonTai.Admin.Core;
  54. public class HostApp
  55. {
  56. readonly HostAppOptions _hostAppOptions;
  57. public HostApp()
  58. {
  59. }
  60. public HostApp(HostAppOptions hostAppOptions)
  61. {
  62. _hostAppOptions = hostAppOptions;
  63. }
  64. public void Run(string[] args)
  65. {
  66. var builder = WebApplication.CreateBuilder(args);
  67. //使用NLog日志
  68. builder.Host.UseNLog();
  69. //添加配置
  70. builder.Host.ConfigureAppConfiguration((context, builder) =>
  71. {
  72. builder.AddJsonFile("./Configs/ratelimitconfig.json", optional: true, reloadOnChange: true);
  73. if (context.HostingEnvironment.EnvironmentName.NotNull())
  74. {
  75. builder.AddJsonFile($"./Configs/ratelimitconfig.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true);
  76. }
  77. builder.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
  78. if (context.HostingEnvironment.EnvironmentName.NotNull())
  79. {
  80. builder.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true);
  81. }
  82. });
  83. var services = builder.Services;
  84. var env = builder.Environment;
  85. var configuration = builder.Configuration;
  86. var configHelper = new ConfigHelper();
  87. var appConfig = ConfigHelper.Get<AppConfig>("appconfig", env.EnvironmentName) ?? new AppConfig();
  88. //应用配置
  89. services.AddSingleton(appConfig);
  90. //使用Autofac容器
  91. builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
  92. //配置Autofac容器
  93. builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
  94. {
  95. // 控制器注入
  96. builder.RegisterModule(new ControllerModule());
  97. // 单例注入
  98. builder.RegisterModule(new SingleInstanceModule(appConfig));
  99. // 仓储注入
  100. builder.RegisterModule(new RepositoryModule(appConfig));
  101. // 服务注入
  102. builder.RegisterModule(new ServiceModule(appConfig));
  103. });
  104. //配置Kestrel服务器
  105. builder.WebHost.ConfigureKestrel((context, options) =>
  106. {
  107. //设置应用服务器Kestrel请求体最大为100MB
  108. options.Limits.MaxRequestBodySize = 1024 * 1024 * 100;
  109. });
  110. //访问地址
  111. builder.WebHost.UseUrls(appConfig.Urls);
  112. //配置服务
  113. ConfigureServices(services, env, configuration, configHelper, appConfig);
  114. var app = builder.Build();
  115. //配置中间件
  116. ConfigureMiddleware(app, env, configuration, appConfig);
  117. app.Run();
  118. }
  119. /// <summary>
  120. /// 配置服务
  121. /// </summary>
  122. /// <param name="services"></param>
  123. /// <param name="env"></param>
  124. /// <param name="configuration"></param>
  125. /// <param name="configHelper"></param>
  126. /// <param name="appConfig"></param>
  127. private void ConfigureServices(IServiceCollection services, IWebHostEnvironment env, IConfiguration configuration, ConfigHelper configHelper, AppConfig appConfig)
  128. {
  129. var hostAppContext = new HostAppContext()
  130. {
  131. Services = services,
  132. Environment = env,
  133. Configuration = configuration
  134. };
  135. _hostAppOptions?.ConfigurePreServices?.Invoke(hostAppContext);
  136. //雪花漂移算法
  137. var idGeneratorOptions = new IdGeneratorOptions(1) { WorkerIdBitLength = 6 };
  138. _hostAppOptions?.ConfigureIdGenerator?.Invoke(idGeneratorOptions);
  139. YitIdHelper.SetIdGenerator(idGeneratorOptions);
  140. //权限处理
  141. services.AddScoped<IPermissionHandler, PermissionHandler>();
  142. // ClaimType不被更改
  143. JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
  144. //用户信息
  145. services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  146. services.TryAddScoped<IUser, User>();
  147. //数据库配置
  148. var dbConfig = ConfigHelper.Get<DbConfig>("dbconfig", env.EnvironmentName);
  149. services.AddSingleton(dbConfig);
  150. //添加数据库
  151. if (!_hostAppOptions.CustomInitDb)
  152. {
  153. services.AddDb(env, _hostAppOptions);
  154. }
  155. //上传配置
  156. var uploadConfig = ConfigHelper.Load("uploadconfig", env.EnvironmentName, true);
  157. services.Configure<UploadConfig>(uploadConfig);
  158. //程序集
  159. Assembly[] assemblies = null;
  160. if(appConfig.AssemblyNames?.Length > 0)
  161. {
  162. assemblies = DependencyContext.Default.RuntimeLibraries
  163. .Where(a => appConfig.AssemblyNames.Contains(a.Name))
  164. .Select(o => Assembly.Load(new AssemblyName(o.Name))).ToArray();
  165. }
  166. #region Mapster 映射配置
  167. services.AddScoped<IMapper>(sp => new Mapper());
  168. if(assemblies?.Length > 0)
  169. {
  170. TypeAdapterConfig.GlobalSettings.Scan(assemblies);
  171. }
  172. #endregion Mapster 映射配置
  173. #region Cors 跨域
  174. services.AddCors(options =>
  175. {
  176. options.AddPolicy(AdminConsts.RequestPolicyName, policy =>
  177. {
  178. var hasOrigins = appConfig.CorUrls?.Length > 0;
  179. if (hasOrigins)
  180. {
  181. policy.WithOrigins(appConfig.CorUrls);
  182. }
  183. else
  184. {
  185. policy.AllowAnyOrigin();
  186. }
  187. policy
  188. .AllowAnyHeader()
  189. .AllowAnyMethod();
  190. if (hasOrigins)
  191. {
  192. policy.AllowCredentials();
  193. }
  194. });
  195. //允许任何源访问Api策略,使用时在控制器或者接口上增加特性[EnableCors(AdminConsts.AllowAnyPolicyName)]
  196. options.AddPolicy(AdminConsts.AllowAnyPolicyName, policy =>
  197. {
  198. policy
  199. .AllowAnyOrigin()
  200. .AllowAnyHeader()
  201. .AllowAnyMethod();
  202. });
  203. });
  204. #endregion Cors 跨域
  205. #region 身份认证授权
  206. var jwtConfig = ConfigHelper.Get<JwtConfig>("jwtconfig", env.EnvironmentName);
  207. services.TryAddSingleton(jwtConfig);
  208. if (appConfig.IdentityServer.Enable)
  209. {
  210. //is4
  211. services.AddAuthentication(options =>
  212. {
  213. options.DefaultScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
  214. options.DefaultChallengeScheme = nameof(ResponseAuthenticationHandler); //401
  215. options.DefaultForbidScheme = nameof(ResponseAuthenticationHandler); //403
  216. })
  217. .AddJwtBearer(options =>
  218. {
  219. options.Authority = appConfig.IdentityServer.Url;
  220. options.RequireHttpsMetadata = false;
  221. options.Audience = "admin.server.api";
  222. })
  223. .AddScheme<AuthenticationSchemeOptions, ResponseAuthenticationHandler>(nameof(ResponseAuthenticationHandler), o => { });
  224. }
  225. else
  226. {
  227. //jwt
  228. services.AddAuthentication(options =>
  229. {
  230. options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
  231. options.DefaultChallengeScheme = nameof(ResponseAuthenticationHandler); //401
  232. options.DefaultForbidScheme = nameof(ResponseAuthenticationHandler); //403
  233. })
  234. .AddJwtBearer(options =>
  235. {
  236. options.TokenValidationParameters = new TokenValidationParameters
  237. {
  238. ValidateIssuer = true,
  239. ValidateAudience = true,
  240. ValidateLifetime = true,
  241. ValidateIssuerSigningKey = true,
  242. ValidIssuer = jwtConfig.Issuer,
  243. ValidAudience = jwtConfig.Audience,
  244. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey)),
  245. ClockSkew = TimeSpan.Zero
  246. };
  247. })
  248. .AddScheme<AuthenticationSchemeOptions, ResponseAuthenticationHandler>(nameof(ResponseAuthenticationHandler), o => { });
  249. }
  250. #endregion 身份认证授权
  251. #region Swagger Api文档
  252. if (env.IsDevelopment() || appConfig.Swagger.Enable)
  253. {
  254. services.AddSwaggerGen(options =>
  255. {
  256. appConfig.Swagger.Projects?.ForEach(project =>
  257. {
  258. options.SwaggerDoc(project.Code.ToLower(), new OpenApiInfo
  259. {
  260. Title = project.Name,
  261. Version = project.Version,
  262. Description = project.Description
  263. });
  264. //c.OrderActionsBy(o => o.RelativePath);
  265. });
  266. options.CustomOperationIds(apiDesc =>
  267. {
  268. var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
  269. return controllerAction.ControllerName + "-" + controllerAction.ActionName;
  270. });
  271. options.ResolveConflictingActions(apiDescription => apiDescription.First());
  272. //options.CustomSchemaIds(x => x.FullName);
  273. //options.DocInclusionPredicate((docName, description) => true);
  274. string[] xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml");
  275. if (xmlFiles.Length > 0)
  276. {
  277. foreach (var xmlFile in xmlFiles)
  278. {
  279. options.IncludeXmlComments(xmlFile, true);
  280. }
  281. }
  282. var server = new OpenApiServer()
  283. {
  284. Url = appConfig.Swagger.Url,
  285. Description = ""
  286. };
  287. if (appConfig.ApiUI.Footer.Enable)
  288. {
  289. server.Extensions.Add("extensions", new OpenApiObject
  290. {
  291. ["copyright"] = new OpenApiString(appConfig.ApiUI.Footer.Content)
  292. });
  293. }
  294. options.AddServer(server);
  295. options.SchemaFilter<EnumSchemaFilter>();
  296. #region 添加设置Token的按钮
  297. if (appConfig.IdentityServer.Enable)
  298. {
  299. //添加Jwt验证设置
  300. options.AddSecurityRequirement(new OpenApiSecurityRequirement()
  301. {
  302. {
  303. new OpenApiSecurityScheme
  304. {
  305. Reference = new OpenApiReference
  306. {
  307. Id = "oauth2",
  308. Type = ReferenceType.SecurityScheme
  309. }
  310. },
  311. new List<string>()
  312. }
  313. });
  314. //统一认证
  315. options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
  316. {
  317. Type = SecuritySchemeType.OAuth2,
  318. Description = "oauth2登录授权",
  319. Flows = new OpenApiOAuthFlows
  320. {
  321. Implicit = new OpenApiOAuthFlow
  322. {
  323. AuthorizationUrl = new Uri($"{appConfig.IdentityServer.Url}/connect/authorize"),
  324. Scopes = new Dictionary<string, string>
  325. {
  326. { "admin.server.api", "admin后端api" }
  327. }
  328. }
  329. }
  330. });
  331. }
  332. else
  333. {
  334. //添加Jwt验证设置
  335. options.AddSecurityRequirement(new OpenApiSecurityRequirement()
  336. {
  337. {
  338. new OpenApiSecurityScheme
  339. {
  340. Reference = new OpenApiReference
  341. {
  342. Id = "Bearer",
  343. Type = ReferenceType.SecurityScheme
  344. }
  345. },
  346. new List<string>()
  347. }
  348. });
  349. options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
  350. {
  351. Description = "Value: Bearer {token}",
  352. Name = "Authorization",
  353. In = ParameterLocation.Header,
  354. Type = SecuritySchemeType.ApiKey
  355. });
  356. }
  357. #endregion 添加设置Token的按钮
  358. });
  359. }
  360. #endregion Swagger Api文档
  361. #region 操作日志
  362. if (appConfig.Log.Operation)
  363. {
  364. services.AddScoped<ILogHandler, LogHandler>();
  365. }
  366. #endregion 操作日志
  367. #region 控制器
  368. void mvcConfigure(MvcOptions options)
  369. {
  370. //options.Filters.Add<ControllerExceptionFilter>();
  371. options.Filters.Add<ValidateInputFilter>();
  372. if (appConfig.Validate.Login || appConfig.Validate.Permission)
  373. {
  374. options.Filters.Add<ValidatePermissionAttribute>();
  375. }
  376. //在具有较高的 Order 值的筛选器之前运行 before 代码
  377. //在具有较高的 Order 值的筛选器之后运行 after 代码
  378. if (appConfig.DynamicApi.FormatResult)
  379. {
  380. options.Filters.Add<FormatResultFilter>(20);
  381. }
  382. if (appConfig.Log.Operation)
  383. {
  384. options.Filters.Add<ControllerLogFilter>(10);
  385. }
  386. //禁止去除ActionAsync后缀
  387. //options.SuppressAsyncSuffixInActionNames = false;
  388. if (env.IsDevelopment() || appConfig.Swagger.Enable)
  389. {
  390. //API分组约定
  391. options.Conventions.Add(new ApiGroupConvention());
  392. }
  393. }
  394. var mvcBuilder = appConfig.AppType switch
  395. {
  396. AppType.Controllers => services.AddControllers(mvcConfigure),
  397. AppType.ControllersWithViews => services.AddControllersWithViews(mvcConfigure),
  398. AppType.MVC => services.AddMvc(mvcConfigure),
  399. _ => services.AddControllers(mvcConfigure)
  400. };
  401. if (assemblies?.Length > 0)
  402. {
  403. foreach (var assembly in assemblies)
  404. {
  405. services.AddValidatorsFromAssembly(assembly);
  406. }
  407. }
  408. services.AddFluentValidationAutoValidation();
  409. mvcBuilder.AddNewtonsoftJson(options =>
  410. {
  411. //忽略循环引用
  412. options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
  413. //使用驼峰 首字母小写
  414. options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  415. //设置时间格式
  416. options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
  417. })
  418. .AddControllersAsServices();
  419. #endregion 控制器
  420. services.AddHttpClient();
  421. _hostAppOptions?.ConfigureServices?.Invoke(hostAppContext);
  422. #region 缓存
  423. var cacheConfig = ConfigHelper.Get<CacheConfig>("cacheconfig", env.EnvironmentName);
  424. if (cacheConfig.Type == CacheType.Redis)
  425. {
  426. var csredis = new CSRedis.CSRedisClient(cacheConfig.Redis.ConnectionString);
  427. RedisHelper.Initialization(csredis);
  428. services.AddSingleton<ICacheTool, RedisCacheTool>();
  429. }
  430. else
  431. {
  432. services.AddMemoryCache();
  433. services.AddSingleton<ICacheTool, MemoryCacheTool>();
  434. }
  435. #endregion 缓存
  436. #region IP限流
  437. if (appConfig.RateLimit)
  438. {
  439. services.AddIpRateLimit(configuration, cacheConfig);
  440. }
  441. #endregion IP限流
  442. //阻止NLog接收状态消息
  443. services.Configure<ConsoleLifetimeOptions>(opts => opts.SuppressStatusMessages = true);
  444. //性能分析
  445. if (appConfig.MiniProfiler)
  446. {
  447. services.AddMiniProfiler();
  448. }
  449. //动态api
  450. services.AddDynamicApi(options =>
  451. {
  452. Assembly[] assemblies = DependencyContext.Default.RuntimeLibraries
  453. .Where(a => a.Name.EndsWith("Service"))
  454. .Select(o => Assembly.Load(new AssemblyName(o.Name))).ToArray();
  455. options.AddAssemblyOptions(assemblies);
  456. options.FormatResult = appConfig.DynamicApi.FormatResult;
  457. options.FormatResultType = typeof(ResultOutput<>);
  458. _hostAppOptions?.ConfigureDynamicApi?.Invoke(options);
  459. });
  460. _hostAppOptions?.ConfigurePostServices?.Invoke(hostAppContext);
  461. }
  462. /// <summary>
  463. /// 配置中间件
  464. /// </summary>
  465. /// <param name="app"></param>
  466. /// <param name="env"></param>
  467. /// <param name="configuration"></param>
  468. /// <param name="appConfig"></param>
  469. private void ConfigureMiddleware(WebApplication app, IWebHostEnvironment env, IConfiguration configuration, AppConfig appConfig)
  470. {
  471. var hostAppMiddlewareContext = new HostAppMiddlewareContext()
  472. {
  473. App = app,
  474. Environment = env,
  475. Configuration = configuration
  476. };
  477. _hostAppOptions?.ConfigurePreMiddleware?.Invoke(hostAppMiddlewareContext);
  478. //异常处理
  479. app.UseMiddleware<ExceptionMiddleware>();
  480. //IP限流
  481. if (appConfig.RateLimit)
  482. {
  483. app.UseIpRateLimiting();
  484. }
  485. //性能分析
  486. if (appConfig.MiniProfiler)
  487. {
  488. app.UseMiniProfiler();
  489. }
  490. //静态文件
  491. app.UseDefaultFiles();
  492. app.UseStaticFiles();
  493. app.UseUploadConfig();
  494. //路由
  495. app.UseRouting();
  496. //跨域
  497. app.UseCors(AdminConsts.RequestPolicyName);
  498. //认证
  499. app.UseAuthentication();
  500. //授权
  501. app.UseAuthorization();
  502. //登录用户初始化数据权限
  503. app.Use(async (ctx, next) =>
  504. {
  505. var user = ctx.RequestServices.GetRequiredService<IUser>();
  506. if (user?.Id > 0)
  507. {
  508. var userService = ctx.RequestServices.GetRequiredService<IUserService>();
  509. await userService.GetDataPermissionAsync();
  510. }
  511. await next();
  512. });
  513. //配置端点
  514. app.UseEndpoints(endpoints =>
  515. {
  516. endpoints.MapControllers();
  517. });
  518. _hostAppOptions?.ConfigureMiddleware?.Invoke(hostAppMiddlewareContext);
  519. #region Swagger Api文档
  520. if (env.IsDevelopment() || appConfig.Swagger.Enable)
  521. {
  522. var routePrefix = appConfig.ApiUI.RoutePrefix;
  523. if (!appConfig.ApiUI.Enable && routePrefix.IsNull())
  524. {
  525. routePrefix = appConfig.Swagger.RoutePrefix;
  526. }
  527. var routePath = routePrefix.NotNull() ? $"{routePrefix}/" : "";
  528. app.UseSwagger(optoins =>
  529. {
  530. optoins.RouteTemplate = routePath + optoins.RouteTemplate;
  531. });
  532. app.UseSwaggerUI(options =>
  533. {
  534. options.RoutePrefix = appConfig.Swagger.RoutePrefix;
  535. appConfig.Swagger.Projects?.ForEach(project =>
  536. {
  537. options.SwaggerEndpoint($"/{routePath}swagger/{project.Code.ToLower()}/swagger.json", project.Name);
  538. });
  539. options.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);//折叠Api
  540. //options.DefaultModelsExpandDepth(-1);//不显示Models
  541. if (appConfig.MiniProfiler)
  542. {
  543. options.InjectJavascript("/swagger/mini-profiler.js?v=4.2.22+2.0");
  544. options.InjectStylesheet("/swagger/mini-profiler.css?v=4.2.22+2.0");
  545. }
  546. });
  547. }
  548. #endregion Swagger Api文档
  549. _hostAppOptions?.ConfigurePostMiddleware?.Invoke(hostAppMiddlewareContext);
  550. }
  551. }