中间件订购

Ech*_*lon 5 asp.net .net-core asp.net-core asp.net-core-3.1

我有一个新的 .NET Core 3.1 应用程序,并且正在努力解决中间件的概念。通过阅读,似乎包含不同中间件的顺序很重要。我目前有几个似乎无法解决的问题:

  1. 我从来没有看到开发人员错误页面,并且必须检查事件日志以查看发生错误时发生的情况。我刚刚从 Chrome 中得到空白的“错误 500”等页面。当出现 500/400 时,自定义错误页面也不会显示。
  2. 尽管在 cookie 设置中更改了此设置,但该应用程序始终尝试将我重定向到/Account/Login 。
  3. 在 Elmah 中进行CheckPermissionsAction调用时, User.IsAuthenticated返回 false ,因此我无法访问 Elmah。不过,User.IsInRole调用适用于控制器。

这就是我引导应用程序的方式。感觉就像有什么东西覆盖了设置:

public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<DataProtectionTokenProviderOptions>(options =>
            options.TokenLifespan = TimeSpan.FromDays(2));

        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromMinutes(30);
        });
        services.AddControllersWithViews();
        services.AddTransient<IUserStore<User>, UserStore>();
        services.AddTransient<IRoleStore<IdentityRole>, RoleStore>();
        services.AddRazorPages();
        

        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(options =>
            {
                options.LoginPath = new PathString("/login");
                options.AccessDeniedPath = new PathString("/error/denied");
                options.LogoutPath = new PathString("/log-off");
                options.ExpireTimeSpan = TimeSpan.FromDays(60);
                options.SlidingExpiration = true;
                options.Cookie.HttpOnly = true;
                options.Cookie.Name = "MyCookie";
                options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
                options.Cookie.SameSite = SameSiteMode.Lax;
            });
        services.AddIdentity<User, IdentityRole>(options =>
        {
            options.Password.RequireDigit = true;
            options.Password.RequiredLength = 6;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = false;
            options.Password.RequireLowercase = false;
        })
        .AddUserStore<UserStore>()
        .AddRoleStore<RoleStore>()
        .AddDefaultTokenProviders();
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => false;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.AddElmah<SqlErrorLog>(options =>
        {
            options.ConnectionString = Configuration.GetConnectionString("MyApp");
            options.CheckPermissionAction = (context)=>{
                return context.User.Identity.IsAuthenticated && context.User.IsInRole(RoleHelper.SuperAdmin);
            };
            options.Path = "/elmah";
        });
        services.AddSingleton<IAppConfiguration, AppConfiguration>(e => Configuration.GetSection("AppConfig")
           .Get<AppConfiguration>());

        OptionsConfigurationServiceCollectionExtensions.Configure<DbHelper>(services, Configuration.GetSection("ConnectionStrings"));
        services.AddHttpContextAccessor();
    }

    public void ConfigureContainer(ContainerBuilder builder)
    {
        // wire up using autofac specific APIs here
        builder.Register(context => new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<User, MyDetailsViewModel>();
        })).AsSelf().SingleInstance();
        builder.RegisterModule(new RegistrationModule()); // separate assembly, wires up autofac registrations
        builder.Register(c =>
        {
            //This resolves a new context that can be used later.
            var context = c.Resolve<IComponentContext>();
            var config = context.Resolve<MapperConfiguration>();
            return config.CreateMapper(context.Resolve);
        })
        .As<IMapper>()
        .InstancePerLifetimeScope();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            // debugger shows this section is called, but I never see the error page.
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
            app.UseRouteDebugger();
        }
        else
        {
            app.UseExceptionHandler("/error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseSession();
        app.UseElmah();
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();
        var cookiePolicyOptions = new CookiePolicyOptions
        {
            Secure = CookieSecurePolicy.SameAsRequest,
            MinimumSameSitePolicy = SameSiteMode.None
        };
        app.UseCookiePolicy(cookiePolicyOptions);
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Guest}/{action=Index}/{id?}");
            endpoints.MapRazorPages();
            endpoints.MapControllers();
        });

        app.UseStatusCodePages(async ctx =>
        {
            //Re-execute the request so the user gets the error page
            string originalPath = ctx.HttpContext.Request.Path.Value;
            switch (ctx.HttpContext.Response.StatusCode)
            {
                case 401:
                    //Re-execute the request so the user gets the error page
                    ctx.HttpContext.Items["originalPath"] = originalPath;
                    ctx.HttpContext.Request.Path = "/error/denied";
                    break;
                case 412:
                    ctx.HttpContext.Items["originalPath"] = originalPath;
                    ctx.HttpContext.Request.Path = "/error/expired-account";
                    break;
                case 404:
                    ctx.HttpContext.Items["originalPath"] = originalPath;
                    ctx.HttpContext.Request.Path = "/error/not-found";
                    break;
                case 500:
                    ctx.HttpContext.Items["originalPath"] = originalPath;
                    ctx.HttpContext.Request.Path = "/error/not-found";
                    break;
            }
        });
        DapperExtensions.DapperExtensions.SetMappingAssemblies(new[]
            {
                 Assembly.GetAssembly(typeof(MyApp.Domain.Model.Note)),
                 Assembly.GetExecutingAssembly()
        });
    }
Run Code Online (Sandbox Code Playgroud)

Den*_* W. 9

关于你的中间件的顺序,有问题。

微软文档中有一节专门介绍中间件的顺序,我建议阅读它。

至于您的中间件,正确的顺序是:

        app.UseHttpsRedirection();
        app.UseStatusCodePages(async ctx =>
        {
            // Omitted for brevity.
        });

        app.UseStaticFiles();

        var cookiePolicyOptions = new CookiePolicyOptions
        {
            // Omitted for brevity.
        };
        app.UseCookiePolicy(cookiePolicyOptions);

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        // If the app uses session state, call Session Middleware after Cookie
        // Policy Middleware and before MVC Middleware.
        app.UseSession();

        app.UseElmah(); // Not sure about this one. I don't know what it's supposed to do?

        app.UseEndpoints(endpoints =>
        {
            // Omitted for brevity.
        });

        DapperExtensions.DapperExtensions.SetMappingAssemblies(new[]
        {
            // Omitted for brevity.
        });
Run Code Online (Sandbox Code Playgroud)