情報応用演習Ⅰ(2024)

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

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

9a-10. ユーザーの追加

次にAccountsコントローラーにユーザーの追加のためのアクションとビューを追加しよう.

ユーザーの新規登録のためにリスト9a-5-1BlogUserクラスをそのままモデルクラスとして使用する手もありうるが, このクラスには新規登録時は入力しない情報も多数含まれている.また,例えば「パスワード」の入力に対して「パスワードの確認」を入力するといった 新規登録時にのみ必要となるデータもあるため,ログイン時のアクションでLoginUserInfoクラスを特別に作成したのと同様に 「ユーザーの新規登録時の情報」を入力するためのモデルクラスを作成することにする.

プロジェクト内の Models フォルダを右クリックし,「追加」→「クラス」をクリックする.作成するクラス名を訊かれるので NewUserInfo(.csは省略可能)と入力して「追加」ボタンをクリックする.すると空のクラス定義が作られるので_の定義を書き込もう.

ユーザー追加画面のためのモデルクラス
 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
using System.ComponentModel.DataAnnotations; // 追記

namespace T9a.Models
{
    // 新規ユーザー登録のためのモデルクラス
    public class NewUserInfo
    {
        [Display(Name = "ユーザー名")]
        public string Username { get; set; } = "";

        [Display(Name = "ニックネーム")]
        public string? Nickname { get; set; }

        [Display(Name = "メールアドレス")]
        [EmailAddress]
        public string Email { get; set; } = "";

        [Display(Name = "パスワード")]
        [DataType(DataType.Password)]
        public string Password { get; set; } = "";

        [Display(Name = "パスワード(確認)")]
        [DataType(DataType.Password)]
        public string PasswordConfirmation { get; set; } = "";
    }
}

ユーザーの追加はAccountsコントローラーのAddUserアクションで行うので,そのためのアクションメソッドを追加しよう. Controllers/AccountsController.cs のAccountsControllerクラスに__に示す内容を追記しよう.

Accountsコントローラーの追記内容(AddUserアクション(GET用))
1
2
3
4
5
6
// AddUserアクション(GET用)
[Authorize(Roles = IdentityDataSeeder.AdminRoleName)]
public IActionResult AddUser() 
{
    return View(new NewUserInfo());
}
Accountsコントローラーの追記内容(AddUserアクション(POST用))
 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
39
// AddUserアクション(POST用)
[Authorize(Roles = IdentityDataSeeder.AdminRoleName)]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddUser(NewUserInfo newUserInfo) 
{
    if (ModelState.IsValid) 
    {
        // 新規ユーザーのための BlogUser オブジェクトを作成する.
        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(Index)); // 本来はユーザー詳細画面(UserDetails)に遷移させる前提であるが
                                                    // まだ作成していないのでユーザー一覧画面(Index)に遷移させる.
        }
        catch (CompositeMessagesException ex) 
        {
            ViewBag.ErrorMessages = ex.ErrorMessages;
        }
    }//if

    return View(newUserInfo);
}

_の引数なしのAddUser()メソッドはGET用であり,単にビューを表示するだけである. _で定義したNewUserInfoクラスを引数に持つAddUser()メソッドがPOST用の, 実際にユーザー情報の登録を行うアクションである.どちらのアクションメソッドにも管理者ユーザーしか アクセスができないようにするため,Rolesプロパティに管理者ユーザーグループを指定した[Authorize]属性を付与している.

_のPOST用のAddUser()アクションメソッドでは,UserManager<TUser>クラスの .CreateAsync()メソッドを 使用してユーザー情報の作成を行っている.このメソッドには「ユーザー」のオブジェクトとパスワードを 指定して呼び出すことで実際にユーザーを作成することができる(22行目).26行目では,同じくUserManager<TUser>クラスの .AddToRoleAsync()メソッドを 使用して,作成したユーザーをロール(グループ)に所属させている. これらのメソッドは,そのユーザーアカウントに関する操作の成否をIdentityResultクラスの オブジェクトとして返却する.先ほどのLoginアクションの時と同様に,操作に失敗した場合はそれをIdentityOperationFailedExceptionクラスに乗せて投じ, catch ブロックでそれを捕えてエラーメッセージのリストとしてViewBag.ErrorMessagesに格納している.

