Startup.cs 14.9 KB
using Autofac.Extensions.DependencyInjection;
using Hh.Mes.Common.config;
using Hh.Mes.Common.log;
using Hh.Mes.Service;
using Hh.Mes.Service.Repository;
using Hh.Mes.Service.SystemAuth;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Linq;
using Quartz;
using Quartz.AspNetCore;
using Quartz.AspNetCore.Logging;
using Quartz.Job.Jobs;
using Quartz.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using WebMvc.Aop;

namespace WebMvc
{
    public class Startup
    {
        public IConfiguration Configuration { get; }
        public IWebHostEnvironment webHostEnvironment { get; set; }

        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;
            webHostEnvironment = env;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            #region 自定义配置appsettings.json 节点 AppSettings.SetAppSetting(Configuration.GetSection("AppCustomSettings"));
            AppSettings.SetAppSetting(Configuration.GetSection("AppCustomSettings"));
            ConfigRead.GetInstance.GetAppsetConnection().AppCustomExtend1 = AppSettings.GetAppSeting("AppCustomExtend1");
            ConfigRead.GetInstance.GetAppsetConnection().AppCustomExtend2 = AppSettings.GetAppSeting("AppCustomExtend2");
            ConfigRead.GetInstance.GetAppsetConnection().AppCustomExtend3 = AppSettings.GetAppSeting("AppCustomExtend3");
            ConfigRead.GetInstance.GetAppsetConnection().AppCustomExtend4 = AppSettings.GetAppSeting("AppCustomExtend4");

            ConfigRead.GetInstance.GetAppsetConnection().AppCustomExtend5 = AppSettings.GetAppSeting("AppCustomExtend5");
            ConfigRead.GetInstance.GetAppsetConnection().AppCustomExtend6 = AppSettings.GetAppSeting("AppCustomExtend6");
            ConfigRead.GetInstance.GetAppsetConnection().AppCustomExtend7 = AppSettings.GetAppSeting("AppCustomExtend7");
            ConfigRead.GetInstance.GetAppsetConnection().AppCustomExtend8 = AppSettings.GetAppSeting("AppCustomExtend8");
            #endregion

            services.Configure<FormOptions>(options =>
            {
                options.ValueCountLimit = int.MaxValue;
                options.ValueLengthLimit = int.MaxValue;
                options.KeyLengthLimit = int.MaxValue;
                options.MultipartBodyLengthLimit = int.MaxValue;
                options.MultipartBoundaryLengthLimit = int.MaxValue;
                //解决文件上传Request body too large
                options.MultipartBodyLengthLimit = 268435456;
            });

            services.AddControllersWithViews(option =>
            {
                option.EnableEndpointRouting = false;
                option.ModelBinderProviders.Insert(0, new JsonBinderProvider());
                //加入全局异常类
                option.Filters.Add<HttpGlobalExceptionFilter>();
            });

            //Asp.Net Core获取请求上下文HttpContext https://www.cnblogs.com/tianma3798/p/10361644.html
            services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();

            //内存缓存
            services.AddDistributedMemoryCache();

            //Redis缓存
            //services.AddStackExchangeRedisCache(options =>
            //{
            //    options.Configuration = Configuration.GetConnectionString("RedisIp");
            //    options.InstanceName = "SampleInstance";
            //});
             
            services.AddOptions();

            #region HTTPS
            //services.AddMvc(options =>
            //{
            //    options.SslPort = ConfigRead.GetInstance.GetAppsetConnection().WebPort;
            //    options.Filters.Add(new RequireHttpsAttribute());
            //});

            //services.AddAntiforgery(
            //    options =>
            //    {
            //        options.Cookie.Name = "_af";
            //        options.Cookie.HttpOnly = true;
            //        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            //        options.HeaderName = "X-XSRF-TOKEN";
            //    });
            #endregion

            //数据库连接地址
            var connectionString = ConfigRead.GetInstance.GetAppsetConnection().BaseDBContext;

            #region 注册Quartz中间件
            services.AddQuartz(options =>
            {
                options.UseSqlServer(connectionString);
                options.UseProperties(false);
            });
            #endregion


