📖

.NET Core Blazor + MicroCMSでブログサイト作成⑤

2021/09/02に公開

これから数回に渡って、ブログページの作成に取り掛かります。
今回やることは以下のとおりです。

  • タグ一覧、ブログ記事一覧の取得アクションを作ってコントローラーを完成させる
  • タグ一覧を画面に表示する
  • ブログ一覧を表示する

コントローラーを完成させる

サイトデータ取得のアクションをずっと扱ってきましたが、タグ一覧やブログ一覧を取得するときと異なるのはリクエストURLデシリアライズ先のクラスのみです。
そのためリクエスト送信~デシリアライズを共通関数として切り出します。

MicroCMSController.cs
// 完成版
using BlazorBlog.Shared;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

namespace BlazorBlog.Server.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class MicroCMSController : ControllerBase
    {
        private readonly MicroCMSSettingModel apiSettings;

        public MicroCMSController(IOptions<MicroCMSSettingModel> setting)
        {
            apiSettings = setting.Value;
        }

        /// <summary>
        /// サイトデータ取得
        /// </summary>
        /// <returns>サイトデータ</returns>
        [Route("SiteData")]
        public async Task<SiteData> GetSiteData()
        {
            string url = apiSettings.BaseUrl + apiSettings.Endpoints.SiteData;
            string apikey = apiSettings.ApiKey;

            SiteData result = await Fetch<SiteData>(url, apikey);

            return result;
        }

        /// <summary>
        /// タグ一覧取得
        /// </summary>
        /// <returns>タグ一覧</returns>
        [Route("Tags")]
        public async Task<Tags> GetTags()
        {
            string url = apiSettings.BaseUrl + apiSettings.Endpoints.Tags;
            string apikey = apiSettings.ApiKey;

            Tags result = await Fetch<Tags>(url, apikey);

            return result;
        }

        /// <summary>
        /// ブログ記事一覧取得
        /// </summary>
        /// <returns>ブログ記事一覧</returns>
        [Route("Blogs")]
        public async Task<Blogs> GetBlogs()
        {
            string url = apiSettings.BaseUrl + apiSettings.Endpoints.Blogs;
            string apikey = apiSettings.ApiKey;

            Blogs result = await Fetch<Blogs>(url, apikey);

            return result;
        }

        /// <summary>
        /// リクエストを送信し、取得した結果(JSON)をデシリアライズして返します。
        /// </summary>
        /// <typeparam name="T">取得結果クラス</typeparam>
        /// <param name="url">リクエストURL</param>
        /// <param name="apikey">X-API-KEY</param>
        /// <returns>デシリアライズされたデータ</returns>
        private static async Task<T> Fetch<T>(string url, string apikey)
        {
            using (var client = new HttpClient())
            {
                // リクエストヘッダーにX-API-KEYを追加
                var request = new HttpRequestMessage(HttpMethod.Get, url);
                request.Headers.Add("X-API-KEY", apikey);

                // リクエスト送信
                var response = await client.SendAsync(request);

                // レスポンスから結果を取り出し
                var result = await response.Content.ReadAsStringAsync();

                // デシリアライズ(プロパティ名の大文字/小文字の差異を無視)
                var options = new JsonSerializerOptions
                {
                    PropertyNameCaseInsensitive = true
                };
                T deserialisedResult = JsonSerializer.Deserialize<T>(result, options);

                return deserialisedResult;
            }
        }
    }
}

データを取得してみる

Blog.razorにタグ一覧、ブログ記事一覧を取得する処理を追記します。

Blog.razor
// 抜粋
@code {
    private SiteData sitedata;
    private Tags tags;
    private Blogs blogs;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            sitedata = await Http.GetFromJsonAsync<SiteData>("MicroCMS/SiteData");
	    // 追記
            tags = await Http.GetFromJsonAsync<Tags>("MicroCMS/Tags");
	    blogs = await Http.GetFromJsonAsync<Blogs>("MicroCMS/Blogs");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}

デベロッパーツールから、ちゃんとデータがとれていることが確認できました。

