我有两个单独的项目,一个是在.net Core 2.2中使用Windows身份验证开发的WebAPI,另一个是Angular。我陷入了CORS问题。我能够使用withCredentials处理GET请求:如下所述,在GET方法选项中为true,其中httpClient来自'@ angular / common / http'的import {HttpClient}:
return this.httpClient.get(this.getWebApiServiceUrl('/applicationusers/userprofile'), { params: this.httpParams, withCredentials: true }) as Observable<UserProfile>;
但是在POST的情况下,请求将作为OPTION进行。并且每次都失败,并在Chrome开发者工具窗口的“网络”标签中显示错误代码401 UNAUTHORIZED。并在控制台中显示以下错误
从原点“ http://localhost:5000/api/xxx/xxxMethod”到“ http://localhost:4200”的XMLHttpRequest的访问已被CORS策略阻止:对预检请求的响应未通过访问控制检查:它没有HTTP正常状态。
为解决此问题,我对以下Web AP项目文件进行了一些更改:
Web.config:我在system.webserver标记下添加了以下代码
<system.webServer>
<security>
<requestFiltering>
<verbs>
<remove verb="OPTIONS" />
<add verb="OPTIONS" allowed="true" />
</verbs>
</requestFiltering>
</security>
<httpErrors errorMode="Detailed" />
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" hostingModel="InProcess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
<environmentVariable name="COMPLUS_ForceENC" value="1" />
</environmentVariables>
</aspNetCore>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:4200" />
<add name="Accept" value="application/json, text/plain, */*"/>
</customHeaders>
</httpProtocol>
</system.webServer>
PreflightRequestMiddleware.cs:我创建了这个中间件来处理所有传入的请求并以OK状态绕过OPTIONS请求
public class PreflightRequestMiddleware
{
private readonly RequestDelegate Next;
public PreflightRequestMiddleware(RequestDelegate next)
{
Next = next;
}
public Task Invoke(HttpContext context)
{
return BeginInvoke(context);
}
private Task BeginInvoke(HttpContext context)
{
context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept, Athorization, ActualUserOrImpersonatedUserSamAccount, IsImpersonatedUser" });
context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, POST, PUT, DELETE, OPTIONS" });
if (context.Request.Method == HttpMethod.Options.Method)
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
return context.Response.WriteAsync("OK");
}
return Next.Invoke(context);
}
}
public static class PreflightRequestExtensions
{
public static IApplicationBuilder UsePreflightRequestHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<PreflightRequestMiddleware>();
}
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("CorePolicy", builder =>
{
builder.AllowAnyMethod();
}));
.......
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UsePreflightRequestHandler();
.....
app.UseCors("CorePolicy"); //Tried to put this first line too, but no luck
.....
}
然后在Angular Project中,对于POST方法调用,我首先创建了标头:
AbstractReadOnlyService.httpHeaders = new HttpHeaders().set('Content-Type', 'application/json');
AbstractReadOnlyService.httpHeaders = AbstractReadOnlyService.httpHeaders.set('Accept-Language', ['en-US', 'en', 'q=0.9']);
AbstractReadOnlyService.httpHeaders = AbstractReadOnlyService.httpHeaders.set('Accept', ['application/json', 'text/plain', '*/*']);
AbstractReadOnlyService.httpHeaders = AbstractReadOnlyService.httpHeaders.set('Athorization', 'Include');
AbstractReadOnlyService.optionsStatic = {
headers: AbstractReadOnlyService.httpHeaders,
params: new HttpParams()
};
return this.httpClient.post(url, firmDashboardSearchCriteria, AbstractReadOnlyService.optionsStatic).pipe(
map((response: any) => response as xxxxSearchResult[]),
catchError(this.handleError)
但是,我注意到了一件奇怪的事情,当我第一次运行Fiddler时,然后当我运行Web API和Angular应用程序时,所有的OPTIONS请求都在PreflightRequestMiddleware中处理。但是当我在没有Fiddler的情况下运行时,请求甚至没有到达PreflightRequestMiddleware。我已经花了4天的时间,但仍然不知道出什么问题了。很少有人会建议我检查在Request中运行Fiddler时收到的标头,但我也没有运气尝试过。有人有任何线索吗?
有谁能让我知道如何将Windows凭据与POST请求一起传递,如当前在响应标头中一样,我正在 WWW-Authenticate:Negotiate,NTLM ,这意味着未传递Windows凭据到服务器。
链接问题中提到的建议不能解决我的问题。我仍然遇到同样的问题。请删除重复的标记,因为它与IIS或设置CORS无关。我能够使用GET请求获取数据,并且只有POST请求引起了问题。我也已经确定了问题,这是OPTIONS请求没有将Windows凭据发送到API,因此POST请求没有得到处理。
我希望@Jota,托莱多,您现在将了解这不是重复的问题。
工作解决方案
最后,我能够弄清楚如何解决上述问题。由于我无法在此处回答自己的问题,因此我在此处发布解决方案:
解决方案: 我删除了上述所有代码,然后重新开始,如下文所述:
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(
"CorsPolicy",
builder => builder.WithOrigins("http://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddAuthentication(IISDefaults.AuthenticationScheme);
.....
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("CorsPolicy");
app.UsePreflightRequestHandler();
.......
}
launchSettings.json:我将匿名和Windows身份验证设置为true
{
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:5000",
"sslPort": 0
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"TPIGO.WebAPI": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5000"
}
}
}
然后,以角度显示:
AbstractReadOnlyService.httpHeaders = new HttpHeaders().set('Content-Type', 'application/json');
AbstractReadOnlyService.httpHeaders = AbstractReadOnlyService.httpHeaders.set('Accept', 'application/json');
AbstractReadOnlyService.optionsStatic = {
headers: AbstractReadOnlyService.httpHeaders,
params: new HttpParams(),
withCredentials: true
};
//For GET
return this.httpClient.get(this.getWebApiServiceUrl('/applicationusers/userprofile'), { params: this.httpParams, withCredentials: true }) as Observable<UserProfile>;
//For POST
return this.httpClient.post(url, firmDashboardSearchCriteria, AbstractReadOnlyService.optionsStatic).pipe(
map((response: any) => response as xxxxSearchResult[]),
catchError(this.handleError)
现在,对该解决方案进行说明。
正如我在问题陈述中提到的那样,GET请求工作正常,但问题出在POST请求上。
对于POST请求,浏览器首先发送OPTIONS请求以对服务器进行身份验证,但是此请求将永远无法到达服务器,这就是我无法在PreflightRequestMiddleware中处理该请求的原因。
OPTIONS请求失败,因为该API已配置为Windows身份验证,并且OPTIONS请求未对其进行任何身份验证。
这就是我在launchSettings.json中启用匿名身份验证的原因。
但是,当我启用匿名身份验证时,我开始收到500个内部服务器错误,并进行了进一步调查,我知道我需要提供身份验证方案。
然后我添加了services.AddAuthentication(IISDefaults.AuthenticationScheme);在Startup.cs文件中的ConfigureServices(IServiceCollection services)方法。一切都像魅力。
请记住在发送给需要身份验证的API的每个请求中添加 withCredentials:true 。
如果有人有任何疑问或困惑,请随时在这里提问。我不会关闭这篇文章,以便其他人可以在这里提及我提到的解决方案。
注意:我仍在使用PreflightRequestMiddleware只是为了在Request and Response上做一些额外的事情,但这不是必需的。
public class PreflightRequestMiddleware
{
private readonly RequestDelegate Next;
public PreflightRequestMiddleware(RequestDelegate next)
{
Next = next;
}
public Task Invoke(HttpContext context)
{
return BeginInvoke(context);
}
private Task BeginInvoke(HttpContext context)
{
// Do stuff here
return Next.Invoke(context);
}
}
public static class PreflightRequestExtensions
{
public static IApplicationBuilder UsePreflightRequestHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<PreflightRequestMiddleware>();
}
}
感谢Manoj,Bogdan和Jota的意见和建议。这些建议给了我解决该问题的提示。
答案 0 :(得分:0)
看看在Angular应用程序中使用proxy.conf.json文件。这将使您能够将某些URL转移到后端服务器。有了这个,您将不再收到臭名昭著的CORS错误。
在proxy.conf.json文件中,您可以在标题中传递身份验证/授权。
{
"/api/*": {
"target": "http://thisroute.com/",
"secure": false,
"logLevel": "debug",
"changeOrigin": true,
"headers": {
"Content-Type": "application/json",
"Authorization": "your auth here"
}
}
}
以下是官方的角度文档:https://angular.io/guide/build#proxying-to-a-backend-server
这是我发现有用的视频:https://www.youtube.com/watch?v=OjmZPPKaj6A