【解決方法】angular および Web API (.NET コア) が使用されている場合の CORS の問題 [SOLVED]

プログラミングQA


私は2つの別々のプロジェクトを持っています。1つはWindows認証を使用して.net Core 2.2で開発されたWebAPIで、も​​う1つはAngularです。 CORSの問題で立ち往生しています。 以下で説明するように、GET メソッド オプションで withCredentials: true を使用して GET リクエストを処理できました。ここで、httpClient は import { HttpClient } from ‘@angular/common/http’ からのものです。

JavaScript
httpClient.get('url'), { withCredentials: true }) as Observable<Type>;

ただし、POST の場合、リクエストは OPTION として実行されます。 また、Chrome 開発者ツール ウィンドウの[ネットワーク]タブでエラー コード 401 UNAUTHORIZED で失敗するたびに。 コンソールでは、以下のエラーが表示されています

オリジン ‘http://localhost:4200’ から ‘http://localhost:5000/api/xxx/xxxMethod’ にある XMLHttpRequest へのアクセスが CORS ポリシーによってブロックされました: プリフライト リクエストへの応答がアクセス制御チェックに合格しません: HTTP ok ステータスがありません。

私が試したこと:

この問題を解決するために、Web API と Angular プロジェクトの次のファイルにいくつかの変更を加えました。

Web.config:system.webserver タグの下に以下のコードを追加しました

XML
<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>

PreflightRequestMiddleware.cs: すべての受信リクエストを処理し、OK ステータスの OPTIONS リクエストをバイパスするために、このミドルウェアを作成しました

C#
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:

C#
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
  .....
}
Then in Angular Project, for POST method call I first create headers:
<pre lang="TypeScript">
HttpHeaders() with below values:
'Content-Type', 'application/json'
'Content-Type', 'application/json'
'Accept-Language', ['en-US', 'en', 'q=0.9']
'Accept', ['application/json', 'text/plain', '*/*']
'Athorization', 'Include'

Calling POST Method
client.post(url, postParam, optionsWithHeader).pipe(
            map((response: any) => response as Result[]),
            catchError(#HandleError)

ただ、一つ気になる点があり、 最初に Fiddler を実行し、次に Web API と Angular アプリを実行すると、すべての OPTIONS リクエストが PreflightRequestMiddleware で処理されます。 しかし、私が Fiddler なしで実行している場合、リクエストは PreflightRequestMiddleware に到達していません。 私は4日間過ごしましたが、何が悪いのかまだわかりません。 Request で Fiddler を実行しているときに受信するヘッダーを確認するように提案する人はほとんどいないかもしれませんが、私も試してみましたが、うまくいきませんでした。 誰も手がかりを持っていますか??

解決策 1

最後に、上記の問題を解決する方法を見つけることができました。 ここで自分の質問に答えることができないため、ここに解決策を投稿しています。

解決: 以下のファイルで説明するように、上記のコードをすべて削除し、新たに開始しました。

Startup.cs:

C#
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 に設定しました

JSON
{
  "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"
    }
  }
}

次に、Angular で:

//For GET & POST

Add, withCredentials: true as otions

さて、この解決策の説明です。

問題の説明で述べたように、GET リクエストは正常に機能していましたが、問題は POST リクエストにありました。

POST リクエストの場合、ブラウザはサーバーで認証するために最初に OPTIONS リクエストを送信していましたが、このリクエストはサーバーに到達しないため、PreflightRequestMiddleware でリクエストを処理できませんでした。

API が Windows 認証用に構成されており、OPTIONS 要求に認証が含まれていなかったため、OPTIONS 要求が失敗していました。

これが、launchSettings.json で匿名認証を有効にした理由です。

しかし、匿名認証を有効にすると、500 内部サーバー エラーが発生し始め、さらに調査すると、認証スキームを提供する必要があることがわかりました。

次に、services.AddAuthentication(IISDefaults.AuthenticationScheme); を追加しました。 Startup.cs ファイルの ConfigureServices(IServiceCollection services) メソッド。 すべてが魔法のように機能します。

