使用角度和Web API(.NET核心)时发生CORS问题[已解决]

时间:2019-07-16 14:49:26

标签: c# angular .net-core cors asp.net-core-webapi

我有两个单独的项目,一个是在.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的意见和建议。这些建议给了我解决该问题的提示。

1 个答案:

答案 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