こんにちは。松井です。
「blocked by CORS policy」
このメッセージに何度、頭を悩ましたことでしょうか。
Access to image at 'http://localhost:5044/UploadFiles/SupportParts/ThumbnailBase/Cobotta.png' from origin 'http://localhost:8100' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
例によって、今回、僕が関わることになったシステム開発でも、
このメッセージが発生したので、
今回は、その解決方法について、話していこうと思います。
目次
前提
開発環境
・バック :C# (ASP.NET Core)
・フロント:Angular (何らかのJS)
サーバーにある静的ファイル(今回の場合、3Dファイル)を、
フロントのJSから取得・参照する機能を実装したい。
エラーが起こってたコード
まずは、CORS(これって、「コルス」って読むらしいですね)エラーが
起こっていたコードを見ていこうと思います。
いや! 解決後のコードだけ見れればいいから!
って方は、ぜひ読み飛ばしてください。
using Microsoft.Extensions.FileProviders;
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();
// CORSオリジン設定
builder.Services.AddCors(options =>
{
options.AddPolicy(
"AllowAll",
builder =>
{
builder.AllowAnyOrigin() // すべてのオリジンからのアクセスを許可
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// 静的ファイルの提供には、これが必要。
// https://qiita.com/gushwell/items/462fb61ff9025657256e
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "UploadFiles")),
RequestPath = "/UploadFiles"
});
app.UseAuthorization();
app.MapControllers();
// CORS ミドルウェアを有効にする
app.UseCors("AllowAll");
app.Run();
一見、このコード、CORS対応がちゃんとされている
(全てを許容してるのが、セキュリティ的にどうなのかはさておき、)
ように見えます。
しかし、この状態で、フロントのJSから
サーバーにアクセスすると、CORSエラーが発生するのです。
ちなみに、フロントでは、three.jsを使って、
サーバーにあるSTLファイル(3Dデータ)を読み込んで、
3Dモデルを表示させる処理を作っていました。
(three.js:https://threejs.org/)
この原因調査のために、
就業時間30分前に、4人でうんうんと悩みながら、
結局、1時間の残業となりました。
答えは、いつも公式ドキュメントに!
いろいろ4人で悩みましたが、
結局、公式ドキュメントをもう一度しっかり読み直そう という話となり、
公式ドキュメントを読み直すことにしました。
👇公式ドキュメントはこちら。
ASP.NET Core でクロスオリジン要求 (CORS) を有効にする
とはいえ、もちろん、今までも何度も
読んできたし、ここに書かれているようなことは全て書いてある。
でも、「絶対にサーバー側の設定に問題があるから」
という先輩の言葉を聞いて、
いま一度、読み直しました。
そして、見つけた(気づいた?)のが、この2文。
UseCors と UseStaticFiles の順序
一般に、UseCors の前に UseStaticFiles が呼び出されます。 JavaScript を使用してクロスサイトで静的ファイルを取得するアプリでは、UseStaticFiles の前に UseCors を呼び出す必要があります。
な、
な、
なんだって〜〜〜〜〜〜?????
そして、驚きとともに、その通りにコードを書き換えました。
解決したコードは、こちら。
using Microsoft.Extensions.FileProviders;
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();
// CORSオリジン設定
builder.Services.AddCors(options =>
{
options.AddPolicy(
"AllowAll",
builder =>
{
builder.AllowAnyOrigin() // すべてのオリジンからのアクセスを許可
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// CORS ミドルウェアを有効にする
app.UseCors("AllowAll"); // ポイント!!! ← app.UseStaticFiles()の前に移動。
// 静的ファイルの提供には、これが必要。
// https://qiita.com/gushwell/items/462fb61ff9025657256e
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "UploadFiles")),
RequestPath = "/UploadFiles"
});
app.UseAuthorization();
app.MapControllers();
app.Run();
「app.UseCors()」を「app.UseStaticFiles()」の前に移動させたのがポイントです!
そして、再度、フロントからJSを実行!
そして、
無事、CROSエラーが起こらず、表示したかった3Dモデルが見れました( ´ ▽ ` )
まさか、大切なのは、順序。
設定の順番で、アクセスの制御のされ方が変わるなんて、、。
UseCors と UseStaticFiles の順序
JavaScript を使用してクロスサイトで静的ファイルを取得するアプリでは、UseStaticFiles の前に UseCors を呼び出す必要があります。
全くもって、盲点でした。
きっと今まで困ってきた、CROSエラーも
これがわかっていれば、解決できていたのかもしれません。
現に、以前、
html2canvasを使って、
画面入力値から、サムネイル画像を作成する処理を作ったときは、
(html2canvas:https://html2canvas.hertzen.com/)
このCROSエラーの解決ができず、
結局、サーバー側からフロントへ、画像をバイナリ型に変換して渡して、
それをフロントで表示する。
というやり方で、
クロスオリジンがそもそも起こらないようにして、解決していました。
(いま思えば、逃げの一手ですね。)
今回の学びと、論語
子曰く、過ちてはすなわち改むるに憚る(はばかる)ことなかれと。
過って改めることを、躊躇してはいけない。
過ちは誰にでもある。
そして、それは、一時のことだ。
しかし、過って改めることをしなければ、それこそ救いがたい過ちで、
生涯をとおして、同じ過ちを繰り返すことになる。
今回の場合は、「過ち」というには、少しおおげさに聞こえるが、
CROSエラーという問題に対面したときに、
その解決に挑まず、逃げてしまったことに関して、
その(本質的な)解決ができない限り、
今後、何度も、その問題に直面することになっていたように思う。
そういう意味では、今回の開発では、再び、
CROSエラーが起こってくれたことで、
(そして、先輩や同僚の協力を借りられたことで)
今まで何度も解決できなかったCROSエラーへ挑戦する機会をもらえ、
そして、解決できてよかったと思います。
開発していると、日々日々いろんなエラーにぶち当たりますが、
その都度、挑戦していきたいと思います!
ここまで、読んでいただきありがとうございました!
松井
ちなみに
動くのか確認してないけど、調査したので載せておきます。参考程度にどうぞ!
html2canvasのCROS問題には、たぶんもうひと工夫必要で。
ここのオプションで、
「allowTaint」と、「useCORS」 というオプションがあって、その設定が必要そう。
allowTaint
Whether to allow cross-origin images to taint the canvas
→ クロスオリジンの画像がキャンバスを汚すことを許可するかどうか
useCORS
Whether to attempt to load images from a server using CORS
→ CORSを使用してサーバーから画像のロードを試みるかどうか
なので、両方Trueにして、実行する必要があるような気がする。
とはいえ実際に、実装して試したわけでないので、参考程度にどうぞ。