情報応用演習Ⅰ(2024)

【T9b】簡易ブログソフトウェアの作成 Part.Ⅱ ~ 記事関連機能の実装(10/11)

プロジェクトタイプ(注意: 本文参照)
プロジェクト名T9b
ソリューション名PIT9
注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業については準備作業を参照せよ.

9b-10. 記事の削除のためのアクションの修正

次にArticlesコントローラーの仕上げとして,記事の削除のためのアクションであるDeleteアクションとそのビューを見直そう. デフォルトでは誰であってもすべての記事の削除が可能になっているが,その記事の作成者であるか管理者ユーザーしか 削除操作できないようにしなくてはならない.そのためGET用およびPOST用のDeleteアクションを __に示すように修正しよう.

なお POST用のアクションメソッドはDeleteConfirmedという名前になっているので注意しよう . ASP.NET Core ではデフォルトでは/○○○/▲▲▲というURLに対しては, ○○○Controllerクラスの▲▲▲()メソッドが呼び出されて,そのリクエストを処理するようになっているが, そのクラスやメソッドが対応するコントローラー名やアクション名は,実際のクラス名やメソッド名とは無関係に任意のものに変更することも可能である. このDeleteConfirmed()メソッドには[ActionName("Delete")]という属性が指定されており (_3行目),これによってこのメソッドが処理するアクション名がDeleteアクションに変更されている. この自動生成されたコントローラーの場合,GET用とPOST用のアクションメソッドの引数型が同じとなってしまい オーバーロードとして定義することができないため,回避策としてこのような方法が用いられている. このようなルーティングのカスタマイズ方法に関して,詳しくは Routing to controller actions in ASP.NET Core - Microsoft Docsを 参照するとよいだろう.

Articlesコントローラーの修正内容(Deleteアクション(GET用))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// GET: Articles/Delete/5
[Authorize]
public async Task<IActionResult> Delete(int? id)
{
    if (id == null )
    {
        return NotFound();
    }

    var article = await _context.Articles
        .Include(a => a.BlogUser)
        .FirstOrDefaultAsync(m => m.ArticleId == id);
    if (article == null)
    {
        return NotFound();
    }

    // ログイン中のユーザー情報を取得して削除可能かどうかを調べる.
    BlogUser? currentUser = await _userManager.GetUserAsync(User);
    if (!await IsModifiableAsync(currentUser, article)) return Forbid();

    return View(article);
}
Articlesコントローラーの修正内容(Deleteアクション(POST用))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// POST: Articles/Delete/5
[Authorize]
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
    var article = await _context.Articles.FindAsync(id);
    if (article == null)   // 要求されたIDの記事が
        return NotFound(); // 存在しなければ 404 Not Found

    // ログイン中のユーザー情報を取得して削除可能かどうかを調べる.
    BlogUser? currentUser = await _userManager.GetUserAsync(User);
    if (!await IsModifiableAsync(currentUser, article)) return Forbid();

    _context.Articles.Remove(article); // 指定された記事を削除する.
    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));
}

このDeleteアクションのためのビューはあえて自動生成のままのビューを用いることにする. このようにした場合,プロジェクトのデフォルトのCSSスタイルが適用されるため,手作業で作成したものより見栄えがするかもしれない. デフォルトのCSSに含まれているクラス名などは,自動生成されたビューや wwwroot/css/site.css を見て自分で調べるとよいだろう. 一部の文言が英語のままであったり,レイアウトページの変更に伴って不要な文言もあるため少しだけ修正しておこう. Views/Articles/Delete.cshtml を_に示すように変更しよう.

Views/Articles/Delete.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
@model T9b.Models.Article

@{
    ViewData["Title"] = "記事削除";

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

<h1>Delete</h1> @* ←削除 *@

<h3>記事を削除しますか?</h3>
<div>
    <h4>Article</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Body)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Body)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Created)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Created)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Modified)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Modified)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.PublicationState)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.PublicationState)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.BlogUser)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.BlogUser.Id)
        </dd>
    </dl>
    
    <form asp-action="Delete">
        <input type="hidden" asp-for="ArticleId" />
        <input type="submit" value="削除" class="btn btn-danger" /> |
        <a asp-action="Details" asp-route-id="@Model.ArticleId">詳細に戻る</a>
    </form>
</div>

ここまで書けたら実行してみよう.プロジェクトを起動して「ログイン」のリンクをクリックし適当な通常ユーザー (admin以外のユーザー)としてログインして「全記事一覧」のリンクをたどる. 既にログイン済みであれば記事一覧画面が表示されているはずである. ここで 自分で作成した 記事の「削除」のリンクをクリックしてみよう.すると記事の削除画面(Delete)が表示される(__). ここで「削除」ボタンをクリックしてみよう.記事一覧画面に遷移して選択された記事が削除されたことが分かるはずである(__).

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

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