部署IIS后Blazor WASM登录用户POST/PUT/DELETE请求CORS失效
Blazor WASM + ASP.NET Core API 部署IIS后登录状态下CORS/405问题
问题概况
- 技术栈:Blazor WebAssembly作为前端,标准C# REST API作为后端
- 异常现象:未登录时CORS正常工作;用户登录后,所有POST、PUT、DELETE方法的端点均返回
405 Method Not Allowed错误 - 环境差异:本地开发环境一切正常,部署到Windows Server的IIS服务器后问题出现
- 已排查:更换Chrome、Edge、Firefox等浏览器及不同设备测试,排除缓存旧配置的可能
相关请求截图描述
- 未登录用户的预检请求截图
- 登录用户的预检请求截图
- 最终405错误截图
后端核心配置代码
CORS及认证授权主配置
var builder = WebApplication.CreateBuilder(args); var corsSettings = builder.Configuration.GetSection(CorsSettings.SectionName).Get<CorsSettings>(); ArgumentNullException.ThrowIfNull(corsSettings, nameof(corsSettings)); builder.Services.AddCorsPolicy(corsSettings); [...] builder.Services.AddDbContext<AuthDbContext>(options => options.UseSqlite(connectionString)); builder.Services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<AuthDbContext>(); builder.Services.ConfigureApplicationCookie(options => { options.Cookie.HttpOnly = true; options.Cookie.Name = "GlossaryAuthCookie"; options.Cookie.SameSite = SameSiteMode.Lax; options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; options.Events.OnRedirectToLogin = context => { context.Response.StatusCode = 401; return Task.CompletedTask; }; }); builder.Services.AddAuthentication().AddCookie(); builder.Services.AddAuthorization(options => { options.AddPolicy(ClaimsNames.IsAdmin, policy => policy.RequireClaim(ClaimsNames.IsAdmin, ClaimsValues.True)); options.AddPolicy(ClaimsNames.IsEditor, policy => policy.RequireClaim(ClaimsNames.IsEditor, ClaimsValues.True)); options.AddPolicy(ClaimsNames.IsActive, policy => policy.RequireClaim(ClaimsNames.IsActive, ClaimsValues.True)); }); [...] var app = builder.Build(); app.UseRouting(); app.UseCors(corsSettings.PolicyName); app.UseAuthentication(); app.UseAuthorization(); app.UseSwagger(); app.UseSwaggerUI(); app.MapControllers(); app.MapGet("/", () => "PW Glossary API"); app.Run();
AddCorsPolicy扩展方法实现
serviceCollection.AddCors(options => { options.AddPolicy(corsSettings.PolicyName, policyBuilder => { policyBuilder.AllowCredentials(); if (corsSettings.AllowedHeaders.All(x => x.Equals("*"))) policyBuilder.AllowAnyHeader(); else policyBuilder.WithHeaders(corsSettings.AllowedHeaders); if (corsSettings.AllowedMethods.All(x => x.Equals("*"))) policyBuilder.AllowAnyMethod(); else policyBuilder.WithMethods(corsSettings.AllowedMethods); if (corsSettings.AllowedOrigins.All(x => x.Equals("*"))) policyBuilder.AllowAnyOrigin(); else policyBuilder.WithOrigins(corsSettings.AllowedOrigins); }); });
前端Blazor WASM配置代码
HttpClient与Refit客户端配置
builder.Services.AddRefitClient<IBackendApi>() .ConfigureHttpClient(httpClient => httpClient.BaseAddress = new Uri(backendUrl)) .AddHttpMessageHandler<CookieHandler>();
CookieHandler实现
public class CookieHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { request.SetBrowserRequestMode(BrowserRequestMode.Cors); request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include); return await base.SendAsync(request, cancellationToken); } }
Refit接口示例
[Put("/api/glossary/")] Task<ApiResponse<UpdateGlossaryEntryResponse>> UpdateGlossaryEntryAsync([Body] UpdateGlossaryEntryRequest request);
内容的提问来源于stack exchange,提问作者MarekWdowiak
相关产品推荐
相关产品推荐

