static-web-apps-cliでBlazor WebAssembly + Azure Static Web Apps + Azure FunctionsのHello World

# はじめに

私は最近はBlazor WebAssemblyでアプリを開発をしているのでAzure Static Web Appsを使うことが増えてきましたが、まだBlazor WebAssemblyとAzure Functionsと連携するような機会はありませんでした。来年はそういったシステム構成の開発もしたいと思っているので、その準備としてAzure Static Web AppsとAzure Functionsを連携するHello Worldをやってみます。

以下を参考にしました。

https://github.com/Azure/static-web-apps-cli

https://zenn.dev/okazuki/articles/static-web-apps-cli-getting-start

# static-web-apps-cli を使ってローカルで動作

## Blazor WebAssembly

Visual Studio 2022でBlazor WebAssemblyのアプリを作成します。この記事の例では`SwaSample`で作成しています。アプリを作成したら起動します。私の環境では7012番で起動したので、起動したポート番号を指定して`static-web-apps-cli`を起動します。

```powershell
> npx @azure/static-web-apps-cli start https://localhost:7012
```

`http://localhost:4280`にアクセスすると、Blazor WebAssemblyのアプリの画面が表示されました。

## Azure Functions

Azure Functions Core Toolsをインストールします。

https://github.com/Azure/azure-functions-core-tools

Visual Studio 2022でAzure Functionsのプロジェクトを`Api`という名前で作成して、`HTTP trigger`の関数を作成します。デフォルトでは以下のような関数が作成されました。

```csharp:Api/Function1.cs
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace Api
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string name = req.Query["name"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name = name ?? data?.name;

            string responseMessage = string.IsNullOrEmpty(name)
                ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
                : $"Hello, {name}. This HTTP triggered function executed successfully.";

            return new OkObjectResult(responseMessage);
        }
    }
}
```

Visual Studio 2022から起動すると私の環境では7071番のポートを使用して起動したので、コンソールに`http://localhost:7071/api/Function1`でアクセスできるというメッセージが表示され、Webブラウザでアクセスするとレスポンスが返ってきました。

## Blazor WebAssemblyとAzure Functionsをstatic-web-apps-cliで連携

以下のコマンドでBlazor WebAssemblyとAPIをstatic-web-apps-cliで連携して、どちらも同じホスト名とポート番号でアクセスできるようになります。

```powershell
> npx @azure/static-web-apps-cli start https://localhost:7012 --api-location ./Api
```

Webブラウザで`http://localhost:4280`にアクセスするとBlazor WebAssemblyの画面が表示され、`http://localhost:4280/api/Function1`にアクセスするとAPIのレスポンスが返ってきました。

Blazor WebAssemblyからAPIを呼び出してみます。`Pages/Index.razor`を以下のように編集します。

```csharp:SwaSample/Pages/Index.razor
@page "/"
@inject HttpClient Http

<PageTitle>Index</PageTitle>

<p>
    <input @bind="name" type="text" class="form-control" placeholder="Name" />
</p>

<p>
    <button class="btn btn-primary" @onclick="InvokeAPI">Start</button>
</p>

<p>
    @message
</p>


@code {
    private string? name { get; set; }
    private string? message { get; set; }

    private async Task InvokeAPI()
    {
        try
        {
            message = await Http.GetStringAsync($"/api/Function1?name={name}");
        }
        catch (Exception ex)
        {
            message = ex.ToString();
        }
    }
}
```

テキスト入力欄とボタンのシンプルなフォームが表示されます。テキスト入力欄に名前を入力してボタンをクリックすると、フォームで入力したテキストがAPIに渡されて、レスポンスが表示されました。

![動作画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2238166/52111882-41d3-9f09-47bf-53fa437d029b.png)


# Azure Static Web Appsにデプロイ

Azure Static Web Appsへのデプロイについては以下の手順を参考に進めていきます。

https://qiita.com/kaorumori/items/88d836fab1fcbbbb7114#azure-static-web-apps%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4

`ビルドの詳細`を指定する画面では`APIの場所`にAzure Functionsのプロジェクトが格納されているフォルダーを指定します。

![ビルドの詳細](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2238166/fd4115fa-215b-d685-d9b2-4a10a09cd6fe.png)

GitHub Actionsでビルドが完了したら、WebブラウザでAzure Static Web AppsのURLにアクセスして動作を確認できました。

私の場合、最初はGitHub Actionsのビルドで以下のIssueと同じ`Deployment Failure Reason: Failed to deploy the Azure Functions.`というエラーが表示されてビルドに失敗しました。GitHub Actionsでビルドを再度実行したら今度は成功してデプロイできました。

https://github.com/Azure/static-web-apps/issues/622

# おわりに

`static-web-apps-cli`を利用してBlazor WebAssemblyのアプリとAzure Functionsの関数にローカルでアクセスしました。また、Azure Static Web Appsにそれらをデプロイすることができました。もっと大変かと思っていたのですが、思っていたよりも簡単でよかったです。