You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server应用局域网IP访问时Cookie与语言切换失效问题

问题描述

开发了一个多语言DevExpress XAF Blazor Server应用,使用http://localhost:8080或http://127.0.0.1:8080访问时,所有功能正常,Cookie可正常设置,语言也能切换;但通过http://192.168.178.200:8080本地或跨设备访问时,Cookie无法设置,语言切换操作仅会刷新页面,无任何变化。

现有代码

Startup.cs

public class Startup {
    public Startup(IConfiguration configuration) {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services) {
        services.AddSingleton(typeof(Microsoft.AspNetCore.SignalR.HubConnectionHandler<>), typeof(ProxyHubConnectionHandler<>));
        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddHttpContextAccessor();
        services.AddScoped<CircuitHandler, CircuitHandlerProxy>();
        services.AddXaf(Configuration, builder => {
            builder.UseApplication<TestBlazorApplication>();
            builder.Services.AddDevExpressServerSideBlazorPdfViewer();
            builder.Modules
                .AddAuditTrailXpo()
                .AddCloningXpo()
                .AddConditionalAppearance()
                .AddDashboards(options => {
                    options.DashboardDataType = typeof(DevExpress.Persistent.BaseImpl.DashboardData);
                })
                .AddFileAttachments()
                .AddNotifications()
                .AddOffice(options => options.RichTextMailMergeDataType = typeof(RichTextMailMergeData))
                .AddReports(options => {
                    options.EnableInplaceReports = true;
                    options.ReportDataType = typeof(DevExpress.Persistent.BaseImpl.ReportDataV2);
                    options.ReportStoreMode = DevExpress.ExpressApp.ReportsV2.ReportStoreModes.XML;
                })
                .AddScheduler()
                .AddStateMachine(options => {
                    options.StateMachineStorageType = typeof(DevExpress.ExpressApp.StateMachine.Xpo.XpoStateMachine);
                })
                .AddValidation(options => {
                    options.AllowValidationDetailsAccess = false;
                })
                .AddViewVariants()
                .Add<Test.Module.TestModule>()
                .Add<TestBlazorModule>();
            builder.ObjectSpaceProviders
                .AddSecuredXpo((serviceProvider, options) => {
                    string connectionString = null;

                    if (Configuration.GetConnectionString("ConnectionString") != null)
                    {
                        connectionString = Configuration.GetConnectionString("ConnectionString");
                    }

                    ArgumentNullException.ThrowIfNull(connectionString);
                    options.ConnectionString = connectionString;
                    options.ThreadSafe = true;
                    options.UseSharedDataStoreProvider = true;
                })
                .AddNonPersistent();
            builder.Security
                .UseIntegratedMode(options => {
                    options.Lockout.Enabled = true;

                    options.RoleType = typeof(Test.Module.BusinessObjects.ApplicationPermissionPolicyRole);
                    options.UserType = typeof(Test.Module.BusinessObjects.ApplicationUser);
                    options.UserLoginInfoType = typeof(Test.Module.BusinessObjects.ApplicationUserLoginInfo);
                    options.UseXpoPermissionsCaching();
                    options.Events.OnSecurityStrategyCreated += securityStrategy => {
                        ((SecurityStrategy)securityStrategy).PermissionsReloadMode = PermissionsReloadMode.NoCache;
                    };
                })
                .AddPasswordAuthentication(options => {
                    options.IsSupportChangePassword = true;
                });
        });
        var authentication = services.AddAuthentication(options => {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        });
        authentication.AddCookie(options => {
            options.LoginPath = "/LoginPage";
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
        if(env.IsDevelopment()) {
            app.UseDeveloperExceptionPage();
        }
        else {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }
        app.UseHttpsRedirection();
        app.UseRequestLocalization();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseXaf();
        app.UseEndpoints(endpoints => {
            endpoints.MapXafEndpoints();
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
            endpoints.MapControllers();
        });
    }
}

appsettings.json

{
  "ConnectionStrings": {
    "ConnectionString": ""
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "DevExpress.ExpressApp": "Information"
    }
  },
  "AllowedHosts": "*",
  "DevExpress": {
    "ExpressApp": {
      "Languages": "de-DE;en-US;",
      "ShowLanguageSwitcher": true,
      "ThemeSwitcher": {
        "DefaultItemName": "Blazing Dark",
        "ShowSizeModeSwitcher": true,
        "Groups": [
          {
            "Caption": "DevExpress Themes",
            "Items": [
              {
                "Caption": "Blazing Berry",
                "Url": "_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css",
                "Color": "#5c2d91"
              },
              {
                "Caption": "Blazing Dark",
                "Url": "_content/DevExpress.Blazor.Themes/blazing-dark.bs5.min.css",
                "Color": "#46444a"
              },
              {
                "Caption": "Office White",
                "Url": "_content/DevExpress.Blazor.Themes/office-white.bs5.min.css",
                "Color": "#fe7109"
              },
              {
                "Caption": "Purple",
                "Url": "_content/DevExpress.Blazor.Themes/purple.bs5.min.css",
                "Color": "#7989ff"
              }
            ]
          }
        ]
      }
    }
  }
}

Program.cs

public class Program : IDesignTimeApplicationFactory {
    private static bool ContainsArgument(string[] args, string argument) {
        return args.Any(arg => arg.TrimStart('/').TrimStart('-').ToLower() == argument.ToLower());
    }
    public static int Main(string[] args) {

        Tracing.LogName = "TestLogFile";

        if (ContainsArgument(args, "help") || ContainsArgument(args, "h")) {
            Console.WriteLine("Updates the database when its version does not match the application's version.");
            Console.WriteLine();
            Console.WriteLine($"    {Assembly.GetExecutingAssembly().GetName().Name}.exe --updateDatabase [--forceUpdate --silent]");
            Console.WriteLine();
            Console.WriteLine("--forceUpdate - Marks that the database must be updated whether its version matches the application's version or not.");
            Console.WriteLine("--silent - Marks that database update proceeds automatically and does not require any interaction with the user.");
            Console.WriteLine();
            Console.WriteLine($"Exit codes: 0 - {DBUpdaterStatus.UpdateCompleted}");
            Console.WriteLine($"            1 - {DBUpdaterStatus.UpdateError}");
            Console.WriteLine($"            2 - {DBUpdaterStatus.UpdateNotNeeded}");
        }
        else {
            DevExpress.ExpressApp.FrameworkSettings.DefaultSettingsCompatibilityMode = DevExpress.ExpressApp.FrameworkSettingsCompatibilityMode.Latest;
            DevExpress.ExpressApp.Security.SecurityStrategy.AutoAssociationReferencePropertyMode = DevExpress.ExpressApp.Security.ReferenceWithoutAssociationPermissionsMode.AllMembers;
            IHost host = CreateHostBuilder(args).Build();
            if(ContainsArgument(args, "updateDatabase")) {
                using(var serviceScope = host.Services.CreateScope()) {
                    return serviceScope.ServiceProvider.GetRequiredService<DevExpress.ExpressApp.Utils.IDBUpdater>().Update(ContainsArgument(args, "forceUpdate"), ContainsArgument(args, "silent"));
                }
            }
            else {
                host.Run();
            }
        }
        return 0;
    }
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder => {
                webBuilder.UseStartup<Startup>();
            });
    XafApplication IDesignTimeApplicationFactory.Create() {
        IHostBuilder hostBuilder = CreateHostBuilder(Array.Empty<string>());
        return DesignTimeApplicationFactoryHelper.Create(hostBuilder);
    }
}

