放浪軍師のアプリ開発局

Xamarin.Formsを使ってAndroid,iOS,UWP,WPFで動くアプリを開発したりしています。他にもいろいろやってます。尚、このブログはわからないところを頑張って解決するブログであるため、正しい保証がありませんのでご注意ください。

Blazor WebAssembly と Azure Functions を Azure Static Web Apps で公開する

次の休日まであと50日もあるという現実に打ちひしがれている放浪軍師です。みなさん頑張りましょうね…。さて今回は表題の件をやってみようと思います。

言葉の説明

今までこのブログでは紹介していない言葉が並んでいるので、そこから説明していきたいと思います。

Blazor WebAssembly

docs.microsoft.com 前に紹介していますが一応。C# で静的 Web アプリが作れてしまう凄いやつです。フロントエンドだけでなくバックエンドも同時に作れてしまうので、一部のコードを共通化したりできて非常に便利!複数言語なんて覚えてられるか!C# で全部やらせろ!って私みたいなタイプには特にお勧めです。(但しCSSやhtmlやjsの知識はどうしても必要)。尚、Blazor Serverというものもありますが、詳しくはリンク先を読んでください(ぶん投げ)。要は動画をダウンロードして見る(WebAssembly)のかストリーミングで見る(Server)のかの違いみたいなもんです。たぶん。

Azure Functions

docs.microsoft.com サーバーを用意することなくバックエンド側の処理を作れてしまう凄いやつです。いわゆるサーバーレス。例えば WebApi を作ったりデータベースに書き込んだりとか色々できます。サーバーを立てるとなるとインフラ構築の手間がかかりますが、サーバーレスならそれが一切不要となりますので、コーディングだけに集中できます。稼働時間分だけお金を取られるシステムなのでそこは注意が必要。

Azure Static Web Apps

docs.microsoft.com GitHub (や Azure DevOps) を介して静的アプリと Azure Functions を公開できる凄いやつです。こちらもサーバーレス。該当ブランチに push するだけで自動でビルドしてデプロイして全世界公開してしまうというまさに夢のようなサービス。認証まわりも付いていて至れり尽くせり。結構な範囲を無料で使えるのでお財布にも優しい。一般公開されたのは2021/05/12というまさにナウなヤング向けの最新サービス。

Blazor WebAssembly と Azure Functions を Azure Static Web Apps で公開する

上記3つを利用して「温度と時間を WebApi で取得して表示する Web アプリ」を公開してみます。燻製アプリの前哨戦ってところですかね。ちょっとハマりポイントもあるので細かくいくよー。

環境

Microsoft Visual Studio Community 2019 Version 16.8.2 Microsoft.AspNetCore.Components.WebAssembly 5.0.5

GitHub

参考になれば幸い github.com

プロジェクトの作成

  1. 新しいプロジェクトの作成で「空のソリューション」を好きな名前で作成。

  2. ソリューションエクスプローラーからソリューションを右クリックして「追加 → 新しいプロジェクト」で Blazor アプリを選択。

  3. プロジェクト名を Client に変更。

  4. 詳細設定の「ASP.NET Corehosted」にはチェックを入れないで作成。

  5. ソリューションエクスプローラーからソリューションを右クリックして「追加 → 新しいプロジェクト」で Azure Functions を選択。

  6. プロジェクト名を Api に変更。

  7. Http trigger を選択し作成。

  8. ソリューションエクスプローラーからソリューションを右クリックして「追加 → 新しいプロジェクト」で 「クラスライブラリ(.NET Standard)」 を選択。

  9. プロジェクト名を Data に変更して作成。

完成した構造

ソリューションエクスプローラ

f:id:roamschemer:20210602115443p:plain:w300

ディレクト

f:id:roamschemer:20210602115606p:plain:w300

この手順を取った理由

空のプロジェクトを作らずにいきなり Blazor プロジェクトを作ってしまうと、プロジェクト名にソリューション名が付いてしまい、その後追加する Functions プロジェクトや共通で使用するクラスライブラリプロジェクトの名前にはソリューション名が付かないという気持ち悪い状態が発生してしまいます。逆に Functions などにソリューション名を付けて作成しようとするとフォルダ構造が崩れ、後述する Azure Static Web Apps を使用する際の障害になります。これらを嫌ったため、このような手順を取ってプロジェクトを作成しました。この辺を気にしないというのであれば普通に作成してもらってもかまわないと思います。もし「ソリューション名.プロジェクト名」みたいにしたい場合は、ソリューションエクスプローラから名前を変更すれば、ディレクトリ構造を変更することなくプロジェクト名を変更できますのでお試しください。ま、要するに上のようなディレクトリ構造であればOKです!

構造説明

  1. Api プロジェクト

    Azure Functions 用のプロジェクトです。バックエンド側と思ってもらえば大体大丈夫かと思います。

  2. Client プロジェクト

    Blazor WebAssembly 用のプロジェクトです。フロントエンド側と思ってもらえば大体大丈夫かと思います。

  3. Data プロジェクト

    共通で使うクラスライブラリです。便利だね!