            #region 注册SqlSugars、操作日志、XSS攻击防御等
            //SqlSugars
            services.BatchRegisterService(Program.service, Program.serviceSuffix);
            services.AddDirectoryBrowser();

            //操作日志
            services.AddScoped<OperLogFilter>();
            //services.AddScoped<InterfaceLogFilter>();
            //xss攻击防御
            services.AddScoped<XSSFilterAttribute>();
            #endregion

            #region 注入登录、授权等服务

            services.AddScoped<LoginParse>();
            services.AddScoped<AuthContextFactory>();
            services.AddScoped<SystemAuthStrategy>();
            services.AddScoped<NormalAuthStrategy>();
            services.AddScoped(typeof(IAuth), typeof(LocalAuth));
            #endregion

            //解决开发环境修改html,需要重新编译启动程序,编译发布环境不需要  https://www.cnblogs.com/Can-daydayup/p/13630050.html#!comments
            if (webHostEnvironment.IsDevelopment()) services.AddRazorPages().AddRazorRuntimeCompilation();

            //使用AutoFac进行注入
            var serviceProvider = new AutofacServiceProvider(AutofacExt.InitAutofac(services));

            //Redis 设置缓存
            Task.Factory.StartNew(() =>
            {
                var baseInfoCacheService = serviceProvider.GetService<BaseInfoCacheService>();
                baseInfoCacheService.SetBaseInfoCacheFirst();
            });
            return serviceProvider;
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                ConfigRead.GetInstance.GetAppsetConnection().IsDevelopment = false;
                app.UseDeveloperExceptionPage();
            }
            else
            {
                if (IsExtSysFile()) return;
                ConfigRead.GetInstance.GetAppsetConnection().IsDevelopment = true;
                app.UseExceptionHandler("/htmlTemp");
            }
            app.UseStaticFiles();
            Console.ResetColor();

            #region apk https://www.cnblogs.com/1175429393wljblog/p/8624679.html
            app.UseStaticFiles(
                new StaticFileOptions
                {
                    ContentTypeProvider = new FileExtensionContentTypeProvider(new Dictionary<string, string>
                    {
                                  { ".apk", "application/vnd.android.package-archive" }
                    })
                });
            #endregion


            #region 启用Quartz中间件 开发环境不启用定时器,如果需要开启 isOpenForDev 设置true ,发布时默认值false
            var isOpenForDev = false;
            var isDevelopment = ConfigRead.GetInstance.GetAppsetConnection().IsDevelopment;
            if (isDevelopment)
            {
                InitialQuartzAndData(app, true);
            }
            else
            {
                InitialQuartzAndData(app, isOpenForDev);
            }
            Console.WriteLine();

            #endregion

            app.UseMvcWithDefaultRoute();