非同期で取得しているものの3回の取得にすべてawaitがついているので、直列処理になってます。
サイトが完成した後に、機能改善として並列化できないか調査してみます。
Http.GetFromJsonAsyncを3回分よーいどんで実行する方法があるはずなので、調べておきます。(詳しい方はコメントください(´°̥̥̥̥̥̥̥̥ω°̥̥̥̥̥̥̥̥`))

タグ一覧表示

データは持っているので、ボタン表示させてみます。
ついでにデータフェッチ中が「Loading...」だと格好がつかないので、とりあえずスピナーを置いておきます。

Blog.razor
@page "/blog"
@using BlazorBlog.Shared
@inject HttpClient Http

@if (sitedata == null)
{
    // スピナー
    <div class="d-flex justify-content-center">
        <div class="spinner-border" style="width: 3rem; height: 3rem;" role="status">
            <span class="visually-hidden">Loading...</span>
        </div>
    </div>
}
else
{
    <h1>@sitedata.BlogTitle</h1>

    <div class="mb-5">
        @foreach (TagContents tag in tags.TagList)
        {
	    // サイドが丸い黒ボタン
            <button type="button" class="btn btn-dark rounded-pill mx-2">@tag.Name</button>
        }
    </div>
}

@code {
    private SiteData sitedata;
    private Tags tags;
    private Blogs blogs;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            // TODO:並列化
            sitedata = await Http.GetFromJsonAsync<SiteData>("MicroCMS/SiteData");
            tags = await Http.GetFromJsonAsync<Tags>("MicroCMS/Tags");
            blogs = await Http.GetFromJsonAsync<Blogs>("MicroCMS/Blogs");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}

デバッグするとこんな感じです。


     👇

ちゃんとしたタイトルに変えました。

ブログ一覧表示

これもデータがあるので、よしなに表示させるだけです。

Blog.razor
@page "/blog"
@using BlazorBlog.Shared
@inject HttpClient Http

@if (sitedata == null)
{
    <div class="d-flex justify-content-center">
        <div class="spinner-border" style="width: 3rem; height: 3rem;" role="status">
            <span class="visually-hidden">Loading...</span>
        </div>
    </div>
}
else
{
    <h1>@sitedata.BlogTitle</h1>

    <div class="mb-5">
        @foreach (TagContents tag in tags.TagList)
        {
            <button type="button" class="btn btn-dark rounded-pill mx-2">@tag.Name</button>
        }
    </div>

    // 追加
    @foreach (BlogContents contents in blogs.BlogList)
    {
        <BlogCard title=@contents.Title tag=@contents.Tag.Name publishedAt=@contents.PublishedAt />
    }
}

@code {
    private SiteData sitedata;
    private Tags tags;
    private Blogs blogs;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            sitedata = await Http.GetFromJsonAsync<SiteData>("MicroCMS/SiteData");
            tags = await Http.GetFromJsonAsync<Tags>("MicroCMS/Tags");
            blogs = await Http.GetFromJsonAsync<Blogs>("MicroCMS/Blogs");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}
Shared/BlogCard.razor
<div class="row my-3 d-flex justify-content-center">
    <div class="col-12 col-md-8 border border-3 rounded-3">
        <div class="m-3">
            <h2>@title</h2>
            <p>Tag:@tag</p>
            <p>Publish:@publishedAt.ToLocalTime().ToString("yyyy/MM/dd")</p>
        </div>
    </div>
</div>

@code {
    [Parameter]
    public string title { get; set; }

    [Parameter]
    public string tag { get; set; }

    [Parameter]
    public DateTime publishedAt { get; set; }
}

デバッグするとこんな感じです。テスト用記事はテキトーに追加してください。

【注意】MicroCMSのDateTimeはUTC

https://document.microcms.io/faq/pa2gfm7xf

テンプレート内でJSTに変換する必要があります。
いろいろググるとタイムゾーン情報を取得して変換するように勧められますが、日本国内で開発する分にはDateTime.ToLocaleTime()でOKです。

次回

とりあえず基本的な表示部分はできたので、次回は処理の最適化を行います。

  • データフェッチの並列化
  • 取得データを必要なものにしぼる(現在、ブログ一覧表示のために本文データまで取得しているが不要なので取得しないようにする)
  • (ちょっと内容足りないと思うので)いろいろスタイリング

これを行った後、タグ検索、記事表示を実装していきます。

Discussion