追加することを忘れないでください withCredentials: 真 認証が必要な API に送信するすべてのリクエストで。

疑問や混乱がある場合は、ここで気軽に質問してください。 私が言及した解決策に関して、他の人がここで疑問を共有できるように、この投稿を閉じるつもりはありません。

ノート: 私はまだ PreflightRequestMiddleware を使用して、リクエストとレスポンスで追加の作業を行っていますが、このミドルウェアは必須ではありません。

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>();
        }
    }

解決策 8

これは私のために働く:

Web.config

<httpProtocol>
  <customHeaders>
	<add name="Access-Control-Allow-Origin" value="*" />
	<add name="Access-Control-Allow-Methods" value="*" />
	<add name="Access-Control-Allow-Headers" value="x-requested-with, Content-Type, origin, authorization, accept, client-security-token" />
  </customHeaders>
</httpProtocol>  

Global.asax

protected void Application_BeginRequest()
{
	if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
	{
		// These headers are handling the "pre-flight" OPTIONS call sent by the browser
		HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
		HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
		HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");
		HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
		HttpContext.Current.Response.End();
	}
}

解決策 11

ASP.Net Web API では、.Net 7.0 + Angular でまったく同じ状況が発生しました。 GET リクエストは機能しましたが、POST リクエストでエラーが発生しました (コンソールに表示されます):

引用:

オリジンからの XMLHttpRequest へのアクセスが CORS ポリシーによってブロックされました: プリフライト要求への応答がアクセス制御チェックに合格しません: 要求されたリソースに ‘Access-Control-Allow-Origin’ ヘッダーが存在しません。

ここのように、これまで使用していた複数の app.UseCors(….) をすべて削除する必要がありました。

app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowCredentials().WithOrigins("http://localhost:4200"));
app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowCredentials().WithOrigins("https://localhost:4200"));

削除した後、新しいポリシー定義を追加しました。

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyCorsPolicy",
        builder => builder
                          .AllowAnyMethod()
                          .AllowAnyHeader()
                          //.WithExposedHeaders("Access-Control-Allow-Origin")
                          //.AllowAnyOrigin() // <- allowes all!
                          .WithOrigins("http://localhost:4200", "https://localhost:4200")
                          );
});

そして、そのポリシーを使用しました:

app.UseCors("MyCorsPolicy");

完全な Program.cs コンテンツ:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddCors();

builder.Services.AddScoped<CosmosDataContext>();
builder.Services.AddScoped<TokenService>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new TokenService(builder.Configuration).TokenKey,
            ValidateIssuer = false,
            ValidateAudience = false
        };
    });

builder.Services.AddCors(options =>
{
    options.AddPolicy("MyCorsPolicy",
        builder => builder
                          .AllowAnyMethod()
                          .AllowAnyHeader()
                          //.WithExposedHeaders("Access-Control-Allow-Origin")
                          //.AllowAnyOrigin() // <- allows all!
                          .WithOrigins("http://localhost:4200", "https://localhost:4200")
                          );
});

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseCors("MyCorsPolicy");

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

解決策 7

asp.netコア3.1と角度9でも同じ問題があります。
私の場合、あなたが説明した解決策は機能しません。

同じエラーが発生し続けます:
「元の ‘http://localhost:4200’ からの ‘http://localhost:5000/api/……’ での XMLHttpRequest へのアクセスは、CORS ポリシーによってブロックされました: プリフライト要求への応答がアクセスを渡しません制御チェック: 要求されたリソースに ‘Access-Control-Allow-Origin’ ヘッダーが存在しません。”

また、デバッグ中に、GET リクエストの場合は PreflightRequestMiddleware クラスが処理されますが、OPTIONS リクエストに関しては処理されないことに気付きました。

解決策 9

こんにちは、このコード スニペットで

builder => builder.WithOrigins("http://localhost:4200")

私のローカルホストのポートに置き換えますか?

コメント

タイトルとURLをコピーしました