            #region http 500
            app.UseExceptionHandler(errorApp =>
            {
                errorApp.Run(async context =>
                {
                    context.Response.StatusCode = 500;
                    // 检查请求是否为 Ajax 请求
                    if (context.Request.Headers["X-Requested-With"] != "XMLHttpRequest")
                    {
                        context.Response.ContentType = "text/html";
                        await context.Response.SendFileAsync($@"{env.WebRootPath}/htmlTemp/500.html");
                    }
                });
            });
            app.UseStatusCodePagesWithReExecute("/htmlTemp/{0}");
            #endregion

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                  name: "areas",
                  template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
                );
            });
        }

        #region 自定义方法
        private bool IsExtSysFile()
        {
            var webMvcPath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "WebMvc.xml");
            if (!File.Exists(webMvcPath))
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("WebMvc.xml 文件路径不存在,请在bin/Debug 手动拷贝复制该文件到发布目录下面(和appsettings.json平级)!:" + webMvcPath);
                Log4NetHelper.Instance.Info("WebMvc path:" + webMvcPath);
                Console.ReadLine();
                return true;
            }
            var wwwRootPath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "wwwroot");
            if (!Directory.Exists(wwwRootPath))
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("wwwroot 文件夹不存在,请手动拷贝复制该文件到发布目录下面(和appsettings.json平级)!:" + webMvcPath);
                Log4NetHelper.Instance.Info("wwwroot path:" + wwwRootPath);
                Console.ReadLine();
                return true;
            }

            var aaptPath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "tools", "aapt.exe");
            if (!File.Exists(aaptPath))
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("aapt.exe 文件路径不存在,请在根目录tools/aapt.exe手动拷贝复制该文件到发布目录下面(和appsettings.json平级)!:" + aaptPath);
                Log4NetHelper.Instance.Info("aaptPath path:" + aaptPath);
                Console.ReadLine();
                return true;
            }
            var unzipPath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "tools", "unzip.exe");
            if (!File.Exists(unzipPath))
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("unzip.exe 文件路径不存在,请在根目录tools/unzip.exe手动拷贝复制该文件到发布目录下面(和appsettings.json平级)!:" + aaptPath);
                Log4NetHelper.Instance.Info("unzip path:" + unzipPath);
                Console.ReadLine();
                return true;
            }

            return false;
        }

        #region 初始化Quartz组件,添加定时任务数据。解决服务程序重启后定时任务无法执行问题。
        /// <summary>
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="isDevelopment"> true是生产环境,false是开发环境</param>
        /// <param name="isOpen">true开启定时器,有时候开发环境需要测试,默认是false</param>
        /// <returns></returns>
        public static IApplicationBuilder InitialQuartzAndData(IApplicationBuilder builder, bool isDevelopment, bool isOpen = false)
        {
            LogProvider.SetCurrentLogProvider(builder.ApplicationServices.GetRequiredService<LoggingProvider>());
            var sched = builder.ApplicationServices.GetRequiredService<IScheduler>();
            var sysJobRepository = new SysJobRepository();
            var sysJobs = sysJobRepository.GetList(x => x.status == "0");

            foreach (var item in sysJobs)
            {
                try
                {
                    var jobDataMap = CreateJobDataMap(item.methodParams);
                    var classType = LoadJobClass(item.methodName);

                    if (classType != null)
                    {
                        IJobDetail job = JobBuilder.Create(classType)
                                                   .WithIdentity(item.jobName)
                                                   .UsingJobData(jobDataMap)
                                                   .Build();

                        // 创建并调度触发器
                        var trigger = TriggerBuilder.Create()
                                                    .WithIdentity(item.jobName)
                                                    .WithCronSchedule(item.cronExpression)
                                                    .Build();

                        // 删除旧任务并调度新任务
                        sched.DeleteJob(new JobKey(item.jobName)).Wait();
                        sched.ScheduleJob(job, trigger).Wait();
                    }
                }
                catch (Exception ex)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($"创建定时任务[{item.jobName}]异常:{ex.Message}");
                    Console.ResetColor();
                }
            }

            // 启动调度器
            if (isOpen || isDevelopment)
            {
                sched.Start();
            }

            // 输出提示信息
            LogSchedulerStatus(isDevelopment, isOpen);

            return builder;
        }

        private static JobDataMap CreateJobDataMap(string methodParams)
        {
            var jobDataMap = new JobDataMap();
            var json = JObject.Parse(methodParams);
            foreach (var v in json)
            {
                jobDataMap.Add(v.Key, v.Value.ToString());
            }
            return jobDataMap;
        }

        private static Type LoadJobClass(string methodName)
        {
            return Assembly.Load(typeof(JobBase).Assembly.FullName).GetType($"{typeof(JobBase).Namespace}.{methodName}");
        }

        private static void LogSchedulerStatus(bool isDevelopment, bool isOpen)
        {
            Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.Green;
            if (isDevelopment)
            {

                Console.WriteLine("友好提示: webApp线上环境已开启定时器作业任务!");
            }
            else
            {
                if (isOpen)
                {
                    Console.WriteLine("友好提示: webApp线下环境已开启定时器作业任务!");
                }
                else
                {
                    Console.WriteLine("友好提示: 基础版本扩展新项目后需要重新新建数据库");
                    Console.WriteLine("友好提示: webApp线下环境未开启定时器作业任务!");
                }
            }
            Console.ResetColor();
        } 
        #endregion
        #endregion
    }
}