Data プロジェクト

まず共通で使う Data プロジェクトを改造しましょう。今回は時間と温度を取得するのでその為の TempData クラスを作りました。

using System;

namespace Data {
    public class TempData {
        public DateTime DateTime { get; set; }
        public double Temp { get; set; }
    }
}

Api プロジェクト

次にバックエンド側である Api プロジェクトを改造し、時刻と温度を返す TempDataFunction.cs を作成しました。温度は0℃~99.9℃の間でランダム取得します。また、「追加→参照」で Data プロジェクトを参照するのを忘れないようにしてください。TempData クラスが使えませんからね。

using System;
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 Data;

namespace Api {
    public static class TempDataFunction {
        [FunctionName("Temp")]
        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.");

            var r = new Random(DateTime.Now.Millisecond);
            var tempData = new TempData() {
                DateTime = DateTime.Now,
                Temp = r.Next(0, 1000) * 0.1
            };

            return new OkObjectResult(tempData);
        }
    }
}

この状態で Api プロジェクトを実行するとコマンドプロンクトが立ち上がり、以下のような画面がでます。

f:id:roamschemer:20210602225528p:plain

そこで Chrome とかの Web ブラウザを開いて表示された URL にアクセスすると、先ほど作成した TempDataFunction からの返答が表示されます。クラスを返すと json の形で返ってくるみたいですね。便利だね!

f:id:roamschemer:20210602225835p:plain

Client プロジェクト

さて最後にフロントエンド側を作成します。今回は実験なので初期表示画面の Pages/Index.razor を改造してしまいましょう。ボタンを押したら先ほどの Functions を叩いて時刻と温度を受け取り、テーブルに追加していくイメージです。こちらも、「追加→参照」で Data プロジェクトを参照するのを忘れないようにしてください。TempData クラスが使えませんからね。

@page "/"
@inject HttpClient Http
@using Data

<h3>Blazor WebAssembly と Azure Functions を Azure Static Web Apps で公開する放浪軍師</h3>

<button class="btn btn-primary" @onclick="GetTempData">Get TempData</button>

@if (tempDatas == null) {
    <p><em>Loading...</em></p>
} else {
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp(℃)</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var tempData in tempDatas) {
                <tr>
                    <td>@tempData.DateTime</td>
                    <td>@tempData.Temp</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private List<TempData> tempDatas = new List<TempData>();

    private async Task GetTempData() {
        var tempData = await Http.GetFromJsonAsync<TempData>("api/temp");
        tempDatas.Add(tempData);
    }

}

さてここで動作を確認したいところですが…これどうやってデバッグするんでしょうね?調べてみたけど分からんかったッス。まぁバックエンドへのアクセスは流石に無理で、デバッグ時は DI で仮のデータをとれるようにするのが正解かな?

GitHub にプッシュする

さて最後にこのソリューションを GitHub にプッシュしておきます。説明は割愛。なお master ブランチを使用しています。

Azure Static Web Apps で公開する

さていよいよ公開です。手順は以下の通り。

  1. Microsoft Azure のポータルサイトにサインインする。

  2. 「すべてのサービス」から「静的Webアプリ」を選択。

  3. 「追加」を選択。

  4. 以下のような感じにする

    f:id:roamschemer:20210602233127p:plain:w300

  5. GitHub でログインを選択し、「Authorize Azure-App-Service-Static-Web-Apps」と書かれた緑色のボタンを押す。

  6. パスワードを要求されたら入力する。

  7. 以下のような感じにする。ここでアプリの場所とApiの場所に関してはディレクトリ構造に従う事。

    f:id:roamschemer:20210602233500p:plain

  8. 確認および作成を押して作成を押してしばらく待つ。

  9. デプロイが完了しましたと表示されるのでリソースに移動を押す。

  10. 以下のようなメッセージが確認できます。

    f:id:roamschemer:20210602234202p:plain

    このタイミングでURLが発行されますが、この時点ではアクセスしても「Your Azure Static Web App is live and waiting for your content」と表示されるだけでまだ公開はできていません。赤枠の部分をクリックします。

  11. GitHubリポジトリで Actions というページに移動します。ここでデプロイ状況が確認でき、緑のチェックが入ったら公開されます。

    f:id:roamschemer:20210602234415p:plain

  12. 先ほどのページに戻り、URL にアクセスすると公開されていることが確認できます。

  13. Get TempData ボタンを押すと時間と温度が取得できます。

    https://gentle-ocean-0ce08e300.azurestaticapps.net/

  14. URL/api/temp にアクセスすると、jsonが確認できます。バックエンドも正しく動いてますね!

    https://gentle-ocean-0ce08e300.azurestaticapps.net/api/temp

  15. ちなみにこの状態で master ブランチが更新されると自動でデプロイして公開までされます。マジで凄いね!

まとめ

驚くほど簡単に静的Webアプリケーションとバックエンドが完成してしまいました。デプロイもクッソ楽だし、これはもう使っていくしかねーですね!!!