📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

 
(同じ利用者による、間の4版が非表示)
60行目: 60行目:
<br>
<br>
なお、<code>@page</code>ディレクティブで明示的にルートを指定した場合は、この規約が上書きされる。<br>
なお、<code>@page</code>ディレクティブで明示的にルートを指定した場合は、この規約が上書きされる。<br>
<br><br>
== レンダーフラグメント ==
レンダーフラグメントは、UIの一部を表すデリゲート型であり、コンポーネント間でコンテンツを受け渡すための仕組みである。<br>
これは、JSXのchildren propやReactのprops.childrenに相当する概念である。<br>
<br>
また、<code>@Body</code>は、このレンダーフラグメントの特別な実装である。<br>
<br>
<syntaxhighlight lang="c#">
[Parameter]
public RenderFragment CustomContent { get; set; }
// 使用例
<div>
    @CustomContent
</div>
</syntaxhighlight>
<br>
レンダーフラグメントは、Blazorにおける重要な型であり、以下に示す用途で使用される。<br>
<syntaxhighlight lang="c#">
// LayoutComponentBaseクラスでの定義
public RenderFragment Body { get; set; }
// カスタムコンポーネントでの使用例
[Parameter]
public RenderFragment ChildContent { get; set; }
// テンプレート用途での使用例
[Parameter]
public RenderFragment<TItem> ItemTemplate { get; set; }
</syntaxhighlight>
<br>
==== @Body ====
<code>@Body</code>は、レイアウトコンポーネント内でページコンテンツを表示する場所を指定するプレースホルダーである。<br>
<br>
@Bodyは、レイアウトを使用する各ページのコンテンツが配置される場所を示す。<br>
<br>
例えば、以下に示すようなページコンポーネントが存在する場合<br>
<syntaxhighlight lang="c#">
// MainLayout.razorファイル
<div class="page">
    <div class="sidebar">
      <NavMenu />
    </div>
    <main>
      <div class="content">
          @Body
      </div>
    </main>
</div>
</syntaxhighlight>
<br>
<syntaxhighlight lang="c#">
// Pages/Counter.razorファイル
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button @onclick="IncrementCount">Click me</button>
@code {
    private int currentCount = 0;
    private void IncrementCount() => currentCount++;
}
</syntaxhighlight>
<br>
上記のCounter.razorファイルの内容が、MainLayout.razorファイルの@Body部分に展開されて表示される。<br>
<syntaxhighlight lang="c#">
// MainLayout.razorファイル
<div class="page">
    <div class="sidebar">
      <NavMenu />
    </div>
    <main>
      <div class="content">
          // ここに、Counter.razorファイルの内容が展開される
          <h1>Counter</h1>
          <p>Current count: 0</p>
          <button>Click me</button>
      </div>
    </main>
</div>
</syntaxhighlight>
<br><br>
<br><br>


192行目: 278行目:


== オプションパラメータ / クエリパラメータ ==
== オプションパラメータ / クエリパラメータ ==
パラメータ処理により、以下に示すようなURLに対応することができる。<br>
* /products
*: カテゴリなし
* /products/1
*: カテゴリID指定
* /products?search=keyboard
*: 検索キーワード指定
* /products/1?search=keyboard&page=2
*: 全てのパラメータ指定
<br>
==== オプションパラメータ ====
==== オプションパラメータ ====
波括弧内に<code>?</code>を付加することにより、実現できる。<br>
波括弧内に<code>?</code>を付加することにより、実現できる。<br>
<br>
<br>
  <syntaxhighlight lang="c#">
  <syntaxhighlight lang="c#">
  @page "/user/{id?}"
  @page "/user/{id?}"                     // idパラメータはオプション (省略可能)