ユーザーの作成はさまざまな理由で失敗することがあるので注意が必要である.たとえば入力されたユーザー名やメールアドレスが既に使われていた場合や, 指定されたパスワードが平易すぎる場合である. 特にパスワードに関してはデフォルトでは以下のすべての条件を満たさなければならない

  • 英字の大文字を含む.
  • 英字の小文字を含む.
  • 数字を含む.
  • 記号を含む.
  • 6文字以上である.
  • 1種類以上の文字が使われなければならない.

これらの制限は変更が可能である.詳しくは Configure ASP.NET Core Identity - Microsoft Docsを 参照せよ.

ユーザー作成後は本来はユーザー詳細画面(UserDetails)に遷移する(図9a-7-1)設計であるが,現時点ではまだ作成していないので, Indexアクションにリダイレクトしている(29行目).

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

Views/Accounts/AddUser.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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@model NewUserInfo

@{
    ViewData["Title"] = "新規ユーザー登録";

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

<form asp-action="AddUser">
    <table>
        <tr><th>項目</th><th></th></tr>

        @* ユーザー名(Username)のための入力欄 *@
        <tr>
            <td><label asp-for="@Model.Username"></label></td>
            <td><input asp-for="@Model.Username" /></td>
        </tr>

        @* ニックネーム(Nickname)のための入力欄 *@
        <tr>
            <td><label asp-for="@Model.Nickname"></label></td>
            <td><input asp-for="@Model.Nickname" /></td>
        </tr>

        @* メールアドレス(Email)のための入力欄 *@
        <tr>
            <td><label asp-for="@Model.Email"></label></td>
            <td><input asp-for="@Model.Email" /></td>
        </tr>

        @* パスワード(Password)のための入力欄 *@
        <tr>
            <td><label asp-for="@Model.Password"></label></td>
            <td> <input asp-for="@Model.Password" /></td>
        </tr>

        @* パスワードの入力確認(PasswordConfirmation)のための入力欄 *@
        <tr>
            <td><label asp-for="@Model.PasswordConfirmation"></label></td>
            <td><input asp-for="@Model.PasswordConfirmation" /></td>
        </tr>
    </table>
    <input type="submit" value="登録" />
    <div asp-validation-summary="All"></div>
</form>

@* ログインにまつわるエラーメッセージを ul 要素で表示する *@
@if (ViewBag.ErrorMessages != null)
{
    var msgs = (IEnumerable<string>)ViewBag.ErrorMessages;
    <ul>
        @foreach (var msg in msgs)
        {
            <li>@msg</li>
        }
    </ul>
}

@section Scripts
{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

これに関しても特筆すべきことはないだろう.@modelディレクティブで_で作成した NewUserInfoクラスを指定して,それを入力するためのフォームを作成しているのみである.

ここまで書けたら実行してみよう.プロジェクトを起動してログインのリンクをクリックして,adminとして ログインしよう.プロジェクト起動時にログイン済みの場合は画面上部の「ユーザー一覧」のリンクをクリックすると ユーザー一覧の画面(Index)が表示される.「新規ユーザー登録」のリンクをクリックするとユーザーの新規登録画面(AddUser)に遷移する(_). ここで適当に入力してユーザーを作成してみよう(_).この際,先ほど言及したパスワードに関する制約に注意しよう. 成功すれば,_に示すようにユーザー一覧画面(Index)に遷移して,一覧に追加したユーザー名が表示されているはずである.

実行結果

ただし気付いている読者もいるかもしれないが,実は現状ではまだ正しく機能していない部分がある. 次節ではそのことについて説明する.デバッグ実行は停止させないまま次節に進もう

Last updated on 2024-06-19
Published on 2024-06-19

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