情報応用演習Ⅰ(2024)

【T9a】簡易ブログソフトウェアの作成 Part.Ⅰ ~ 認証機能の組み込み(12/14)

プロジェクトタイプASP.NET Core Web アプリ(Model-View-Controller)
プロジェクト名T9a
ソリューション名PIT9
ターゲットフレームワーク.NET 8.0(長期的なサポート)
最上位レベルのステートメントを使用しない使用する(チェックオフ)
注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業については準備作業を参照せよ.

9a-12. ユーザーの詳細情報の表示

つぎにユーザーの詳細情報を表示するUserDetailsアクションを実装しよう. Controllers/AccountsController.cs のAccountsControllerクラスに_に示す内容を追記しよう.

Accountsコントローラーの追記内容(UserDetails)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// UserDetailsアクション(GETのみ)
[Authorize(Roles = IdentityDataSeeder.AllRoles)]
public async Task<IActionResult> UserDetails(string? id)           // 引数名はルーティングパラメーター名と一致している必要がある.
{
    if (string.IsNullOrEmpty(id))                                  // ルーティングパラメーターが指定されていなければ
        return NotFound();                                         // 404 Not Found を返す.

    var userName = id;                                             // ルーティングパラメーターには実際には
                                                                   // ユーザー名が格納されている(Idではない)
    
    var targetUser = await _userManager.FindByNameAsync(userName); // 指定されたユーザー名から,表示対象のユーザー情報を検索する.
    
    if (targetUser == null)                                        // 見つからなければ
        return NotFound();                                         // 404 Not Found を返す.

    var currentUser = await _userManager.GetUserAsync(User);       // 現在ログイン中のユーザー情報を取得して
    bool isAdminUser = await IsAdminUserAsync(currentUser);        // それが管理者ユーザーかどうかを調べる.

    if (!(isAdminUser || targetUser.Id == currentUser!.Id))        // 管理者ユーザーであるか,もしくは
        return Forbid();                                           // 表示対象がログインしているユーザー自身である場合以外は,
                                                                   // アクセスを拒否する.

    return View(targetUser);
}

リスト9a-9-2で言及した通り,ルーティングパラメーターidにはユーザーIDではなくユーザー名(UserName)を使ったリンクを作成してるため, UserDetails()メソッドの引数にはユーザー名が渡されている点に注意しよう.ここではUserManager<TUser>クラスの .FindByNameAsync()を 使ってユーザー名を格納した文字列から,対応するユーザーのBlogUserオブジェクトを検索している.

ここで16~21行目ではログイン中のユーザー情報を取得して,通常ユーザーが自分自身以外を表示しようとしているときはForbid()メソッドを呼び出して アクセスを拒否している.Forbid()メソッドメソッドは,要求されたリソースへのアクセスが禁じられていることを示すHTTPのレスポンスコード 403 Forbiddenをクライアントに返却するためのメソッドである.

次にこのUserDetailsアクションのためのビューを作成しよう. Views/Accounts/UserDetails.cshtml を_のように記述する.

Views/Accounts/UserDetails.cshtmlの内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@model BlogUser

@{
    ViewData["Title"] = "ユーザー詳細";

    if (Model is null) throw new ArgumentNullException(nameof(Model)); // ad-hoc! 非null保証のための回避策
}

<table>
    <tr><th>項目</th><th></th></tr>

    @* ユーザー名(Username)の表示 *@
    <tr>
        <td>@Html.DisplayNameFor(u => u.UserName)</td>
        <td>@Html.DisplayFor(u => u.UserName)</td>
    </tr>

    @* ニックネーム(Nickname)の表示 *@
    <tr>
        <td>@Html.DisplayNameFor(u => u.Nickname)</td>
        <td>@Html.DisplayFor(u => u.Nickname)</td>
    </tr>

    @* メールアドレス(Email)の表示 *@
    <tr>
        <td>@Html.DisplayNameFor(u => u.Email)</td>
        <td>@Html.DisplayFor(u => u.Email)</td>
    </tr>

    @* 登録日時(Registered)の表示 *@
    <tr>
        <td>@Html.DisplayNameFor(u => u.Registered)</td>
        <td>@Html.DisplayFor(u => u.Registered)</td>
    </tr>
</table>

<a asp-action="EditUser" asp-route-id="@Model.UserName">編集</a>
| <a asp-action="DeleteUser" asp-route-id="@Model.UserName">退会(削除)</a>

このビューでも特に変わったことはしていない.

これでユーザー詳細画面ができたので,LoginRouteアクションとAddUserアクションにおける遷移先を修正しておこう. Controllers/AccountController.cs のLoginRoute()メソッドおよびAddUser()メソッド(POST用)を それぞれ__に示すように変更する.

Accountsコントローラーの変更内容(LoginRoute)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// LoginRouteアクション(遷移先の振り分け用),認証済みなら誰でもアクセス可
[Authorize(Roles = IdentityDataSeeder.AllRoles)]
public async Task<IActionResult> LoginRoute() 
{
    var user = await _userManager.GetUserAsync(User);

    if (await IsAdminUserAsync(user))
        return RedirectToAction(nameof(Index));
    else
        return RedirectToAction(nameof(UserDetails), new { id = user!.UserName }); // ユーザー詳細画面へ遷移する.
}
Accountsコントローラーの変更内容(AddUser)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// AddUserアクション(POST用)
[Authorize(Roles = IdentityDataSeeder.AdminRoleName)]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddUser(NewUserInfo newUserInfo) 
{
    if (ModelState.IsValid) 
    {
        var user = new BlogUser() 
        {
            UserName = newUserInfo.Username,
            Nickname = newUserInfo.Nickname,
            Email = newUserInfo.Email,
            Registered = DateTime.Now,
            SecurityStamp = Guid.NewGuid().ToString("D")
        };

        try
        {
            var res1 = await _userManager.CreateAsync(user, newUserInfo.Password);
            if (!res1.Succeeded) throw new IdentityOperationFailedException(res1);

            var res2 = await _userManager.AddToRoleAsync(user, IdentityDataSeeder.NormalRoleName);
            if (!res2.Succeeded) throw new IdentityOperationFailedException(res2);

            return RedirectToAction(nameof(UserDetails), new { id = user.UserName }); // ユーザー詳細画面へ遷移する.
        }
        catch (CompositeMessagesException ex) 
        {
            ViewBag.ErrorMessages = ex.ErrorMessages;
        }
    }//if

    return View(newUserInfo);
}

ここまで書けたら実行してみよう.プロジェクトを起動して「ログイン」のリンクをクリックしadminとして ログインしよう.起動時にログイン済みの場合は画面上部の「ユーザー一覧」のリンクをクリックして ユーザー一覧画面(Index)を表示させ,適当なユーザーのリンクをクリックしてみよう. クリックしたユーザーのユーザー名,ニックネーム,メールアドレスといった詳細情報が表示されるはずである(_). また,ユーザー一覧画面に戻り「新規ユーザー登録」のリンクをクリックして適当にユーザーを作成しよう.ユーザー一覧画面ではなく ユーザー詳細画面に遷移するはずである(_).

実行結果
Last updated on 2024-06-19
Published on 2024-06-19

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.