📖

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

2021/08/28に公開

今回はMicroCMSにブログ記事やメタデータのスキーマを登録し、お試しデータを追加します。
その後Controllerを介してAPIリクエスト/レスポンスのやり取りをし、画面に反映させる実装を行います。

MicroCMSの設定

サイトデータ(オブジェクト形式)

タグ(リスト形式)

記事(リスト形式)

データ受け取り用のクラス作成

MicroCMSから取得したデータをデシリアライズ(JSON⇒オブジェクトの変換)してクライアントに渡すためのクラスを作成します。
https://docs.microsoft.com/ja-jp/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-5-0

作成場所はBlazorBlog.Shared/MicroCMSです。
MicroCMSフォルダをBlazorBlog.Shared下に作成して、クラスの追加をします。

各クラスのメンバー名をMicroCMSで設定したフィールドIDと同じにすることでデシリアライズが可能です。別名にする場合はクラスのメンバーに[JsonPropertyName("JSONプロパティ名")]アノテーションを付けます。
https://docs.microsoft.com/ja-jp/dotnet/standard/serialization/system-text-json-customize-properties

また大文字/小文字の違いは区別されますが、これはデシリアライズの設定で無視するようにできます。
https://docs.microsoft.com/ja-jp/dotnet/standard/serialization/system-text-json-character-casing

SiteData.cs

BlazorBlog.Shared/MicroCMS/SiteData.cs
using System;
using System.Text.Json.Serialization;

namespace BlazorBlog.Shared
{
    public class SiteData
    {
        [JsonPropertyName("title")]
        public string BlogTitle { get; set; }

        public DateTime CreatedAt { get; set; }

        public DateTime UpdatedAt { get; set; }

        public DateTime PublishedAt { get; set; }

        public DateTime RevisedAt { get; set; }
    }
}

Tags.cs

BlazorBlog.Shared/MicroCMS/Tags.cs
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace BlazorBlog.Shared
{
    public class Tags
    {
        [JsonPropertyName("contents")]
        public List<TagContents> TagList { get; set; }

        public int TotalCount { get; set; }
        
        public int Offset { get; set; }

        public int Limit { get; set; }
    }

    public class TagContents
    {
        public string Id { get; set; }

        public DateTime CreatedAt { get; set; }

        public DateTime UpdatedAt { get; set; }

        public DateTime PublishedAt { get; set; }

        public DateTime ReviesdAt { get; set; }

        public string Name { get; set; }
    }
}

記事

BlazorBlog.Shared/MicroCMS/Blogs.cs
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace BlazorBlog.Shared
{
    public class Blogs
    {
        [JsonPropertyName("contents")]
        public List<BlogContents> BlogList { get; set; }

        public int TotalCount { get; set; }

        public int Offset { get; set; }

        public int Limit { get; set; }
    }

    public class BlogContents
    {
        public string Id { get; set; }

        public DateTime CreatedAt { get; set; }

        public DateTime UpdatedAt { get; set; }

        public DateTime PublishedAt { get; set; }

        public DateTime RevisedAt { get; set; }

        public string Title { get; set; }

        public TagContents Tag { get; set; }

        public string Body { get; set; }
    }
}

appsettings.json

MicroCMSへリクエストを送るURLを記述します。

BlazorBlog.Server/appsettings.json
{
  "AllowedHosts": "*",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  // 以下追記
  "MicroCMS": {
    "BaseUrl": "https://[サービスID].microcms.io/api/v1/",
    "X-API-KEY": "MicroCMSの管理画面から確認します",
    "Endpoints": {
      "SiteData": "sitedata",
      "Tags": "tags",
      "Blogs": "blogs"
    }
  }
}

Controller

MicroCMSにHTTPSリクエストを送り、デシリアライズして、クライアントに渡すためのコントローラーを作成します。
デフォルトで作成されているWeatherForecastController.csをパクって作ります。

