.NET Core 3 Web Api Cors fetch 一直 307 Temporary Redirect 繼上一篇 ".net core 3 web api jwt 一直 401" 為添加 所述的坑後, 本次為添加 ,又踩坑了。 自從 .NET Core 2.2 之後,CORS跨域配置代碼發 ...
.NET Core 3 Web Api Cors fetch 一直 307 Temporary Redirect
繼上一篇 .net core 3 web api jwt 一直 401 為添加JWT-BearerToken認證
所述的坑後,
本次為添加CORS跨域
,又踩坑了。
自從 .NET Core 2.2 之後,CORS跨域配置代碼發生了很大變化。
在 .NET Core 3.1 中,本作者碰到各種HTTP錯誤,諸如 500、307、401
等錯誤代碼...
在必應Bing和不斷Debug調整配置代碼位置後,得知:
AllowAnyOrigin
方法,在新的 CORS 中間件已經被阻止使用允許任意 Origin,所以該方法無效。AllowCredentials
方法,自從 .NET Core 2.2 之後,不允許和AllowAnyOrigin
同時調用。WithOrigins
方法,在 .NET Core 3.1 中有bug,具體原因未知,暫時只能用SetIsOriginAllowed(t=> true)
代替,等效.AllowAnyOrigin
方法。- 創建項目預設的模板中,
app.UseHttpsRedirection()
在前面,所以我將app.UseCors()
放在它後面,這是導致HTTP 307 Temporary Redirect
福報的根本原因之一。 - 度娘告訴我,
app.UseCors()
方法要在app.UseAuthentication()
之後,是誤人子弟的,其實放在它前面也可以,並且app.UseCors()
要在app.UseRouting()
之後,app.UseEndpoints()
和app.UseHttpsRedirection()
之前 - 使用fetch跨域請求時,要註意controller的action是否有設置除了
HttpOptions
之外的其它Http Method方法,如果有要加上HttpOptions
標記特性,因為fetch跨域請求會先執行OPTIONS
預請求。 - 使用fetch請求需要JWT認證的介面時,除了在HTTP Headers設置
Authorization
之外,還需要設置'credentials': 'include'
。 - 寫
app.UseXxxxxx
方法,引入中間件時,要註意管道(Middleware
)註冊順序。
參考:
- CORS配置:https://docs.microsoft.com/zh-cn/aspnet/core/security/cors?view=aspnetcore-3.1
- JWT認證:https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/?view=aspnetcore-3.1
- Cors Issue: https://github.com/dotnet/aspnetcore/issues/16672
- Forum: https://forums.asp.net/t/2160821.aspx?CORS+in+ASP+NET+Core+3+x
源代碼
以下是在 .NET Core 3.1下經過嚴謹測試,可以JWT認證
、CORS跨域
、IIS托管
、自寄主運行
的源代碼,僅供參考。
WebApi.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>WebApi</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.EventSource" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.TraceSource" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.2" />
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="16.153.0" />
<PackageReference Include="Microsoft.VisualStudio.Services.Client" Version="16.153.0" />
<PackageReference Include="Microsoft.VisualStudio.Services.InteractiveClient" Version="16.153.0" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
</ItemGroup>
</Project>
Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using System.Diagnostics;
using System.IO;
namespace WebApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders()
#if DEBUG
.AddConsole()
.AddDebug()
.AddEventLog()
.AddTraceSource(new SourceSwitch(nameof(Program), "Warning"), new ConsoleTraceListener())
#endif
.AddNLog();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
.UseIISIntegration()
.UseIIS()
.UseStartup<Startup>();
});
}
}
}
Startup.cs
using MCS.Vsts.Options;
using MCS.Vsts.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using System.Text;
namespace WebApi
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//認證
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var secretBytes = Encoding.UTF8.GetBytes(Configuration["ServerConfig:Secret"]);
options.TokenValidationParameters = new TokenValidationParameters()
{
IssuerSigningKey = new SymmetricSecurityKey(secretBytes),
ValidateIssuer = false,
ValidateAudience = false,
ValidateActor = false,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateLifetime = true
};
});
//跨域
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder
//允許任何來源的主機訪問
//TODO: 新的 CORS 中間件已經阻止允許任意 Origin,即設置 AllowAnyOrigin 也不會生效
//AllowAnyOrigin()
//設置允許訪問的域
//TODO: 目前.NET Core 3.1 有 bug, 暫時通過 SetIsOriginAllowed 解決
//.WithOrigins(Configuration["CorsConfig:Origin"])
.SetIsOriginAllowed(t=> true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
//TODO: do something...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
//Enabled HSTS
app.UseHsts();
}
//TODO: 要放在UseCors之後
//app.UseHttpsRedirection();
app.UseRouting();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
//TODO: UseCors要在UseRouting之後,UseEndpoints 和 UseHttpsRedirection 之前
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseHttpsRedirection();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"https_port": 44370,
"urls": "http://*:50867",
"ServerConfig": {
"Secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
"CorsConfig": {
"BaseUri": "http://myserver"
}
}