// Admin.NET 项目的版æƒã€å•†æ ‡ã€ä¸“利和其他相关æƒåˆ©å‡å—ç›¸åº”æ³•å¾‹æ³•è§„çš„ä¿æŠ¤ã€‚ä½¿ç”¨æœ¬é¡¹ç›®åº”éµå®ˆç›¸å…³æ³•律法规和许å¯è¯çš„è¦æ±‚。 // // 本项目主è¦éµå¾ª MIT 许å¯è¯å’Œ Apache 许å¯è¯ï¼ˆç‰ˆæœ¬ 2.0)进行分å‘和使用。许å¯è¯ä½äºŽæºä»£ç æ ‘æ ¹ç›®å½•ä¸çš„ LICENSE-MIT å’Œ LICENSE-APACHE 文件。 // // ä¸å¾—利用本项目从事å±å®³å›½å®¶å®‰å…¨ã€æ‰°ä¹±ç¤¾ä¼šç§©åºã€ä¾µçŠ¯ä»–äººåˆæ³•æƒç›Šç‰æ³•å¾‹æ³•è§„ç¦æ¢çš„æ´»åЍï¼ä»»ä½•基于本项目二次开å‘è€Œäº§ç”Ÿçš„ä¸€åˆ‡æ³•å¾‹çº çº·å’Œè´£ä»»ï¼Œæˆ‘ä»¬ä¸æ‰¿æ‹…ä»»ä½•è´£ä»»ï¼ using Admin.NET.Core; using Admin.NET.Core.Service; using AspNetCoreRateLimit; using Furion; using Furion.Logging; using Furion.SpecificationDocument; using Furion.VirtualFileServer; using IGeekFan.AspNetCore.Knife4jUI; using IPTools.Core; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using OnceMi.AspNetCore.OSS; using SixLabors.ImageSharp.Web.DependencyInjection; using System; using System.Threading.Tasks; namespace Admin.NET.Web.Core; public class Startup : AppStartup { public void ConfigureServices(IServiceCollection services) { // é…置选项 services.AddProjectOptions(); // ç¼“å˜æ³¨å†Œ services.AddCache(); // SqlSugar services.AddSqlSugar(); // JWT services.AddJwt<JwtHandler>(enableGlobalAuthorize: true, jwtBearerConfigure: options => { // 实现 JWT 身份验è¯è¿‡ç¨‹æŽ§åˆ¶ options.Events = new JwtBearerEvents { OnMessageReceived = context => { var httpContext = context.HttpContext; // 若请求 Url åŒ…å« token 傿•°ï¼Œåˆ™è®¾ç½® Token 值 if (httpContext.Request.Query.ContainsKey("token")) context.Token = httpContext.Request.Query["token"]; return Task.CompletedTask; } }; }).AddSignatureAuthentication(options => // æ·»åŠ Signature èº«ä»½éªŒè¯ { options.Events = SysOpenAccessService.GetSignatureAuthenticationEventImpl(); }); // å…许跨域 services.AddCorsAccessor(); // 远程请求 services.AddRemoteRequest(); // 任务队列 services.AddTaskQueue(); // 任务调度 services.AddSchedule(options => { options.AddPersistence<DbJobPersistence>(); // æ·»åŠ ä½œä¸šæŒä¹…化器 options.AddMonitor<JobMonitor>(); // æ·»åŠ ä½œä¸šæ‰§è¡Œç›‘è§†å™¨ }); // è„±æ•æ£€æµ‹ services.AddSensitiveDetection(); // Jsonåºåˆ—化设置 static void SetNewtonsoftJsonSetting(JsonSerializerSettings setting) { setting.DateFormatHandling = DateFormatHandling.IsoDateFormat; setting.DateTimeZoneHandling = DateTimeZoneHandling.Local; // setting.Converters.AddDateTimeTypeConverters(localized: true); // 时间本地化 setting.DateFormatString = "yyyy-MM-dd HH:mm:ss"; // æ—¶é—´æ ¼å¼åŒ– setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // 忽略循环引用 // setting.ContractResolver = new CamelCasePropertyNamesContractResolver(); // 解决动æ€å¯¹è±¡å±žæ€§å大写 // setting.NullValueHandling = NullValueHandling.Ignore; // 忽略空值 // setting.Converters.AddLongTypeConverters(); // long转string(防æ¢js精度溢出) 超过17ä½å¼€å¯ // setting.MetadataPropertyHandling = MetadataPropertyHandling.Ignore; // 解决DateTimeOffset异常 // setting.DateParseHandling = DateParseHandling.None; // 解决DateTimeOffset异常 // setting.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }); // 解决DateTimeOffset异常 }; services.AddControllersWithViews() .AddAppLocalization() .AddNewtonsoftJson(options => SetNewtonsoftJsonSetting(options.SerializerSettings)) //.AddXmlSerializerFormatters() //.AddXmlDataContractSerializerFormatters() .AddInjectWithUnifyResult<AdminResultProvider>(); // 三方授æƒç™»å½•OAuth services.AddOAuth(); // ElasticSearch services.AddElasticSearch(); // é…ç½®Nginx转å‘获å–客户端真实IP // 注1:如果负载å‡è¡¡ä¸æ˜¯åœ¨æœ¬æœºé€šè¿‡ Loopback 地å€è½¬å‘请求的,一定è¦åŠ ä¸Šoptions.KnownNetworks.Clear()å’Œoptions.KnownProxies.Clear() // 注2:如果设置环境å˜é‡ ASPNETCORE_FORWARDEDHEADERS_ENABLED 为 True,则ä¸éœ€è¦ä¸‹é¢çš„é…置代ç services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.All; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); // 陿µæœåŠ¡ services.AddInMemoryRateLimiting(); services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(); // 事件总线 services.AddEventBus(options => { options.UseUtcTimestamp = false; // ä¸å¯ç”¨äº‹ä»¶æ—¥å¿— options.LogEnabled = false; // 事件执行器(失败é‡è¯•) options.AddExecutor<RetryEventHandlerExecutor>(); // 事件执行器(é‡è¯•åŽä¾ç„¶å¤„ç†æœªå¤„ç†å¼‚常的处ç†å™¨ï¼‰ options.UnobservedTaskExceptionHandler = (obj, args) => { if (args.Exception?.Message != null) Log.Error($"EeventBus 有未处ç†å¼‚常 :{args.Exception?.Message} ", args.Exception); }; // 事件执行器-监视器(æ¯ä¸€æ¬¡å¤„ç†éƒ½ä¼šè¿›å…¥ï¼‰ options.AddMonitor<EventHandlerMonitor>(); #region Redis消æ¯é˜Ÿåˆ— // 替æ¢äº‹ä»¶æºå˜å‚¨å™¨ä¸ºRedis var cacheOptions = App.GetConfig<CacheOptions>("Cache", true); if (cacheOptions.CacheType == CacheTypeEnum.Redis.ToString()) { options.ReplaceStorer(serviceProvider => { var cacheProvider = serviceProvider.GetRequiredService<NewLife.Caching.ICacheProvider>(); // 创建默认内å˜é€šé“事件æºå¯¹è±¡ï¼Œå¯è‡ªå®šä¹‰é˜Ÿåˆ—路由key,如:adminnet_eventsource_queue return new RedisEventSourceStorer(cacheProvider, "adminnet_eventsource_queue", 3000); }); } #endregion Redis消æ¯é˜Ÿåˆ— #region RabbitMQ消æ¯é˜Ÿåˆ— //// 创建默认内å˜é€šé“事件æºå¯¹è±¡ï¼Œå¯è‡ªå®šä¹‰é˜Ÿåˆ—路由key,如:adminnet //var eventBusOpt = App.GetConfig<EventBusOptions>("EventBus", true); //var rbmqEventSourceStorer = new RabbitMQEventSourceStore(new ConnectionFactory //{ // UserName = eventBusOpt.RabbitMQ.UserName, // Password = eventBusOpt.RabbitMQ.Password, // HostName = eventBusOpt.RabbitMQ.HostName, // Port = eventBusOpt.RabbitMQ.Port //}, "adminnet", 3000); //// 替æ¢é»˜è®¤äº‹ä»¶æ€»çº¿å˜å‚¨å™¨ //options.ReplaceStorer(serviceProvider => //{ // return rbmqEventSourceStorer; //}); #endregion RabbitMQ消æ¯é˜Ÿåˆ— }); // 图åƒå¤„ç† services.AddImageSharp(); // OSS对象å˜å‚¨ var ossOpt = App.GetConfig<OSSProviderOptions>("OSSProvider", true); services.AddOSSService(Enum.GetName(ossOpt.Provider), "OSSProvider"); // 模æ¿å¼•擎 services.AddViewEngine(); // 峿—¶é€šè®¯ services.AddSignalR(options => { options.KeepAliveInterval = TimeSpan.FromSeconds(5); }).AddNewtonsoftJsonProtocol(options => SetNewtonsoftJsonSetting(options.PayloadSerializerSettings)); // 系统日志 services.AddLoggingSetup(); // 验è¯ç services.AddCaptcha(); // 控制å°logo services.AddConsoleLogo(); // å°†IPåœ°å€æ•°æ®åº“æ–‡ä»¶å®Œå…¨åŠ è½½åˆ°å†…å˜ï¼Œæå‡æŸ¥è¯¢é€Ÿåº¦ï¼ˆä»¥ç©ºé—´æ¢æ—¶é—´ï¼Œå†…å˜å°†ä¼šå¢žåŠ 60-70M) IpToolSettings.LoadInternationalDbToMemory = true; // 设置默认查询器Chinaå’ŒInternational //IpToolSettings.DefalutSearcherType = IpSearcherType.China; IpToolSettings.DefalutSearcherType = IpSearcherType.International; } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseForwardedHeaders(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.Use(async (context, next) => { context.Response.Headers.Append("Admin.NET", "Admin.NET"); await next(); }); // 图åƒå¤„ç† app.UseImageSharp(); // 特定文件类型(文件åŽç¼€ï¼‰å¤„ç† var contentTypeProvider = FS.GetFileExtensionContentTypeProvider(); // contentTypeProvider.Mappings[".文件åŽç¼€"] = "MIME 类型"; app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = contentTypeProvider }); //// å¯ç”¨HTTPS //app.UseHttpsRedirection(); // å¯ç”¨OAuth app.UseOAuth(); // æ·»åŠ çŠ¶æ€ç 拦截ä¸é—´ä»¶ app.UseUnifyResultStatusCodes(); // å¯ç”¨å¤šè¯è¨€ï¼Œå¿…须在 UseRouting ä¹‹å‰ app.UseAppLocalization(); // 路由注册 app.UseRouting(); // å¯ç”¨è·¨åŸŸï¼Œå¿…须在 UseRouting å’Œ UseAuthentication 之间注册 app.UseCorsAccessor(); // å¯ç”¨é‰´æƒæŽˆæƒ app.UseAuthentication(); app.UseAuthorization(); // 陿µç»„件(在跨域之åŽï¼‰ app.UseIpRateLimiting(); app.UseClientRateLimiting(); // ä»»åŠ¡è°ƒåº¦çœ‹æ¿ app.UseScheduleUI(options => { options.RequestPath = "/schedule"; // 必须以 / 开头且ä¸ä»¥ / 结尾 options.DisableOnProduction = true; // ç”Ÿäº§çŽ¯å¢ƒå…³é— options.DisplayEmptyTriggerJobs = true; // æ˜¯å¦æ˜¾ç¤ºç©ºä½œä¸šè§¦å‘器的作业 options.DisplayHead = false; // æ˜¯å¦æ˜¾ç¤ºé¡µå¤´ options.DefaultExpandAllJobs = false; // 是å¦é»˜è®¤å±•开所有作业 }); // é…ç½®Swagger-Knife4UI(路由å‰ç¼€ä¸€è‡´ä»£è¡¨ç‹¬ç«‹ï¼Œä¸åŒåˆ™ä»£è¡¨å…±å˜ï¼‰ app.UseKnife4UI(options => { options.RoutePrefix = "kapi"; foreach (var groupInfo in SpecificationDocumentBuilder.GetOpenApiGroups()) { options.SwaggerEndpoint("/" + groupInfo.RouteTemplate, groupInfo.Title); } }); app.UseInject(string.Empty, options => { foreach (var groupInfo in SpecificationDocumentBuilder.GetOpenApiGroups()) { //groupInfo.Description += "<br/><u><b><font color='FF0000'> 👮ä¸å¾—利用本项目从事å±å®³å›½å®¶å®‰å…¨ã€æ‰°ä¹±ç¤¾ä¼šç§©åºã€ä¾µçŠ¯ä»–äººåˆæ³•æƒç›Šç‰æ³•å¾‹æ³•è§„ç¦æ¢çš„æ´»åЍï¼ä»»ä½•基于本项目二次开å‘è€Œäº§ç”Ÿçš„ä¸€åˆ‡æ³•å¾‹çº çº·å’Œè´£ä»»ï¼Œæˆ‘ä»¬ä¸æ‰¿æ‹…任何责任ï¼</font></b></u>"; } }); app.UseEndpoints(endpoints => { // 注册集线器 endpoints.MapHubs(); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } }