.NET Core Blazor + MicroCMSでブログサイト作成⑤
これから数回に渡って、ブログページの作成に取り掛かります。
今回やることは以下のとおりです。
- タグ一覧、ブログ記事一覧の取得アクションを作ってコントローラーを完成させる
- タグ一覧を画面に表示する
- ブログ一覧を表示する
コントローラーを完成させる
サイトデータ取得のアクションをずっと扱ってきましたが、タグ一覧やブログ一覧を取得するときと異なるのはリクエストURLとデシリアライズ先のクラスのみです。
そのためリクエスト送信~デシリアライズを共通関数として切り出します。
// 完成版
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
にタグ一覧、ブログ記事一覧を取得する処理を追記します。
// 抜粋
@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...」だと格好がつかないので、とりあえずスピナーを置いておきます。
@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());
}
}
}
デバッグするとこんな感じです。
👇
ちゃんとしたタイトルに変えました。
ブログ一覧表示
これもデータがあるので、よしなに表示させるだけです。
@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());
}
}
}
<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
テンプレート内でJSTに変換する必要があります。
いろいろググるとタイムゾーン情報を取得して変換するように勧められますが、日本国内で開発する分にはDateTime.ToLocaleTime()
でOKです。
次回
とりあえず基本的な表示部分はできたので、次回は処理の最適化を行います。
- データフェッチの並列化
- 取得データを必要なものにしぼる(現在、ブログ一覧表示のために本文データまで取得しているが不要なので取得しないようにする)
- (ちょっと内容足りないと思うので)いろいろスタイリング
これを行った後、タグ検索、記事表示を実装していきます。
Discussion