@page "/blog/{year:int?}/{month:int?}"  // 年月も省略可能
   
   
  @code {
  @code {
     [Parameter]
     [Parameter]
     public string Id { get; set; }
     public string Id { get; set; }
    [Parameter]
    public int? Year { get; set; }
    [Parameter]
    public int? Month { get; set; }
   
   
     protected override void OnInitialized()
     protected override void OnInitialized()
     {
     {
       if (string.IsNullOrEmpty(Id))
       // パラメータが省略された場合のデフォルト値設定
      Id    ??= "default";          // Idが未指定の場合は、"default"を設定
      Year  ??= DateTime.Now.Year;  // 年が未指定の場合は現在の年を設定
      Month ??= DateTime.Now.Month;  // 月が未指定の場合は現在の月を設定
    }
}
</syntaxhighlight>
<br>
==== クエリパラメータ ====
<syntaxhighlight lang="c#">
@page "/search"
@inject NavigationManager NavigationManager  // URLの操作に必要なサービスの注入
@code {
    // クエリパラメータを格納するディクショナリ
    private Dictionary<string, string> QueryParameters;
    protected override void OnInitialized()
    {
      // 現在のURLからクエリパラメータを解析
      var uri = new Uri(NavigationManager.Uri);
      QueryParameters = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query)
                        .ToDictionary(p => p.Key, p => p.Value.ToString());
    }
    // 検索条件を更新してページ遷移を行うメソッド
    private void UpdateSearch(string query, int page)
    {
      // クエリパラメータの設定
      var parameters = new Dictionary<string, string>
       {
       {
           Id = "default";
           { "q", query },              // 検索キーワード
       }
          { "page", page.ToString() }  // ページ番号
      };
      // クエリパラメータを含むURLを生成して遷移
      var url = QueryHelpers.AddQueryString("/search", parameters);
       NavigationManager.NavigateTo(url);
    }
}
</syntaxhighlight>
<br>
==== 2つのパラメータの組み合わせ ====
<syntaxhighlight lang="c#">
@page "/products/{categoryId?}"              // カテゴリIDはオプション
@inject NavigationManager NavigationManager
@code {
    [Parameter]
    public string CategoryId { get; set; }  // URLパラメータ
    // クエリパラメータから検索キーワードを取得
    private string SearchTerm => GetQueryParameter("search");
    // クエリパラメータからページ番号を取得 (デフォルト : 1)
    private int Page => int.Parse(GetQueryParameter("page") ?? "1");
    // クエリパラメータを取得するヘルパーメソッド
    private string GetQueryParameter(string key)
    {
      var uri = new Uri(NavigationManager.Uri);
      var parameters = QueryHelpers.ParseQuery(uri.Query);
      return parameters.TryGetValue(key, out var value) ? value.ToString() : null;
     }
     }
  }
  }
214行目: 377行目:


== レイアウトの適用 ==
== レイアウトの適用 ==
Blazorのルーティングでは、レイアウトの適用も重要である。<br>
Blazorのルーティングでは、レイアウト機能も重要である。<br>
レイアウト機能により、共通のUIパーツを効率的に管理でき、ページごとに異なるレイアウトを適用することができる。<br>
<br>
_Imports.razorファイルにおいて、デフォルトレイアウトを指定して、必要に応じて個別のコンポーネントで<code>@layout</code>指示子を使用して上書きすることができる。<br>
_Imports.razorファイルにおいて、デフォルトレイアウトを指定して、必要に応じて個別のコンポーネントで<code>@layout</code>指示子を使用して上書きすることができる。<br>
<br>
==== 基本的なレイアウトファイルの構造 ====
<syntaxhighlight lang="c#">
// MainLayout.razorファイル
@inherits LayoutComponentBase
<div class="page">
    <div class="sidebar">
      <NavMenu />
    </div>
    <main>
      <div class="content">
          @Body
      </div>
    </main>
</div>
</syntaxhighlight>
<br>
==== _Imports.razorでのデフォルトレイアウト指定 ====
<syntaxhighlight lang="c#">
@using System.Net.Http
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@layout MainLayout    // デフォルトレイアウトの指定
</syntaxhighlight>
<br>
==== 個別コンポーネントでのレイアウト上書き ====
<syntaxhighlight lang="c#">
// Pages/AdminPage.razorファイル
@layout AdminLayout  // このコンポーネントのみ異なるレイアウトを使用
@page "/admin"
<h1>管理画面</h1>
</syntaxhighlight>
<br>
==== 入れ子レイアウトの例 ====
<syntaxhighlight lang="c#">
// AdminLayout.razorファイル
@inherits LayoutComponentBase
<div class="admin-layout">
    <nav class="admin-nav">
      <AdminMenu />
    </nav>
    <div class="admin-content">
      @Body
    </div>
