// 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?}");
});
}
}