BlazorBlog.Server/Controllers/MicroCMSController.cs
using BlazorBlog.Shared;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

namespace BlazorBlog.Server.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class MicroCMSController : ControllerBase
    {
        // EntityFrameworkを使うときのロガー。今回は使わないけどとりあえず置いとく。
        private readonly ILogger<MicroCMSController> _logger;
	// appsettings.jsonのDI。後述。
        private readonly IConfiguration _configuration;

        public MicroCMSController(ILogger<MicroCMSController> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
        }

        /// <summary>
        /// サイトデータ取得
        /// </summary>
        /// <returns>サイトデータ</returns>
        [HttpGet]
        public async Task<SiteData> Get()
        { 
            // APIコールのベースURL
            var baseUrl = _configuration.GetSection("MicroCMS").GetValue<string>("BaseUrl");
            // エンドポイント
            var endpoint = _configuration.GetSection("MicroCMS").GetSection("Endpoints").GetValue<string>("SiteData");
            // X-API-KEY
            var apikey = _configuration.GetSection("MicroCMS").GetValue<string>("X-API-KEY");
            // URL全体
            string url = baseUrl + endpoint;

            using (var client = new HttpClient())
            {
                // ヘッダーに認証キーを登録
                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
                };
                SiteData deserialisedResult = JsonSerializer.Deserialize<SiteData>(result, options);

                return deserialisedResult;
            }
        }
    }
}

appsettings.jsonのDI

上記ではprivate readonly IConfiguration _configurationをメンバーに置き、コンストラクタで初期化しています。
本来はこのやり方はよろしくないです。DIはプロジェクトの起動時にまとめて行われるべきもので、各クラスで個別に行われるものではないからです(多分)。
「通常はコントローラーに IConfiguration を直接挿入しないでください」と公式に言われています。👇
https://docs.microsoft.com/ja-jp/aspnet/core/mvc/controllers/dependency-injection?view=aspnetcore-5.0#access-settings-from-a-controller

テンプレート

BlazorBlog.Client/Pages/FetchData.razor
@page "/fetchdata"
@using BlazorBlog.Shared
@inject HttpClient Http

<h1>Blog</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (sitedata == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <p>@sitedata.Title</p>
}

@code {
    private SiteData sitedata;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            sitedata = await Http.GetFromJsonAsync<SiteData>("MicroCMS");
        }
        catch (Exception ex)
        {
	    // エラー処理もホントはちゃんとやらないと...
            Console.WriteLine(ex.ToString());
        }
    }
}

デバッグ

コントローラー内のusing内にブレークポイントを置いて、デバッグしてみます。


     👇

(Bootstrap5を使うためにちょっとデザイン変えてます)
     👇

リクエストするURLもきちんと構築されています。

少しステップ実行してみて、レスポンスを受け取ってみます。
ステータス200で正常に返ってきたようです。

少しデバッグを進めてデシリアライズさせてみると、きちんとSiteDataクラスにデータを格納して受け取れています。

これをテンプレートに返すことで、画面に表示することができます。

ここまでの実装をTagsBlogsでも行えば、データを取得することができます。

JsonPropertyNaneアノテーションを外すと...?

試しにSiteData.cs[JsonPropertyName("title")]を外してみます。

ブレークポイントなしでデバッグ実行すると、Fetch Dataページにアクセスしてもタイトルが出てきません。

MicroCMSController.csreturn deserialisedResult;にブレークポイントをおいてもう1回アクセスしてみます。

残念ながら、BlogTitleにはnullが入っています。NullPointerException的な例外は出ないんですね。

他にもJSONシリアライズ/デシリアライズに関するおもしろいトピックスがあるので、見てみてください。👇
https://devadjust.exblog.jp/28571843/

次回

  • タグ一覧取得、ブログ記事一覧取得のアクションを定義するために、エンドポイントの設定を変えます。
  • appsettings.jsonのDI問題を解決します。

Discussion