Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8895

ASP.NET Core 3.0 Razor Pages 事始め(5)

$
0
0

ASP.NET Core 3.0 Razor Pages 事始め(4)の続きです。

なかなか先に進まないですが、ただ動かすだけではなく、コードを理解することが大切だと思うので、今回も、スキャフォールディングで作成されたソースコードを見ていきます。

Create.cshtml.cs

このファイルには、以下に示すように CreateModelクラスが定義されています。

usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Threading.Tasks;usingMicrosoft.AspNetCore.Mvc;usingMicrosoft.AspNetCore.Mvc.RazorPages;usingMicrosoft.AspNetCore.Mvc.Rendering;usingRazorPagesMovie.Models;namespaceRazorPagesMovie.Pages_Movies{publicclassCreateModel:PageModel{privatereadonlyRazorPagesMovie.Models.RazorPagesMovieContext_context;publicCreateModel(RazorPagesMovie.Models.RazorPagesMovieContextcontext){_context=context;}publicIActionResultOnGet(){returnPage();}[BindProperty]publicMovieMovie{get;set;}// To protect from overposting attacks, please enable the specific properties you want to bind to, for// more details see https://aka.ms/RazorPagesCRUD.publicasyncTask<IActionResult>OnPostAsync(){if(!ModelState.IsValid){returnPage();}_context.Movies.Add(Movie);await_context.SaveChangesAsync();returnRedirectToPage("./Index");}}}

OnGetメソッド

Index.cshtmlと違い、同期メソッドとして定義されています。

publicIActionResultOnGet(){returnPage();}

これは、中で非同期メソッド呼ぶ必要がないからですね。

試しに、

publicvoidOnGet(){}

と書き換えて実行してみました。想像通り、正しく動作しました。

OnPostAsync メソッド

CreateModelクラスには、もう一つのメソッドが定義されています。

publicasyncTask<IActionResult>OnPostAsync(){if(!ModelState.IsValid){returnPage();}_context.Movies.Add(Movie);await_context.SaveChangesAsync();returnRedirectToPage("./Index");}

Http Postに対応するメソッドです。

最初のif文は、旧来の ASP.NET MVCと同じですね。

if(!ModelState.IsValid){returnPage();}

これで、モデルの検証結果を判定し、エラーだったら、再度同じページを表示させています。

エラーがなかった場合は、

_context.Movies.Add(Movie);await_context.SaveChangesAsync();

で入力されたMovieデータを追加し、データベースを更新しています。

BindProperty属性

上記Addメソッドの引数のMovieは、自分自身(CreateModelクラス内)に定義されたプロパティです。

[BindProperty]publicMovieMovie{get;set;}

Webページで入力されPOSTされたデータは、このプロパティにバインドされます。
[BindProperty]属性が、バインドされるプロパティであることを示しています。

RedirectToPageメソッド

最後に、

returnRedirectToPage("./Index");

で、同一パスの、Indexページにリダイレクト(遷移)させています。

Create.cshtml

Create.cshtmlについて見てみます。

@page
@model RazorPagesMovie.Pages_Movies.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1><h4>Movie</h4><hr/><divclass="row"><divclass="col-md-4"><formmethod="post"><divasp-validation-summary="ModelOnly"class="text-danger"></div><divclass="form-group"><labelasp-for="Movie.Title"class="control-label"></label><inputasp-for="Movie.Title"class="form-control"/><spanasp-validation-for="Movie.Title"class="text-danger"></span></div><divclass="form-group"><labelasp-for="Movie.ReleaseDate"class="control-label"></label><inputasp-for="Movie.ReleaseDate"class="form-control"/><spanasp-validation-for="Movie.ReleaseDate"class="text-danger"></span></div><divclass="form-group"><labelasp-for="Movie.Genre"class="control-label"></label><inputasp-for="Movie.Genre"class="form-control"/><spanasp-validation-for="Movie.Genre"class="text-danger"></span></div><divclass="form-group"><labelasp-for="Movie.Price"class="control-label"></label><inputasp-for="Movie.Price"class="form-control"/><spanasp-validation-for="Movie.Price"class="text-danger"></span></div><divclass="form-group"><inputtype="submit"value="Create"class="btn btn-primary"/></div></form></div></div><div><aasp-page="Index">Back to List</a></div>

これを見ると、スキャフォールディングエンジンは、IDプロパティは出力の対象外のようですね。

Create.cshtmlでは、Index.cshtmlでは使われていなかったタグヘルパーがいくつか使われています。

formタグヘルパー

<formmethod="post">

見た目は、HTMLの<form>タグそのものですが、自動的に偽造防止トークンが出力されます。
僕の環境だと、</form>閉じタグの直前に以下の隠しフィールドが挿入されてました。

<inputname="__RequestVerificationToken"type="hidden"value="CfDJ8Kws0PPAm1NChsmiTNfRcsX11bkfDPchGc83DJyKlltDfES4-RRpQRGC1_kpkDISRPnKaeSbEBVxYPCqSppRFCM1PieCGNkLk_2u8oUEuBeguEpVQISizdTFjnuCRDIu-IGMQEDP7MNq6UJM57TBku8">

label タグヘルパー

<labelasp-for="Movie.ReleaseDate"class="control-label"></label>

このヘルパーは、以下のタグを生成します。

<labelclass="control-label"for="Movie_ReleaseDate">ReleaseDate</label>

asp-forで、指定したモデルに該当する for属性と、ラベルのキャプションを生成します。<label>要素のコンテンツも自動で挿入されている点に注目です。

input タグヘルパー

<inputasp-for="Movie.ReleaseDate"class="form-control"/>

asp-for属性で指定したモデル(ここでは、Movie.ReleaseDate)の型やDataAnnotation属性の情報を使用して、適切な<input>要素を出力します。

<inputclass="form-control"type="date"data-val="true"data-val-required="The ReleaseDate field is required."id="Movie_ReleaseDate"name="Movie.ReleaseDate"value="">

また、jQueryのクライアント検証に必要な属性も追加します。例えば、data-val-requiredは、必須属性であることを示しています。この属性値で設定したメッセージが、後述の検証タグヘルパー(asp-validation-for)の場所に表示されます。

アンカータグヘルパー

<aasp-page="Index">Back to List</a>

アンカータグを生成するタグヘルパーです。hrefではなく、asp-page=属性を指定します。ASP.NET Core MVCだと、asp-controller, asp-actionですが、Razorページの場合は、asp-pageです。

この場合は、以下のHTMLが出力されます。

<ahref="/Movies">Back to List</a>

検証タグヘルパー

<divasp-validation-summary="ModelOnly"class="text-danger"></div>
<spanasp-validation-for="Movie.ReleaseDate"class="text-danger"></span>

この2つが、検証タグヘルパーです。asp-validation-summary, asp-validation-for属性がそれを表します。検証エラーがあった時に、ここにエラーメッセージが表示されます。

asp-validation-summaryは、検証エラーの概要を表示するために使用されます。

asp-validation-forは、特定の入力項目に対する検証エラーメッセージを表示するために使用されます。

なお、create.cshtmlの最後にある

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

は、クライアント検証に必要になります。これがないと、jQueryのクライアント検証が有効になりません。

具体的には、_Layout.cshtml内に以下の記述があるのですが、

@RenderSection("Scripts", required: false)

この@RenderSectionが書かれた場所に、_ValidationScriptsPartial.cshtmlに書かれた内容が、挿入されることで、jQueryクライアント検証が有効になります。

wwwroot フォルダ

jQueryの話題がすこし出てきたので、wwwrootフォルダの中ものぞいてみます。
このフォルダは、以下のようなフォルダ階層になっていました。

wwwroot
    ├─css
    ├─js
    └─lib
        ├─bootstrap
        │  └─dist
        │      ├─css
        │      └─js
        ├─jquery
        │  └─dist
        ├─jquery-validation
        │  └─dist
        └─jquery-validation-unobtrusive

アプリで作成した css, js ファイルは、それぞれ、css, js フォルダに置きます。libは、外部パッケージを置く場所のようです。

画像ファイルがある場合は、imagesフォルダを作成するのが標準のやり方のようです。

ちなみに、bootstrapのバージョンは v4.3.1 jQuery のバージョンは v3.3.1 でした。

その他のページ

Index, Create の2つのページについて見てきましたが、残りのページの特徴的な部分だけ見てみましょう。

asp-route-id属性

以下は、Delete.cshtml.csのOnGetAsyncメソッドです。

publicasyncTask<IActionResult>OnGetAsync(int?id){if(id==null){returnNotFound();}Movie=await_context.Movies.FirstOrDefaultAsync(m=>m.ID==id);if(Movie==null){returnNotFound();}returnPage();}

引数 id を受け取っています。

index.cshtmlの以下のリンクがクリックされた時に、この上記メソッドが呼び出されます。

<aasp-page="./Delete"asp-route-id="@item.ID">Delete</a>

asp-route-id="@item.ID"で指定した IDの値が、引数idに渡ることになります。

ちなみに、上記のタグヘルパーで、以下のような出力が得られます。

<ahref="/Movies/Delete?id=2">Delete</a>

この例では、仮引数idに、2が渡ってくることになります。

return NotFound()

もし、idに該当する行がテーブルになければ、

returnNotFound();

で、404がクライアントに返ります。

試しに、無理やり NotFound()を返してみたら、エラーページは返らないようです。以下、safariの開発者ツールで確認した画面です。

スクリーンショット 2019-10-30 21.36.01.png

たしかに、404が返っています。

でもこれではユーザは何が起こったかわからないので、何らかのカスタマイズが必要そうですね。これは後で調べます。

一通りページ関連のソースコードを見てみたので、次は、チュートリアルの「データベースの操作」に移ります。


Viewing all articles
Browse latest Browse all 8895

Trending Articles