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"
验证效果
- 重启应用后,用
http://192.168.178.200:8080访问 - 打开浏览器开发者工具,查看
Application->Cookies下是否存在XafCulture及认证Cookie - 尝试切换语言,检查Cookie是否更新,页面语言是否正常切换
内容的提问来源于stack exchange,提问作者Profiler
相关产品推荐
相关产品推荐

