using Hangfire; using Hangfire.MemoryStorage; using Infrastructure.Application.Services.Settings; using Infrastructure.Data; using Infrastructure.Events; using Infrastructure.Extensions; using Infrastructure.Office; using Infrastructure.Security; using Infrastructure.UI; using Infrastructure.Web.Authentication.Cookies; using Infrastructure.Web.Mvc.ModelBinding.Metadata; using Infrastructure.Web.SignalR; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.ResponseCompression; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Newtonsoft.Json; using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.Encodings.Web; using System.Text.Unicode; using System.Threading.Tasks; namespace Infrastructure.Web { public class BaseStartup { protected readonly IConfiguration cfg; protected readonly IWebHostEnvironment env; private string _connectionString; private readonly string _origins = "AllowAllHeaders"; public BaseStartup(IConfiguration configuration, IWebHostEnvironment env) { this.cfg = configuration; this.env = env; } public virtual void ConfigureServices(IServiceCollection services) { if (!this.env.IsDevelopment()) { services.AddResponseCompression(options => { options.Providers.Add(); options.Providers.Add(); }); } services.AddTransient(); services.AddTransient(); services.AddSingleton(cfg as IConfigurationRoot); services.AddCors(options => options.AddPolicy(_origins, builder => { builder.SetIsOriginAllowed(o => true) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); })); services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All)); services.Configure(o => { o.ValueCountLimit = int.MaxValue; o.MultipartBodyLengthLimit = long.MaxValue; }); this.ConfigureOptions(services); EfDbContext.OnModelCreatingAction = this.OnModelCreating; services.AddScoped(); var dbConnectionName = this.cfg.GetSection("AppSettings").GetValue("database"); _connectionString = this.cfg.GetConnectionString(dbConnectionName); if (dbConnectionName == "sqlite") { services.AddDbContext(options => options.UseSqlite(_connectionString)); } else if (dbConnectionName == "mysql") { services.AddDbContext(options => options.UseMySql(_connectionString)); } else { services.AddDbContext(options => options.UseNpgsql(_connectionString)); } //https://github.com/aspnet/Entropy/blob/master/samples/Localization.StarterWeb/Startup.cs services.AddLocalization(options => options.ResourcesPath = null); //https://yetawf.com/BlogEntry/Title/AdditionalMetadataAttribute%20Anyone/?BlogEntry=1005 services.AddMvc(o => o.ModelMetadataDetailsProviders.Add(new AdditionalMetadataProvider())) .SetCompatibilityVersion(CompatibilityVersion.Latest) .AddNewtonsoftJson() .AddControllersAsServices() .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix) .AddDataAnnotationsLocalization(options => { options.DataAnnotationLocalizerProvider = (type, factory) => { var localizer = factory.Create("Resources.Resource", Assembly.GetEntryAssembly().FullName); return localizer; }; }); services.AddApiVersioning(o => { o.ReportApiVersions = true; o.AssumeDefaultVersionWhenUnspecified = true; o.DefaultApiVersion = new ApiVersion(1, 0); o.ApiVersionReader = ApiVersionReader.Combine( new UrlSegmentApiVersionReader() //,new QueryStringApiVersionReader() //,new HeaderApiVersionReader() ); }); services.AddControllers() .ConfigureApiBehaviorOptions(options => { options.SuppressConsumesConstraintForFormFileParameters = true; options.SuppressInferBindingSourcesForParameters = true; options.SuppressModelStateInvalidFilter = true; options.SuppressMapClientErrors = true; //options.ClientErrorMapping[404].Link = "https://httpstatuses.com/404"; }) .AddNewtonsoftJson(o => { o.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); services.Configure(o => { var supportedCultures = new[] { new CultureInfo("zh-CN"), new CultureInfo("en-US") }; o.DefaultRequestCulture = new RequestCulture(culture: "zh-CN", uiCulture: "zh-CN"); o.SupportedCultures = supportedCultures; o.SupportedUICultures = supportedCultures; }); this.AddAuthentication(services); //services.AddAuthorization(); this.AddSwagger(services); var cache = this.cfg.GetSection("AppSettings").GetValue("cache", "memory"); if (cache == "redis") { services.AddStackExchangeRedisCache(o => { o.Configuration = cfg.GetConnectionString("redis"); o.InstanceName = "iot"; }); } else { services.AddDistributedMemoryCache(); } services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; }); services.AddHttpClient(); var signalRBuilder = services .AddSignalR(o => o.EnableDetailedErrors = true) .AddJsonProtocol(); if (cfg.GetValue("useRedisSignalR", false)) { signalRBuilder = signalRBuilder.AddStackExchangeRedis(cfg.GetConnectionString("redis"), o => { o.Configuration.ChannelPrefix = "iot"; //o.ConnectionFactory = async writer => //{ // var config = new ConfigurationOptions // { // AbortOnConnectFail = false // }; // config.EndPoints.Add(IPAddress.Loopback, 0); // config.SetDefaultPorts(); // var connection = await ConnectionMultiplexer.ConnectAsync(config, writer); // connection.ConnectionFailed += (_, e) => // { // Console.WriteLine("Connection to Redis failed."); // }; // if (!connection.IsConnected) // { // Console.WriteLine("Did not connect to Redis."); // } // return connection; //}; }); } this.UseScheduler(services); services.AddMemoryCache(); services.AddHttpContextAccessor(); services.AddSingleton(); services.AddTransient(typeof(IRepository<>), typeof(EfRepository<>)); services.AddTransient(); services.AddTransient(); AppDomain.CurrentDomain.GetAssemblies().SelectMany(o => o.GetTypes()) .Where(t => t.GetInterfaces().Any(o => o.IsGenericType && o.GetGenericTypeDefinition() == typeof(IEventHander<>))) .ToList() .ForEach(t => { t.GetInterfaces().Where(o => o.IsGenericType && o.GetGenericTypeDefinition() == typeof(IEventHander<>)).ToList().ForEach(o => { services.AddTransient(o, t); }); }); services.AddSingleton(); if (cfg.GetValue("useServiceServer", false)) { services.AddNacosAspNetCore(cfg); } } public virtual void UseScheduler(IServiceCollection services) { services.AddHangfire(x => x.UseMemoryStorage()); } public virtual void ConfigureOptions(IServiceCollection services) { services.ConfigureOptions(new FileConfigureOptions(this.env)); } public virtual void AddAuthentication(IServiceCollection services) { services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { services.AddSingleton(o); o.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(cfg["jwt:key"])), ValidateIssuer = false, ValidIssuer = cfg["jwt:issuer"], ValidateAudience = false, ValidAudience = cfg["jwt:audience"] }; o.Events = new JwtBearerEvents { OnTokenValidated = context => { if (DateTime.UtcNow > context.SecurityToken.ValidTo) { context.Fail(""); } return Task.CompletedTask; }, OnForbidden = context => { return Task.CompletedTask; }, OnAuthenticationFailed = context => { return Task.CompletedTask; }, OnChallenge = context => { if (!context.Request.IsAjax()) { context.Response.Redirect("/UserCenter/Account/Login"); context.HandleResponse(); } return Task.CompletedTask; }, OnMessageReceived = context => { if (!context.Request.IsStatic()) { Debug.WriteLine(context.Request.Path); if (context.Request.Query.ContainsKey("access_token")) { context.Token = context.Request.Query["access_token"]; } var jwtCookieName = context.HttpContext.GetJwtCookieName(); if (!context.Request.Headers.ContainsKey("Authorization") && context.Request.Cookies.Keys.Contains(jwtCookieName)) { context.Token = context.Request.Cookies[jwtCookieName]; } } return Task.CompletedTask; } }; o.SecurityTokenValidators.Clear(); o.SecurityTokenValidators.Insert(0, new JwtTokenValidator(services.BuildServiceProvider())); }); } public virtual void AddSwagger(IServiceCollection services) { services.AddSwaggerGen(c => { c.SwaggerDoc(this.cfg.GetValue("openapi.name", "v1"), new OpenApiInfo { Title = this.cfg.GetValue("openapi.title", "web api"), Version = this.cfg.GetValue("openapi.version", "1.0") }); c.EnableAnnotations(); }); } public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { if (app == null) { throw new ArgumentNullException(nameof(app)); } if (!env.IsDevelopment()) { app.UseResponseCompression(); } if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseStatusCodePagesWithReExecute("/Error"); } string basePath = this.cfg.GetValue("BasePath", ""); app.UsePathBase(basePath); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All }); app.UseStaticFiles(); app.UseRouting(); app.UseCors(_origins); var localizationOption = app.ApplicationServices.GetService>(); app.UseRequestLocalization(localizationOption.Value); app.UseSession(); this.UseAuthentication(app); this.UseSwagger(app); this.UseScheduler(app); app.UseEndpoints(endpoints => { this.UseSignalR(endpoints); endpoints.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); using var scope = app.ApplicationServices.CreateScope(); var services = scope.ServiceProvider; this.CreateDatabase(services); if (cfg.GetValue("useServiceServer", false)) { app.UseNacosAspNetCore(); } } public virtual void CreateDatabase(IServiceProvider services) { var context = services.GetService(); if (context.Database.EnsureCreated()) { var sql = context.GetService().GenerateCreateScript(); var file = "db.sql"; if (File.Exists(file)) { File.Delete(file); } using (var fs = File.CreateText(file)) { fs.Write(sql); } this.Seed(context, services, this.cfg); } } public virtual void UseAuthentication(IApplicationBuilder app) { app.UseAuthentication(); app.UseAuthorization(); } public virtual void UseSwagger(IApplicationBuilder app) { app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("v1/swagger.json", "API V1"); }); } public virtual void UseScheduler(IApplicationBuilder app) { app.UseHangfireDashboard(); app.UseHangfireServer(); } public virtual void UseSignalR(IEndpointRouteBuilder endpoints) { this.UseSignalR(endpoints); } protected virtual void UseSignalR(IEndpointRouteBuilder endpoints) where T : Hub { endpoints.MapHub("/hub", o => { o.ApplicationMaxBufferSize = long.MaxValue; o.TransportMaxBufferSize = long.MaxValue; }); } public virtual void OnModelCreating(ModelBuilder modelBuilder) { } public virtual void Seed(DbContext dbContext, IServiceProvider serviceProvider, IConfiguration configuration) { } public virtual Task ValidatePrincipal(CookieValidatePrincipalContext arg) { return Task.CompletedTask; } public virtual Task TokenValidated(TokenValidatedContext arg) { return Task.CompletedTask; } } }