0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【C#入門】初学者がASP.NETでWebアプリを作る:第7回

Posted at

今回やること

・アカウント登録後、ログイン状態にせずログインさせる
・アカウントと給与データを紐づける

アカウント登録後、ログイン状態にせずログインさせる

必要なことは2つと考えました。
①アカウント登録後、ログインしていない状態にする
②アカウント登録後、ログイン画面に遷移する

①アカウント登録後、ログインしていない状態にする

ログイン処理(Register.cshtml.csのOnPostAsyncメソッド)で下記の記述があるのでログインしてしまっています。
なのでこれを削除します。

await _signInManager.SignInAsync(user, isPersistent: false);

また、ホームページにリダイレクトしているところを、ログインページにリダイレクトするようにします。

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/Identity/Account/Login");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = new IdentityUser { UserName = Input.UserName, Email = "" };
                user.PasswordHash = new PasswordHasher<IdentityUser>().HashPassword(user, Input.Password);
                var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("アカウント登録が完了しました。");

                    //var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    //code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    //var callbackUrl = Url.Page(
                    //    "/Account/ConfirmEmail",
                    //    pageHandler: null,
                    //    values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
                    //    protocol: Request.Scheme);

                    //await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                    //    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        //return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        //await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

アカウント登録します。
image.png

ログイン画面が表示されました。
右上を見るとログイン状態にもなっていませんので実装完了です。
image.png

アカウントと給与データを紐づける

やることは2つ。
①給与データ登録時に"DUMMY"としていた登録ユーザID・更新ユーザIDを、ログイン中のユーザ名で設定する
②一覧ページの表示時に、ログイン中のユーザの情報のみを表示するようにする
※ログインしていない状態で一覧ページには行けないようにしますがそれは後で。
 今回はログイン中のユーザー名が取得できなければ何も表示しない(=引っかかるデータがない)にします。

①給与データ登録時に登録ユーザID・更新ユーザIDをログイン中のユーザ名で設定する

SalariesControllerのCreateメソッドを書き換えます。

SalariesController.cs
public async Task<IActionResult> Create([Bind("Id,Beneficiary,PaymentType,PaymentDate,PaymentAmount,TravelExpence,HealthInsurancePremium,WelfarePension,EmploymentInsurancePremium,IncomeTax,ResidentTax,TotalPaymentAmount,OvertimeAllowance,MidnightAllowance,HolidayAllowance,Remarks,RegisterDate,RegisterUser,UpdateDate,UpdateUser")] Salary salary)
        {
            salary.RegisterDate = DateTime.Now;
            salary.RegisterUser = User.Identity.Name ?? "DUMMY";
            salary.UpdateDate = DateTime.Now;
            salary.UpdateUser = User.Identity.Name ?? "DUMMY";
            if (ModelState.IsValid)
            {
                _context.Add(salary);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(salary);
        }

登録してみます。桁間違えて総支給50兆円になっちゃったけど5000兆円ほしい。
image.png

まだいじっていない編集画面で確認すると、ログイン中のユーザ名が登録されていることがわかります。
image.png

②一覧ページの表示時にログイン中のユーザの情報のみを表示する

同じくSalaryControllerのIndexメソッドを修正します。
登録ユーザIDがログイン中のユーザと同じものだけを返します。

SalaryController.cs
        public async Task<IActionResult> Index()
        {
            //return View(await _context.Salary.ToListAsync());
            return View(await _context.Salary.Where(x => x.RegisterUser == (User.Identity.Name ?? "DUMMY")).ToListAsync());
        }

ログインしていない状態だと引っかかるデータがないため、なにも表示されません。
image.png

ログイン状態だとログイン中のユーザが登録したデータが表示されます。
image.png

違うユーザに編集すると…
image.png

表示されません。
image.png

簡単な修正をいくつか

数値の表示を整数にする

モデルクラスの各数値項目に、アノテーションを付けます。

        // 支給額
        [Display(Name = "支給額")]
        [Required(ErrorMessage = "支給額は必須入力です")]
        // ↓これをつけると3桁カンマあり小数0埋めなしになる
        [DisplayFormat(DataFormatString = "{0:#,##0.#}", ApplyFormatInEditMode = true)]
        public decimal PaymentAmount { get; set; }

アカウント管理画面の修正

今のところパスワード変更くらいしか使わないので、それ以外消します。
image.png

_ManageNav.cs.htmlからPassword以外消します。

_ManageNav.cs.html
@inject SignInManager<IdentityUser> SignInManager
@{
    var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
}
<ul class="nav nav-pills flex-column">
    <!--<li class="nav-item"><a class="nav-link @ManageNavPages.IndexNavClass(ViewContext)" id="profile" asp-page="./Index">Profile</a></li>
    <li class="nav-item"><a class="nav-link @ManageNavPages.EmailNavClass(ViewContext)" id="email" asp-page="./Email">Email</a></li>-->
    <li class="nav-item"><a class="nav-link @ManageNavPages.ChangePasswordNavClass(ViewContext)" id="change-password" asp-page="./ChangePassword">パスワード</a></li>
    <!--@if (hasExternalLogins)
        {
            <li id="external-logins" class="nav-item"><a id="external-login" class="nav-link @ManageNavPages.ExternalLoginsNavClass(ViewContext)" asp-page="./ExternalLogins">External logins</a></li>
        }
        <li class="nav-item"><a class="nav-link @ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)" id="two-factor" asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
        <li class="nav-item"><a class="nav-link @ManageNavPages.PersonalDataNavClass(ViewContext)" id="personal-data" asp-page="./PersonalData">Personal data</a></li>-->
    </ul>

_LoginPartial.cs.htmlで右上のボタンを押したときの遷移先をChangePasswordにします。

_LoginPartial.cs.html
@using Microsoft.AspNetCore.Identity

@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a id="manage" class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/ChangePassword" title="アカウント管理">@UserManager.GetUserName(User) としてログイン中</a>
    </li>
    <li class="nav-item">
        <form id="logoutForm" class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })">
            <button id="logout" type="submit" class="nav-link btn btn-link text-dark">ログアウト</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" id="register" asp-area="Identity" asp-page="/Account/Register">アカウント登録</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" id="login" asp-area="Identity" asp-page="/Account/Login">ログイン</a>
    </li>
}
</ul>

