情報応用演習Ⅰ(2024)

【T10b】簡易ブログソフトウェアの作成 Part.Ⅳ ~ ファイルのアップロード/ダウンロード(6/7)

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

10b-6. DeleteAttachmentアクションとビューの作成

次に添付ファイルの削除のためのアクションとビューを作成しよう. Articlesコントローラーに__に示す内容を追記しよう.

Articlesコントローラーの追記内容(DeleteAttachmentアクション(GET用))
 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
// DeleteAttachmentアクション(GET用)
[Authorize]
public async Task<IActionResult> DeleteAttachment(int? id)
{
    if (id == null)                                                 // ルーティングパラメーターが
    {                                                               // 指定されていなければ
        return NotFound();                                          // 404 Not Found を返す.
    }                                                               //

    var attachment = await _context.Attachments                     // 指定されたIDから,
        .Include(a => a.Article)                                    // 削除対象の添付ファイルを
        .FirstOrDefaultAsync(m => m.AttachmentId == id);            // 検索する.

    if (attachment == null)                                         // 指定されたファイルが
    {                                                               // 見つからなければ
        return NotFound();                                          // 404 Not Found を返す.
    }                                                               //

    BlogUser? currentUser = await _userManager.GetUserAsync(User);  // ログイン中のユーザー情報を取得して
    if (!await IsModifiableAsync(currentUser, attachment.Article!)) // 編集可能かどうかを調べる.
    {                                                               //
        return Forbid();                                            // 編集不可の場合は 403 Forbidden を返す.
    }                                                               //

    return View(attachment);
}
Articlesコントローラーの追記内容(DeleteAttachmentアクション(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
// DeleteAttachmentアクション(POST用)
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteAttachment(int? id, [Bind("AttachmentId")] Attachment attachment) 
{
    if (id == null)                                                 // ルーティングパラメーターが 
    {                                                               // 指定されていなければ
        return NotFound();                                          // 404 Not Found を返す.
    }                                                               //

    if (id != attachment.AttachmentId)                              // ルーティングパラメーターとPOSTされている
    {                                                               // 「添付ファイル」のIDが食い違っていたら
        return NotFound();                                          // 404 Not Found を返す.
    }                                                               //

    var confirmed = await _context.Attachments                      // 念のためルーティングパラメーターで
        .Include(a => a.Article)                                    // 指定されている「添付ファイル」が
        .FirstOrDefaultAsync(m => m.AttachmentId == id);            // 存在することを確認する.

    if (confirmed == null)                                          // ↑が存在しなければ
    {                                                               // 
        return NotFound();                                          // 404 Not Found を返す.
    }                                                               //

    BlogUser? currentUser = await _userManager.GetUserAsync(User);  // ログイン中のユーザー情報を取得して
    if (!await IsModifiableAsync(currentUser, confirmed.Article!))  // 編集可能かどうかを調べる.
    {                                                               //
        return Forbid();                                            // 編集不可の場合は 403 Forbidden を返す.
    }                                                               //

    var filepath = GetAttachmentFilePath(confirmed);                // 添付ファイルの保存先のパスを取得して
    System.IO.File.Delete(filepath);                                // 削除する.

    _context.Attachments.Remove(confirmed);                         // データベース側の対応するエントリーも
    await _context.SaveChangesAsync();                              // 削除する.

    return RedirectToAction(nameof(Edit), new { id = confirmed.ArticleId }); // Editアクションに遷移する.
}

どちらのメソッドも記事そのものの削除のロジックとおおむね同じである.異なるのはメソッドの引数(≒ルーティングパラメーター) idを添付ファイルIDとして扱っている点である. _のPOST用のDeleteAttachmentアクションの処理では,32~33行目で削除対象の 添付ファイルをファイルシステムから物理的に削除し,35~36行目ではそのファイルに対応する添付ファイルのメタ情報 (Attachmentクラスのインスタンス)をデータコンテキストクラスの.Remove()メソッドを用いてデータベースから削除している.

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

Views/Articles/DeleteAttachment.cshtmlの内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@model Attachment

@{
    ViewData["Title"] = "添付ファイル削除";

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

<p>
  <a asp-action="Details" asp-route-id="@Model.ArticleId">記事:@Model.Article?.Title</a>から
  <a asp-action="GetAttachement" asp-route-id="@Model.AttachmentId">添付ファイル:@Model.Filename</a>を削除します.
</p>

<form asp-action="DeleteAttachment">
  @* 添付ファイルのIDのみを隠しパラメーターとして埋め込んでおく. *@
  <input type="hidden" asp-for="@Model.AttachmentId" />

  <input type="submit" value="削除" />
</form>

<a asp-action="Edit" asp-route-id="@Model.ArticleId">編集に戻る</a>

これに関しては特筆すべきことはないだろう.

ここまで書けたら実行してみよう.プロジェクトを起動して「ログイン」のリンクをクリックし適当な通常ユーザー (admin以外のユーザー)としてログインしよう.その記事の一覧の中から,先ほどファイルを添付した記事を探して「編集」の リンクをクリックして記事の編集画面(Edit)を表示せる(__). そして添付ファイルの一覧の「削除」のリンクをクリックして,添付ファイルの削除画面(DeleteAttachment)を表示させよう(__). 「削除」ボタンをクリックすると記事の編集画面に遷移し,添付ファイルの一覧から添付ファイルが削除されたことが分かるはずである(__).

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

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