Blazor Server/Clientの実装をもっと試したかったので作ってみました。
あとでデータベースも扱ってみたいということもあり、ある程度のデータがあるはてなブログの情報を取得してみます。
構成
- VS2022
- .NET6
- Blazor WebAssemblyプロジェクト
- HTTPS用の構成
- ASP.NET Core Hosted
↑と同じ構成です。githubにソースもあるので、そこをベースに進めていきます。
事前準備
記事情報を取得するためには認証が必要になります。
その際のパスワード扱いとなるAPIキーを取得します。
はてなブログのAPIキー取得
はてなブログの管理ページから
[アカウント設定] - [基本設定] - [APIキー]
に進み、キーを取得します。
サーバー側の実装
サーバー側は、クライアントからの要求を受け付けたら、はてなブログに処理を要求します。
せっかくWebAssemblyの構成で作っているので、パース処理とか重そうな処理はクライアント側でやらせます。
そのため、サーバ側は受け取った文字列をそのまま文字列でクライアント側に返します。
コントローラーの実装
[Route("api/[controller]")] [ApiController] public class HatenaEntryController : ControllerBase { private readonly HttpClient _client; public HatenaEntryController(IHttpClientFactory factory) { _client = factory.CreateClient("HatenaEntry"); } [HttpGet] public string Get() { return GetAsync().Result; } private async Task<string> GetAsync() { var ret = await _client.GetAsync("entry"); if (!ret.IsSuccessStatusCode) { return string.Empty; } return await ret.Content.ReadAsStringAsync(); } }
ここは前回と変わらずです。
最初に書いたように文字列でそのまま返しています。
そして相変わらずエラー処理は皆無です。
BASIC認証
ここが個人的に一番難解でした。
認証はわかるけど、その種類なんか知らんし何をどう設定したらいいかもわからない。
調べるキーワードもわからないのがなかなかしんどい。
その分動いたとき一番楽しいですが。
builder.Services.AddHttpClient("HatenaEntry", client => { client.BaseAddress = new Uri("https://blog.hatena.ne.jp/<user name>/<domain name>/atom/"); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("<user name>:<API Key>")) ); });
BASIC認証はこんな感じで動くらしいです。
ほかにもWSSE認証とかいろいろ種類あるらしいですが今はもうおなか一杯です。
クライアント側の実装
クライアント側は、今回はパースして表示するだけですがもっとWebAssemblyっぽさを体験するためにやりたいことが貯まってます。
Microsoft.SyndicationFeed.ReaderWriter
今回取得したデータはATOMという形式?らしく、RSSとかFeedとかの仲間らしいです。
そっちも知らんけど。
で、その形式を読み書きしてくれるパッケージを見つけたので取得してインストールしておきます。
Microsoft.SyndicationFeed.ReaderWriterです。
ページの追加
ここも前回と変わらずなので割愛します。
読み込み処理の実装
GitHubのサンプルのほぼコピペですが、相当使いにくいです。
private List<AtomEntry> _items; private async void OnClick(MouseEventArgs e) { var stream = await _client.GetStreamAsync("api/HatenaEntry"); _items = new List<AtomEntry>(); using (var xmlReader = XmlReader.Create(stream, new XmlReaderSettings() { Async = true })) { var feedReader = new AtomFeedReader(xmlReader); while (await feedReader.Read()) { switch (feedReader.ElementType) { // Read category case SyndicationElementType.Category: ISyndicationCategory category = await feedReader.ReadCategory(); break; // Read Image case SyndicationElementType.Image: ISyndicationImage image = await feedReader.ReadImage(); break; // Read Item case SyndicationElementType.Item: if (await feedReader.ReadItem() is AtomEntry entry) { _items.Add(entry); } break; // Read link case SyndicationElementType.Link: ISyndicationLink link = await feedReader.ReadLink(); break; // Read Person case SyndicationElementType.Person: ISyndicationPerson person = await feedReader.ReadPerson(); break; // Read content default: ISyndicationContent content = await feedReader.ReadContent(); break; } } } }
出来上がったもの
作成中のこの記事まで拾ってきてくれてるし、全記事取得するにはもうひと手間いりそうですが、とりあえず取得できました。
おしまい
次以降は、この取得したデータに対してフィルタかけたりデータベース化したりと、WebAssembly側に負荷を与えていこうと思います。