Blazor WASM .NET托管环境调用后端遇405方法不允许问题求助
生产环境PUT/DELETE请求返回405 Method Not Allowed问题排查
困扰多日,查阅了Stack Overflow、GitHub、微软等平台的相关帖子,但尝试的所有解决方案均无效。
系统架构
- 基于Duende BFF安全框架的Blazor .NET WASM ASP.NET Core托管应用
- 独立部署的Duende Identity Server实例
- Ocelot API网关
- .NET6远程Web API
- 所有应用均部署在Interserver共享IIS服务器上,各自对应独立域名
问题描述
调用GET方法时一切正常,响应头符合配置预期;开发环境下调用任意方法均正常;但生产环境中调用PUT或DELETE方法时,立即返回405 Method Not Allowed错误。
已尝试的解决方案
- 为API网关添加CORS策略,允许任意请求头和方法,但日志显示请求未到达网关,说明问题出在客户端与服务器之间
- 更新服务器配置,添加CORS策略:
SetIsOriginAllowed(origin => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials() - 更新web.config,添加以下配置段:
<security> <requestFiltering removeServerHeader="true"> <verbs allowUnlisted="true"> <add verb="POST" allowed="true"/> <add verb="PUT" allowed="true"/> <add verb="DELETE" allowed="true"/> </verbs> </requestFiltering> </security>
- 在服务器中更新自定义安全头,设置
context.Response.Headers.Allow = "GET, POST, DELETE, PUT, OPTIONS";
已准备两张截图:GET请求的响应头符合预期,而PUT请求未包含任何我设置的头信息。
服务器端Program.cs代码
using System.Net.Http.Headers; using Azure.Identity; using JMS.UI.Server.Extensions; using JMS.UI.Server.Helpers; using JMS.UI.Server.Settings; using JMS.UI.Server.Static; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Protocols.OpenIdConnect; var builder = WebApplication.CreateBuilder(args); // Add Azure Key Vault var keyVaultEndpoint = new Uri(HIDDEN); builder.Configuration.AddAzureKeyVault(HIDDEN); // Add identity services var idsSettings = new IdentityServerSettings { ClientPassword = builder.Configuration["HIDDEN"] }; #if DEBUG idsSettings.DiscoveryUrl = "https://localhost:7102"; idsSettings.ClientName = "HIDDEN"; #else idsSettings.DiscoveryUrl = "HIDDEN"; idsSettings.ClientName = "HIDDEN"; #endif builder.Services.AddControllers(); builder.Services.AddRazorPages(); builder.Services.AddBff(); builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; options.DefaultSignOutScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.Cookie.SameSite = SameSiteMode.Lax; }) .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { options.Authority = idsSettings.DiscoveryUrl; options.ClientId = idsSettings.ClientName; options.ClientSecret = idsSettings.ClientPassword; options.ResponseType = OpenIdConnectResponseType.Code; options.ResponseMode = OpenIdConnectResponseMode.Query; options.MapInboundClaims = false; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.UsePkce = true; options.Scope.Clear(); options.Scope.Add("JMS"); options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); options.Scope.Add("offline_access"); }); // Add services to the container. builder.Services.AddAutoMapper(typeof(AutomapperProfiles).Assembly); builder.Services.AddSettingsServiceConfigurations(builder.Configuration); builder.Services.AddServicesInjectors(); #if DEBUG ApiEndpoints.ApiBaseUrl = new Uri("https://localhost:7200"); #else ApiEndpoints.ApiBaseUrl = new Uri("HIDDEN"); #endif builder.Services.AddHttpClient("JmsClient", options => { options.BaseAddress = ApiEndpoints.ApiBaseUrl; options.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { MaxAge = TimeSpan.FromSeconds(2592000) }; }).AddUserAccessTokenHandler(); builder.Services.AddCors(options => { options.AddPolicy("JmsPolicy", b => b.SetIsOriginAllowed(origin => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials()); }); builder.Services.AddHttpContextAccessor(); builder.Logging.SetMinimumLevel(LogLevel.Error); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); } else { app.UseExceptionHandler("/Error"); app.Use(async (context, next) => { context.Response.Headers.XXSSProtection = "1; mode=block"; context.Response.Headers.XFrameOptions = "SAMEORIGIN"; context.Response.Headers.XContentTypeOptions = "nosniff"; context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin"); context.Response.Headers.Allow = "GET, POST, DELETE, PUT, OPTIONS"; context.Response.Headers.ContentSecurityPolicy = "default-src 'self'; " + "frame-ancestors 'none'; " + "font-src 'self' https://fonts.googleapis.com https://fonts.gstatic.com https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; " + "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.googleapis.com https://fonts.gstatic.com; " + "script-src 'self' 'unsafe-eval' 'unsafe-inline' https://cdn.jsdelivr.net https://use.fontawesome.com https://www.google.com https://maps.googleapis.com https://www.gstatic.com; " + "img-src 'self' data: https://www.google.com https://maps.googleapis.com https://www.gstatic.com https://maps.gstatic.com; " + "connect-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://use.fontawesome.com https://maps.googleapis.com https://www.google.com https://fonts.googleapis.com https://fonts.gstatic.com https://www.gstatic.com; " + "frame-src https://www.google.com https://maps.googleapis.com https://www.gstatic.com;"; await next(); }); } app.UseCors("JmsPolicy"); app.UseHttpsRedirection(); app.UseBlazorFrameworkFiles(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseBff(); app.UseAuthorization(); app.MapBffManagementEndpoints(); app.MapRazorPages(); app.MapControllers() .RequireAuthorization() .AsBffApiEndpoint(); app.MapFallbackToFile("index.html"); app.Run();
截图说明
- GET请求响应头:显示符合预期的自定义响应头配置
- PUT请求响应头:未包含任何自定义设置的头信息
内容的提问来源于stack exchange,提问作者jacaru
相关产品推荐
相关产品推荐

