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

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错误。

已尝试的解决方案

  1. 为API网关添加CORS策略,允许任意请求头和方法,但日志显示请求未到达网关,说明问题出在客户端与服务器之间
  2. 更新服务器配置,添加CORS策略:SetIsOriginAllowed(origin => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials()
  3. 更新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>
  1. 在服务器中更新自定义安全头,设置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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 23:25:22