ヘッダー部分(Manage your accountとかの部分)も変更します。

_Layout.cshtml
@{
    if (ViewData.TryGetValue("ParentLayout", out var parentLayout))
    {
        Layout = (string)parentLayout;
    }
    else
    {
        Layout = "/Areas/Identity/Pages/_Layout.cshtml";
    }
}

<h2>アカウント管理</h2>

<div>
    <h4>アカウント情報を変更します。</h4>
    <hr />
    <div class="row">
        <div class="col-md-3">
            <partial name="_ManageNav" />
        </div>
        <div class="col-md-9">
            @RenderBody()
        </div>
    </div>
</div>

@section Scripts {
    @RenderSection("Scripts", required: false)
}

ChangePassword画面も日本語にしておきます。

ChangePassword.cshtml
@page
@model ChangePasswordModel
@{
    ViewData["Title"] = "パスワード変更";
    ViewData["ActivePage"] = ManageNavPages.ChangePassword;
}

<h4>@ViewData["Title"]</h4>
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="change-password-form" method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Input.OldPassword"></label>
                <input asp-for="Input.OldPassword" class="form-control" />
                <span asp-validation-for="Input.OldPassword" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.NewPassword"></label>
                <input asp-for="Input.NewPassword" class="form-control" />
                <span asp-validation-for="Input.NewPassword" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">パスワード変更</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

このようになります。
image.png

ホームをSalaties/Indexにする

今の時点では、「localhost:ポート番号/」にアクセスすると、ようこそ的な画面になりますが、
Indexページを表示するようにします。
デフォルトをSalaries/Indexにするようにしました。(Homeはいらない。)
ヘッダーの「ホーム」やシステム名のところは直接Home/Indexを呼ぶようになっているので、それも直します。

Startup.cs
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Salaries}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });
_Layout.cshtml
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - 給与管理システム(SalaryManagementSystem)</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Salaries" asp-action="Index">給与管理システム(SalaryManagementSystem)</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Salaries" asp-action="Index">ホーム</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                    <partial name="_LoginPartial" />
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - 給与管理システム(SalaryManagementSystem) - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

なりました。
image.png

ログアウト時にログイン画面に遷移

ログアウトのPOSTリクエストに対する処理を書き換えて、ログイン画面に遷移します。
なんだか無理やりすぎな気がする…

Logout.cshtml.cs
        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(Url.Content("~/Identity/Account/Login"));
            }
            else
            {
                return RedirectToPage();
            }
        }

未ログイン状態でのアクセス制御

基本、未ログイン状態でSalary操作系のページにはアクセスしてほしくありません。
まずはSalary操作系のページ全てに対して匿名ユーザのアクセスができないようにします。
SalariesControllerクラスにAuthorize属性を付けます。

SalariesController.cs
namespace SalaryManagementSystem.Controllers
{
    [Authorize]
    public class SalariesController : Controller
    {

ホーム(Indexにリダイレクトされる)を開くと、上記のアクセス制御によりログイン画面に遷移します。
image.png

次回予告

次回はデータを可視化する方法を考えます。

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?