</div>
</syntaxhighlight>
<br><br>
<br><br>


221行目: 441行目:
404エラー等のルーティングエラーは、App.razorで設定したNotFoundテンプレートによって処理される。<br>
404エラー等のルーティングエラーは、App.razorで設定したNotFoundテンプレートによって処理される。<br>
これにより、ユーザフレンドリーなエラー画面を表示することが可能である。<br>
これにより、ユーザフレンドリーなエラー画面を表示することが可能である。<br>
<br>
例えば、NotFoundセクションをカスタマイズすることにより、より使用しやすいエラー画面を提供することができる。<br>
<br>
<syntaxhighlight lang="c#">
// App.razorファイル
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
      <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
      <PageTitle>Not found</PageTitle>
      <LayoutView Layout="@typeof(MainLayout)">
          <div class="alert alert-danger">
            <h3>ページが見つかりません</h3>
            <p>申し訳ありません。要求されたページは存在しません。</p>
            <a href="/" class="btn btn-primary">ホームに戻る</a>
          </div>
      </LayoutView>
    </NotFound>
</Router>
</syntaxhighlight>
<br>
==== カスタム404ページ ====
<syntaxhighlight lang="c#">
// Pages/CustomNotFound.razorファイル
@inject NavigationManager NavigationManager
<div class="error-container">
    <h2>404 - ページが見つかりません</h2>
    <p>リクエストされたURL: @NavigationManager.Uri</p>
    <div class="error-actions">
      <button @onclick="GoBack">前のページに戻る</button>
      <a href="/" class="btn">ホームページへ</a>
    </div>
</div>
@code {
    private void GoBack()
    {
      NavigationManager.NavigateTo(NavigationManager.Uri);
    }
}
</syntaxhighlight>
<br>
上記のファイルをApp.razorファイルで使用する。<br>
<syntaxhighlight lang="c#">
// App.razorファイル
<NotFound>
    <LayoutView Layout="@typeof(MainLayout)">
      <CustomNotFound />
    </LayoutView>
</NotFound>
</syntaxhighlight>
<br><br>
<br><br>


== 認証 / 認可 ==
== 認証 / 認可 ==
<code>[Authorize]</code>属性を使用することにより、特定のルートへのアクセスを制限することができる。<br>
<code>[Authorize]</code>属性を使用することにより、特定のルートへのアクセスを制限することができる。<br>
<br>
==== 基本的な認証要求 ====
<syntaxhighlight lang="c#">
@page "/secured-page"
[Authorize]
public class SecuredPageBase : ComponentBase
{
    // このページは認証済みユーザーのみアクセス可能
}
</syntaxhighlight>
<br>
==== 特定のロールに基づく認証 ====
<syntaxhighlight lang="c#">
@page "/admin-page"
[Authorize(Roles = "Admin")]
public class AdminPageBase : ComponentBase
{
    // 管理者ロールを持つユーザーのみアクセス可能
}
</syntaxhighlight>
<br>
==== 複数のロールの指定 ====
<syntaxhighlight lang="c#">
[Authorize(Roles = "Admin,Manager")]
// ポリシーベースの認証
[Authorize(Policy = "RequireAdminRole")]
// カスタム認証要件
[Authorize(Policy = "MinimumAgeRequirement")]
</syntaxhighlight>
<br>
==== Program.csでの認証サービスの設定 ====
<syntaxhighlight lang="c#">
builder.Services.AddAuthentication().AddCookie();
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdminRole", policy => policy.RequireRole("Admin"));
    options.AddPolicy("MinimumAgeRequirement", policy => policy.Requirements.Add(new MinimumAgeRequirement(18)));
});
</syntaxhighlight>
<br>
==== 認証状態の確認と処理 ====
<syntaxhighlight lang="c#">
@inject AuthenticationStateProvider AuthenticationStateProvider
@code {
    private async Task<bool> IsUserAuthenticated()
    {
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        return authState.User.Identity.IsAuthenticated;
    }
}
</syntaxhighlight>
<br><br>
<br><br>