簡単なチャットボットを作ってみよう（CORSエラー対応）

# 初めに

船井総研デジタルのtakizawaです。
今回は[こちらの記事](https://qiita.com/fsd-takizawa/items/b4d3e517879a873a90a7)の続編になります。今回はクロスオリジンリソース共有 (CORS) エラーの解消を行い、バックエンドとフロントエンドを連携できるようにします。

# 前回までに作成したプロジェクトを起動してみる。

バックエンドを```dotnet run --launch-profile https```コマンドで、フロントエンドを```npm run dev```コマンドで起動します。そしてブラウザに```http://127.0.0.1:5173/```を入力して表示します。続けてメッセージ入力エリアに任意の言葉を入れて「送信」ボタンをクリックします。すると```Failed to fetch```が記載されたポップアップが表示されます。これはエラーが発生したときの動きです。```<F12>```で開発者ツールを開きコンソールを見るとエラーが表示されています。
そのうちの一つを見てみます。
```
Access to fetch at 'https://127.0.0.1:7294/api/Reply' from origin 
'http://127.0.0.1:5173' has been blocked by CORS policy: Response 
to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' 
header is present on the requested resource. If an opaque response serves your needs, 
set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
```
```'http://127.0.0.1:5173'から'https://127.0.0.1:7294/api/Reply'にfetchするアクセスがCORSポリシーによりブロックされました：～```とあり、バックエンドへのアクセスがブロックされてしまっていることがわかります。

# CORSポリシーとは？

詳しくは[こちらのページ](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)に詳しく書かれていますのでここでは要点だけまとめます。

1. オリジン（ドメイン、プロトコル、ポート番号）の異なるリクエストをオリジン間リクエストと呼ぶ。
1. セキュリティ上の理由から、ブラウザはスクリプトによって開始されるオリジン間HTTPリクエストを制限する。
1. 同一オリジンであればHTTPリクエスト可能。
1. 異なるオリジンへのリクエストは正しいCORSヘッダーを含んでいる必要がある。

# どのように対応するか？

ASP.NET CoreではCORSを有効にする機能がサポートされているので、この機能を使って対応します。[ASP.NET Core でクロスオリジン要求 (CORS) を有効にする](https://learn.microsoft.com/ja-jp/aspnet/core/security/cors?view=aspnetcore-7.0)を参考にしています。

他にも調べたところVue.js(フロントエンド)側でProxyの設定を入れることで回避できるといった記事がいくつかありましたが、私の環境では解消できませんでした。

## バックエンド側の修正

```Program.cs```を次のようにコードを追加します。```// !!CORS対応 START[END]```で囲まれた部分が追加コードになります。
```cs:Program.cs
// !!CORS対応 START
var MyAllowOrigins = "_myAllowOrigins";
// !!CORS対応 END
var builder = WebApplication.CreateBuilder(args);

// !!CORS対応 START
builder.Services.AddCors(options => {
    options.AddPolicy(name: MyAllowOrigins,
        policy => {
            policy.WithOrigins("http://127.0.0.1:5173");
            policy.WithHeaders("Content-Type");
        }
    );
});
// !!CORS対応 END

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

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

// !!CORS対応 START
app.UseCors(MyAllowOrigins);
// !!CORS対応 END

app.UseAuthorization();

app.MapControllers();

app.Run();
```
順番に説明します。
1. ```MyAllowOrigins```に設定されている値がポリシー名にで、任意の文字列を設定します。
1. ```policy.WithOrigins```と```policy.WithHeaders```にそれぞれ許可するオリジンとヘッダーを定義して、ポリシーをサービスに追加します。
1. ```app.UseCors(MyAllowOrigins);```でCORSを使用するようにします。

# 動作の確認

ここまでで、入力したインプットを少し加工して返してくれるボットになってると思います。ついでに次の動きも確認してみてください。
* メッセージを送信してボットが応答を返すまで送信ボタンが __非活性__ になる。
* メッセージが多くなった場合、メッセージの送信とボットの応答を表示するときに最下部までスクロールする。
![スクリーンショット 2023-09-01 173207.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/e90f8f9c-3dac-e134-e3bc-05fc3200a9d3.png)
:::note warn
Google Chromeの場合は```Failed to load resource: net::ERR_CERT_COMMON_NAME_INVALID```のエラーが発生することがあります。このエラーが発生した場合は、一度バックエンドのURLをブラウザで表示します。
![スクリーンショット 2023-09-01 172833.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/e2fac4e3-a0b7-6561-3b7a-e62be774471a.png)
この左下の詳細設定をクリックして、その後表示される```127.0.0.1 にアクセスする（安全ではありません）```のリンクをクリックしてから再度試してください。
:::

# 最後に

今回はCORSエラー対応を行いバックエンドとフロントエンドを連携させて実際に動かしてみました。次回はAzure Open AIをデプロイしこのアプリケーションに連携させることを予定しています。

__続編の記事を投稿しました！__
[簡単なチャットボットを作ってみよう（Azure OpenAI連携編）](https://qiita.com/fsd-takizawa/items/a6afd88d71a5f5a739ee)