BlazorWebAssemblyとAzureFunctionsではてなブックマークの人気エントリーを表示するWebサービスを作ってみた

## はじめに
本記事は小島優介さんが運営するコミュニティである[ServerlessLT大会初心者向け](https://serverlesslt.connpass.com/)という場で7月に発表した内容を記事に起こしたものです。(期間が空いてしまいました。。)
発表経験が少なかったですし、すごく初歩的な内容でしたが、聴講者の皆さんから「ナイスチャレンジ！」などポジティブフィードバックをたくさんもらえたのでとても敷居が低いと思います。毎月開催されるので興味のある方はぜひ参加してみてください。

## 使用した技術
フロントエンドにBlazorWebAssembly、サーバーサイドにAzureFunctionを使用しました。
BlazorWebAssemblyについては[こちら](https://qiita.com/minoura_a/items/9d2cc884903d74bde324)にまとめたので参照ください。
AzureFunctionについて簡単に説明すると、
- クラウド上でコード(関数)をサーバなしに手軽に実行できるサービス
- 利用者はサーバの管理を気にする必要はなく、アプリケーションロジックに集中できる
- ローカルで動作確認できる
といった感じでお手軽に利用することができます。

## はてなブックマークの人気エントリーを表示させるためにやったこと
やったことはとても簡単です。

- VisualStudioでAzureFunctionのプロジェクトを立ち上げるとテンプレートの関数があるので、その中身を書き換えて、はてなブログの人気エントリーを取得するAPIを自作

- VisualStudioでBlazorWebAssemblyのプロジェクトを立ち上げるとテンプレートが表示されるのでそれを書き換えてRazorファイルから自作APIを呼び出し、はてなブログの記事一覧を表示

見た目などはとりあえず置いておいて、とにかく人気エントリーを表示させるということをしました。

## 人気エントリーの取得方法
はてなブログにはRSSというWebサイトの更新情報をXML形式で配信する仕組みがあるのでそのXMLを解析して記事のタイトルとURLのディクショナリを作成し、Json形式に変換して返すというAPIを自作しました。(RSSという技術は初めて知りましたが、昔はブログなどの更新を取得するためによく利用されていたようです。今はそういった需要が無くなって廃れつつあるようです。。)


下記のようなXML形式のデータを得られます。
![2020-12-07_17h16_21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562946/a71656be-d6f5-da3f-096e-ba29f1428f5b.png)
XMLの解析は以下のようにして行いました。

まずはchannelを取得し、channelの中にあるitemを全て取得します。
item一つ一つがブログの情報になっているので、欲しい情報であるtitleとlinkを抽出します。
コードは以下です。


``` csharp
        #region フィールド

		/// <summary>
		/// はてなブログのRSSを取得するためのURL
		/// </summary>
		private static string m_feedUrl = "https://blog.hatenablog.com/rss";

		#endregion

		#region 公開サービス

		/// <summary>
		/// はてなブログのRSSをXMLを解析してブログタイトルとURLのリストをJson形式で返す
		/// </summary>
		/// <param name="req"></param>
		/// <param name="log"></param>
		/// <returns></returns>
		[FunctionName("Function1")]
		public static async Task<IActionResult> Run(
			[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
			ILogger log)
		{
			// Rssからchannelを読み込む
			var element = XElement.Load(m_feedUrl);
			var channelElement = element.Element("channel");

			// itemを読み込む
			var itemElements = channelElement?.Elements("item");

			// itemからブログTitleとLinkを抽出して独自のJson形式に変換する
			var rssInfo = new RssInfo();
			foreach (var item in itemElements)
			{
				rssInfo.TitleList.Add(item.Element("title")?.Value);
				rssInfo.UrlList.Add(item.Element("link")?.Value);
			}
			var jsonData = JsonConvert.SerializeObject(rssInfo);
			return new OkObjectResult(jsonData);
		}

		#endregion
```


``` csharp
	/// <summary>
	/// RSSから取得した情報
	/// </summary>
	public class RssInfo
	{
		/// <summary>
		/// タイトルリスト
		/// </summary>
		[JsonProperty]
		public List<string> TitleList { get; set; } = new List<string>();
		
		/// <summary>
		/// URLリスト
		/// </summary>
		[JsonProperty]
		public List<string> UrlList { get; set; } = new List<string>();
	}
```

上記のような感じではてなブログの情報を取得するAPIを作ったら、Azureポータルにリソースを作って発行します。

発行ができたら、AzureポータルのCORS設定というものを行います。
APIを呼び出す側のドメインをここに追加することでアクセスを許可できます。
CORSについては[こちら](https://qiita.com/att55/items/2154a8aad8bf1409db2b)の記事が分かりやすかったです。
![2020-12-07_17h38_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562946/de04a730-c6bc-6ba1-1da2-5d4c874eb80f.png)
また、ローカルでデバッグしたい場合は、以下のようにAzureFunctionsのプロジェクト内に存在するlocal.settings.jsonに以下の設定を追加します。

```csharp
"Host": {
    "LocalHttpPort": 7071,
    "CORS": "*",
    "CORSCredentials": false
  }
```

## BlazorからAPIをたたいてブログの情報を表示する
[記事](https://qiita.com/minoura_a/items/9d2cc884903d74bde324)にした要領でAPIをたたきます。
razorファイルの`@code`内でAPIをたたき、取得できたJson文字列をデシリアライズします。
OnInitializedAsyncをオーバライドしてAPIをたたきました。OnInitializedAsyncはページがレンダリングされるタイミングで実行されるみたいです。詳しくは[こちら](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/components/lifecycle?view=aspnetcore-5.0)。

```csharp
@code {
    protected override async Task OnInitializedAsync()
    {
        var response = await Http.GetAsync(@"https://hatenaXXXXXXX");
        var jsonData=response.IsSuccessStatusCode ?
            await response.Content.ReadAsStringAsync() :
            "Failed";
        var rssInfo = JsonConvert.DeserializeObject<RssInfo>(jsonData);
    }
```

取得ができたら、razorファイル内の`＠page`部分にHTML形式で出力結果を記述しました。
すると、以下のように表示されました！！
![2020-12-07_18h14_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562946/08c2770c-8782-ac81-1c2d-da34f5bf2678.png)
ちなみに作成したページは[こちら](https://htenablogdisplay20201019183319.azurewebsites.net/)です。

## 詰まった点
開発中つまずいた点を共有します。超初心者なのでつまずき内容も初心者ですが、私と同じようにWeb、AzureFunction、Blazorについて全然知らないという方には少しは役に立つと思います。
- CORSというものを知らなかったので、作成したAPIがたたけない理由がわからなくてつまりました。
- CORS設定をしてもAPIがたたけないこともありました。Azureポータルを確認すると原因不明のエラーが出ていました。リソースを作成し直してもう一度発行すると解消できました。
- BlazorWebAssemblyを使うとVisualStudioが時々不安定な動作をしてしまうことがありました。再起動で解消できました。

##  さいごに
自己学習のためにすごく簡単ではありますが、Webサービスを作成してみました。私もまだまだ勉強中なのでわかり辛いところばかりだったとは思いますが最後までお読みいただきありがとうございます。BlazorやAzureなどこれからも勉強していきたいと思います。

Blazorの情報収集に良さそうなサイトを見つけたので共有しておきます。(英語です。)
[Blazor University](https://blazor-university.com/)