ASP.NET Core APIで、CORS問題につまづいてたけど、やっっと乗り越えた話

こんにちは。松井です。

　

「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（これって、「コルス」って読むらしいですね）エラーが
起こっていたコードを見ていこうと思います。

いや！　解決後のコードだけ見れればいいから！

って方は、ぜひ読み飛ばしてください。

```csharp
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分前に、４人でうんうんと悩みながら、

結局、１時間の残業となりました。

　

## 答えは、いつも公式ドキュメントに！

いろいろ４人で悩みましたが、
結局、公式ドキュメントをもう一度しっかり読み直そう　という話となり、

公式ドキュメントを読み直すことにしました。

　

👇公式ドキュメントはこちら。
[ASP.NET Core でクロスオリジン要求 (CORS) を有効にする](
https://learn.microsoft.com/ja-jp/aspnet/core/security/cors?view=aspnetcore-8.0)

　

とはいえ、もちろん、今までも何度も
読んできたし、ここに書かれているようなことは全て書いてある。

　

でも、「絶対にサーバー側の設定に問題があるから」
という先輩の言葉を聞いて、

いま一度、読み直しました。

　

そして、見つけた（気づいた？）のが、この２文。

> **UseCors と UseStaticFiles の順序**
>一般に、UseCors の前に UseStaticFiles が呼び出されます。 JavaScript を使用してクロスサイトで静的ファイルを取得するアプリでは、UseStaticFiles の前に UseCors を呼び出す必要があります。



な、

な、

なんだって〜〜〜〜〜〜？？？？？

　


そして、驚きとともに、その通りにコードを書き換えました。

　

## 解決したコードは、こちら。

```csharp
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();
```

:::note info
「app.UseCors()」を「app.UseStaticFiles()」の前に移動させたのがポイントです！
:::

そして、再度、フロントからJSを実行！

　

そして、

無事、CROSエラーが起こらず、表示したかった３Dモデルが見れました( ´ ▽ ` )

　

まさか、大切なのは、順序。

設定の順番で、アクセスの制御のされ方が変わるなんて、、。


> **UseCors と UseStaticFiles の順序**
>
>JavaScript を使用してクロスサイトで静的ファイルを取得するアプリでは、UseStaticFiles の前に UseCors を呼び出す必要があります。

全くもって、盲点でした。

　

きっと今まで困ってきた、CROSエラーも
これがわかっていれば、解決できていたのかもしれません。

　

現に、以前、

html2canvasを使って、
画面入力値から、サムネイル画像を作成する処理を作ったときは、
（html2canvas：https://html2canvas.hertzen.com/）

　

このCROSエラーの解決ができず、

結局、サーバー側からフロントへ、画像をバイナリ型に変換して渡して、
それをフロントで表示する。

というやり方で、
クロスオリジンがそもそも起こらないようにして、解決していました。

（いま思えば、逃げの一手ですね。）

## 今回の学びと、論語

>子曰く、過ちてはすなわち改むるに憚る（はばかる）ことなかれと。

過って改めることを、躊躇してはいけない。

過ちは誰にでもある。

そして、それは、一時のことだ。

しかし、過って改めることをしなければ、それこそ救いがたい過ちで、

生涯をとおして、同じ過ちを繰り返すことになる。

　
 
今回の場合は、「過ち」というには、少しおおげさに聞こえるが、

CROSエラーという問題に対面したときに、

その解決に挑まず、逃げてしまったことに関して、

その（本質的な）解決ができない限り、

今後、何度も、その問題に直面することになっていたように思う。

　

そういう意味では、今回の開発では、再び、

CROSエラーが起こってくれたことで、

（そして、先輩や同僚の協力を借りられたことで）

今まで何度も解決できなかったCROSエラーへ挑戦する機会をもらえ、

そして、解決できてよかったと思います。

　

開発していると、日々日々いろんなエラーにぶち当たりますが、
その都度、挑戦していきたいと思います！

　

ここまで、読んでいただきありがとうございました！

松井

## ちなみに

:::note warn
動くのか確認してないけど、調査したので載せておきます。参考程度にどうぞ！
:::

html2canvasのCROS問題には、たぶんもうひと工夫必要で。

　

[html2canvas Options](https://html2canvas.hertzen.com/configuration/)

ここのオプションで、
「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にして、実行する必要があるような気がする。

　

とはいえ実際に、実装して試したわけでないので、参考程度にどうぞ。