.NET 8 Web API+Angular部署IIS后出现405 Method Not Allowed错误求助
.NET 8 Web API部署IIS后405 Method Not Allowed错误解决
问题背景
基于.NET 8 Web API + Angular的同解决方案应用,本地Visual Studio运行正常,部署到IIS后出现405 Method Not Allowed错误。已尝试启用请求筛选、追踪、移除webDev处理程序、移除过滤器中间件、启用全局CORS,但问题未解决,怀疑与JWT认证相关。
已尝试操作
- 启用请求筛选
- 启用追踪
- 移除webDev等处理程序
- 移除过滤器与中间件
- 为所有源启用CORS
关键信息
Program.cs代码
var builder = WebApplication.CreateBuilder(args); #region Configure JWT authentication // Load configuration from appsettings.json builder.Configuration.AddJsonFile("appsettings.json"); // Configure JWT authentication builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration.GetSection("Jwt:Issuer").Value, ValidAudience = builder.Configuration.GetSection("Jwt:Audience").Value, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection("Jwt:SecretKey").Value)) }; // Optionally, configure other JwtBearer options }); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "l", Version = "v1" }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { In = ParameterLocation.Header, Description = "Please enter the Bearer token", Name = "Authorization", Type = SecuritySchemeType.ApiKey }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, Array.Empty<string>() } }); }); #endregion builder.Services.AddResponseCompression(options => { options.Providers.Add<GzipCompressionProvider>(); options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat( new[] { "application/json" }); }); #region Add services to the container builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true) .AddEnvironmentVariables(); var connection = builder.Configuration.GetConnectionString("PublicPortalDBcontext"); var SecurityKey = builder.Configuration.GetSection("Jwt:SecretKey"); var Issuer = builder.Configuration.GetSection("Jwt:Issuer"); var audience = builder.Configuration.GetSection("Jwt:Audience"); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddControllersWithViews(); //Services Initialization builder.Services.AddDbContext<DbContext>(options => options.UseSqlServer(connection)); builder.Services.AddScoped<IUnitOfWork, UnitOfWork>(); #endregion #region CORS builder.Services.AddCors(options => { options.AddPolicy("EnableCORS", cbuilder => { cbuilder.WithOrigins(builder.Configuration.GetSection("corsURL:devUrl").Value , builder.Configuration.GetSection("corsURL:testUrl").Value , builder.Configuration.GetSection("corsURL:prodUrl").Value) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); }); #endregion #region LOGGER var logger = new LoggerConfiguration() .ReadFrom.Configuration(builder.Configuration) .Enrich.FromLogContext() .CreateLogger(); builder.Logging.ClearProviders(); builder.Logging.AddSerilog(logger); #endregion LOGGER #region Configure the HTTP request pipeline var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Portal V1"); c.RoutePrefix = "swagger"; // Optional: Set the Swagger UI route prefix }); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } //exception handling and logging middleware app.UseMiddleware<ExceptionHandlingMiddleware>(); app.UseCors("EnableCORS"); app.UseStaticFiles(); app.UseHttpsRedirection(); app.UseResponseCompression(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapFallbackToFile("/index.html"); app.Run(); #endregion
请求头
accept: application/json, text/plain, */* accept-encoding: gzip, deflate, br, zstd accept-language: en-US,en;q=0.9,ta;q=0.8 connection: keep-alive content-length: 75 content-type: application/json; charset=UTF-8 host: localhost:85 origin: http://localhost:85 referer: http://localhost:85/ sec-ch-ua: "Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 withcredentials: true
响应头
HTTP/1.1 405 Method Not Allowed Cache-Control: no-cache,no-store Pragma: no-cache Transfer-Encoding: chunked Allow: GET, HEAD Expires: -1 Server: Microsoft-IIS/10.0 Access-Control-Allow-Origin: * X-Powered-By: ASP.NET Date: Mon, 12 Aug 2024 08:11:50 GMT
解决方案步骤
1. 彻底禁用IIS WebDAV模块
WebDAV是IIS默认启用的模块,会拦截POST/PUT/DELETE等请求导致405。在项目根目录添加web.config(若无则新建),加入以下配置:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <modules runAllManagedModulesForAllRequests="false"> <remove name="WebDAVModule" /> </modules> <handlers> <remove name="WebDAV" /> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" /> </handlers> <aspNetCore processPath="dotnet" arguments=".\YourAppName.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" /> </system.webServer> </configuration>
替换YourAppName.dll为实际程序集名称。
2. 调整路由与中间件顺序
- 给所有API控制器添加
[Route("api/[controller]")]前缀,避免请求被Angular的MapFallbackToFile拦截 - 确保
MapFallbackToFile放在MapControllers之后,保证API请求优先匹配路由
3. 验证JWT配置与日志
- 检查部署环境
appsettings.json中JWT的Issuer、Audience、SecretKey是否与本地一致,且不为空 - 添加JWT事件日志排查认证问题:
.AddJwtBearer(options => { // 原有TokenValidationParameters配置... options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { Serilog.Log.Error($"JWT认证失败: {context.Exception.Message}"); return Task.CompletedTask; }, OnChallenge = context => { Serilog.Log.Warning($"JWT触发Challenge: {context.Error}, {context.ErrorDescription}"); return Task.CompletedTask; } }; });
查看Serilog日志确认是否存在认证失败情况。
4. 修复CORS配置冲突
响应头返回Access-Control-Allow-Origin: *但请求带withcredentials: true,违反CORS规则(带凭证请求不能用通配符origin)。确保部署环境appsettings.json中corsURL包含当前请求origin(http://localhost:85),或临时修改CORS策略测试:
options.AddPolicy("EnableCORS", cbuilder => { cbuilder.WithOrigins("http://localhost:85") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); });
5. 检查IIS请求筛选
在IIS管理器中进入站点 -> 请求筛选 -> HTTP方法,确保添加了所需的HTTP方法(如POST、PUT、DELETE等),避免IIS拦截这些请求。
总结
405错误多由IIS模块拦截、路由匹配错误或HTTP方法限制导致,优先排查WebDAV模块,再验证配置与中间件顺序,结合日志定位具体问题。
内容的提问来源于stack exchange,提问作者user3184339
相关产品推荐
相关产品推荐

