Blazor WebAssembly から Web API (Azure Functions) を叩きたい

問題点

Azure Functions で作成した関数アプリを Web API として Blazor から叩こうとしたがこのようなエラーが発生する。

Access to fetch at '<関数アプリのURL>' from origin '<実行元アドレス(localhost)>' has been blocked by CORS policy: 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.

どうやら、CORS ポリシー によって HTTP リクエストがブロックされているらしい。

普段からWeb開発をやらないので、こういったことへの知識は疎いなぁと痛感。(もしかするとWeb界隈では常識なのかもしれない)

やりたいこと

Blazor WebAssembly から Azure Functions を実行したいだけ。(ちなみに Blazor Server のほうは問題なく実行できる。)

解決策

Azure Functions の CORS 設定から、リクエスト元になるアドレスを追加する。Blazor のソースコードはいじる必要はなかった。

自分の場合は Azure Static Web Apps からリクエストを送る予定だったので、デプロイしてからの URL を入力した。

f:id:takunology:20220202061721p:plain

ちなみに、ローカルホストから実行したい場合はそのアドレスを追記すれば良いみたい。(グローバル IP かと思っていた。)

f:id:takunology:20220202062905p:plain

めでたく花火が打ち上がった。(ちなみに、この内容は執筆中の本に載せる予定)

f:id:takunology:20220202063536p:plain

正直、たったこれだけか...とは思った。Blazor のリクエストヘッダになにかを追記したりしないといけないものかと丸一日悩んだけど、理由は単純だった...。

試したこと

一応、色々やってみたこともメモ。

1. リファレンスに忠実に

とりあえず、リファレンスを読み漁った。

docs.microsoft.com

この中に クロスオリジン リソース共有 (CORS) というセクションがあるのだが、どうやら次のコードを貼り付けてくれとのこと。

app.UseCors(policy => 
    policy.WithOrigins("http://localhost:5000", "https://localhost:5001")
    .AllowAnyMethod()
    .WithHeaders(HeaderNames.ContentType, HeaderNames.Authorization, "x-custom-header")
    .AllowCredentials());

だが、どこに貼り付ければいいのかわからず、もう一つのリファレンスへ。

docs.microsoft.com

ここに書かれている次のコードを見てみると、どう見ても Blazor Server 向けになっている。特にこのへんとか

f:id:takunology:20220202050952p:plain

一応試してみたが、そもそも UseCors() メソッドがない。

f:id:takunology:20220202051715p:plain

なのでこの方法は断念。

2. NuGet パッケージからの追加

どうやら、HttpConfiguration クラスの EnableCors() メソッドを使う手があるらしい?とのことなので、とりあえずパッケージを追加。

Install-Package Microsoft.Extensions.Http -Version 6.0.0

Program.cs に下記を追記。

HttpConfiguration configuration = new HttpConfiguration();
configuration.EnableCors();

Index.razorAPI を呼び出すメソッドに属性を追記。

@code
{
    [EnableCors(origins: "https://localhost:7159", headers: "*", methods: "*")]
    async Task FireworksButton()
    {
        using (HttpClient client = new HttpClient())
        {
            await client.GetAsync("<関数アプリの URL>");
        }
    }
}

しかし、実行結果は変わらなかったので断念。

3. HttpRequestMessage クラスの使用

次に、こんな記事を見つけた。

www.syncfusion.com

これによると、httpRequest.SetBrowserRequestMode(BrowserRequestMode.Cors)) をつけろということなので、Index.razor を少し変更。Cors を NoCors にすることで、CORS を使わない宣言することができるようになるみたい。

@code
{
    async Task FireworksButton()
    {
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "<関数アプリの URL>");
        request.SetBrowserRequestMode(BrowserRequestMode.NoCors);
        
        using (HttpClient client = new HttpClient())
        {
            await client.SendAsync(request);
        }
    }
}

実行してみると、確かにエラーは出なくなったが、2回目以降が実行できない。もしかしたらレスポンス待ちになってしまっているのかもしれない。

f:id:takunology:20220202055930p:plain

ということでこれも断念。