中间件排序

5
我有一个新的.NET Core 3.1应用程序,但我对中间件的概念感到困惑。从阅读资料来看,包含不同中间件的顺序很重要。我目前遇到了几个无法解决的问题:
  1. 如果出现错误,我永远看不到开发人员错误页面,必须检查事件日志才能知道发生了什么。当出现500/400错误时,我只会看到空白的“error 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()
        });
    }
1个回答

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.
        });

那个页面非常有用,谢谢。现在elmah访问的问题已经解决了,但是认证路径仍然是默认的,并且自定义错误页面也没有显示。开发人员错误页面也没有显示。据我所知,服务不需要按特定顺序初始化,只需要中间件? - Echilon
正确。服务在需要时被调用(通过依赖注入容器),中间件运行于每个请求,它是一条流动的管道。 - Dennis VW
@Echilon 我猜它没有触发开发者异常页面中间件,因为你正在使用 UseStatusCodePages 处理它。尝试注释掉 UseStatusCodePages 然后看看开发者异常中间件是否接管了处理。 - Dennis VW
提供的链接是用于 .NET Core 3.1。对于 .NET Core 6,请使用此链接: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0#middleware-order (或者只需更改下拉菜单中的版本) - Dodger
检查并更新新链接。谢谢Dodger。 - Dennis VW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接