解决方案

1. 调整Cookie认证配置

在Startup.cs的AddCookie配置中,添加适配局域网IP的Cookie属性,确保Cookie能被正确识别:

authentication.AddCookie(options => {
    options.LoginPath = "/LoginPage";
    // 自动适配当前请求的域名/IP,无需硬编码
    options.Cookie.Domain = null;
    // 宽松模式支持跨站点请求
    options.Cookie.SameSite = SameSiteMode.Lax;
    // 开发环境禁用HTTPS要求,生产环境改为CookieSecurePolicy.Always
    options.Cookie.SecurePolicy = CookieSecurePolicy.None;
});

2. 完善本地化请求配置

当前UseRequestLocalization()未指定支持语言,导致语言切换Cookie无法生效,修改如下:
在ConfigureServices中添加本地化服务配置:

services.Configure<RequestLocalizationOptions>(options => {
    var supportedCultures = new[] {
        new CultureInfo("de-DE"),
        new CultureInfo("en-US")
    };
    options.DefaultRequestCulture = new RequestCulture("en-US");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
    // 指定XAF语言切换使用的Cookie名称
    options.RequestCultureProviders.Insert(0, new CookieRequestCultureProvider {
        CookieName = "XafCulture"
    });
});

然后在Configure方法中替换原有的UseRequestLocalization():

app.UseRequestLocalization(app.ApplicationServices.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);

3. 确保应用监听所有网络接口

在Program.cs的CreateHostBuilder中添加监听配置,让应用能响应局域网IP的请求:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder => {
            webBuilder.UseStartup<Startup>();
            // 监听所有IP的8080端口
            webBuilder.UseUrls("http://0.0.0.0:8080");
        });

4. 修正语言列表配置

去掉appsettings.json中Languages末尾的多余分号:

"Languages": "de-DE;en-US"

验证效果

  1. 重启应用后,用http://192.168.178.200:8080访问
  2. 打开浏览器开发者工具,查看Application->Cookies下是否存在XafCulture及认证Cookie
  3. 尝试切换语言,检查Cookie是否更新,页面语言是否正常切换

内容的提问来源于stack exchange,提问作者Profiler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 14:14:53