WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

Visual Studio 2022 の ASP.NET MVC アプリ

by WebSurfer 2021年11月22日 16:28

2021 年 11 月 8 日にリリースされた Visual Studio Community 2022 を自分の PC にダウンロードして使えるように設定しました。

Visual Studio Community 2022

まず手始めに、テンプレートを使って .NET 6.0 の ASP.NET Core MVC アプリのプロジェクトを作ってみましたので、Visual Studio 2019 との違いなど気が付いた点を備忘録として書いておきます。

注: ここに書いたことは自分が最初にプロジェクトを作ったときに気が付いた点のみで他にも変更点は多々あるようです。詳しくは Microsoft のドキュメント「ASP.NET Core 5.0 から 6.0 への移行」を見てください。

新しいプロジェクトの作成

この記事を書いた時点での Visual Studio のバージョンは 17.0.1、.NET SDK のバージョンは 6.0.100、.NET Runtime のバージョンは 6.0.0 です。

(1) 開発サーバー

デフォルトで Kestrel をエッジサーバーとして使うようになってました。

開発サーバー

なので、その設定のまま Visual Studio からアプリを起動すると以下のように dotnet run コマンドによってアプリが Kestrel で実行され、

dotnet コマンド

処理された応答が自動的にブラウザに表示されます。(注: 自動的にブラウザに表示されるのは Visual Studio を使った場合です)

ちなみに、Visual Studio 2019 ではデフォルトでは IIS Express を使用するインプロセスホスティングモデルになります。

詳しくは先の記事「開発環境で Kestrel 利用 (CORE)」を見てください。

(2) Bootstrap

Bootstrap.css, Bootstrap.js のバージョンが 5.1.0 にアップグレードされています。

ちなみに、Visual Studio 2019 で .NET 5.0 の ASP.NET Core MVC アプリを作った場合のバージョンは 4.3.1 になります。

自分が気が付いた限りですが、特に大きく違うのがラベルとテキストボックスの表示でした。他にも違いは多々あると思いますが。

Login 画面

他に、Bootstrap.js が jQuery に依存しなくなったことも大きな違いだと思います。

(3) Startup.cs / Program.cs

Startup.cs が無くなっていました。Visual Studio 2019 ではそれにサービス、ミドルウェア追加のためのコードを書いていたのですが、それらは Program.cs に移すことにしたらしいです。

Program.cs にエントリーポイントとして存在しなければならないはずの Main メソッドが見当たりません。Microsoft の「新しい C# テンプレートで、最上位レベルのステートメントが生成される」はコンソールアプリのドキュメントですが、ここに書いてあることが ASP.NET Core MVC にも適用されているようです。

(4) NULL 許容参照型

NULL 許容参照型がプロジェクト全体で有効化されています。

NULL 許容参照型

ASP.NET Core Identity 関係の .cshtml ファイルのソースコードにそれに対応してない部分があって警告が出ます。(.cshtml.cs ファイルの方は #nullable disable の付与で対応済み)

自分が試した時には Logout.cshtml, Login.cshtml, Register.cshtml, _Layout.cshtml の 4 つのファイルで警告が出ました。

Layout.cshtml は (string) ⇒ (string?) に変更、Logout.cshtml は if (User.Identity?.IsAuthenticated ?? false) に変更、Login.cshtml と Register.cshtml は @foreach (var provider in Model.ExternalLogins!) というように null 免除演算子 ! の追加で対処できます。

(5) CSS 分離

.NET 5.0 の Blazor アプリで導入された CSS 分離のための仕組みが MVC アプリにも導入されました。下の画像は Views/Shared/_Layout.cshtml の一部ですが、赤枠で囲ったコードがその機能を使うためのものです。

CSS 分離

Microsoft のドキュメント「ASP.NET Core Blazor の CSS の分離」によると、CSS スタイルを個々のビューに分離して、次のことを回避するのが目的だそうです。

  • 維持が困難なグローバル スタイルへの依存関係
  • 入れ子になったコンテンツでのスタイルの競合

具体的には、以下のようにビューと同じ名前の css ファイルを作成し、

CSS ファイル

このセクションの一番上の画像のように <link ... href="~/<プロジェクト名>.styles.css" ... という外部 css ファイルへの参照をコードに含めると、それからダウンロードされる css ファイルには以下のように属性セレクタが含まれるようになり、

CSS の属性セレクタ

当該ビュー(上の例では _Layout.cshtml)の html 要素には上の css の属性セレクタに設定された属性が付与されます。

html 要素の属性

その結果、css スタイルを個々のビューに分離することができます。

(6) ブラウザーリンクとファイルウォッチャー

html ソースを見ると以下のコードが自動的に(勝手に)生成されています。

ブラウザーリンクとファイルウォッチャー

前者は名前からすると Visual Studio 2015 にもあったブラウザーリンクという機能らしいです。Visual Studio 2019/2022 ではデフォルトで無効になっています。なのにスクリプトファイルを参照しているのは何故か、どのように使うのかは不明・未確認です。

後者はファイルウォッチャートという機能らしいです。aspnetcore-browser-refresh.js で検索すると「ファイル ウォッチャーを使用した ASP.NET Core アプリの開発」という記事が見つかります。

その記事の「ブラウザーの更新」のセクションに "開発中のこのような場合には、アプリに手動でスクリプトを挿入します。 たとえば、Web アプリを構成してスクリプトを手動で挿入するには、_framework/aspnet-browser-refresh.js を含むようにレイアウト ファイルを更新します。" と書いてあります。

Visual Studio 2022 で提供されたホットリロードというデバッグ中にコードを変更しながら開発を行うことを可能にした機能を実現するために使うものらしいですが、詳細は調べ切れてなくて不明です。

これらをどのように利用できるかは今後の課題ということで・・・

Tags: , ,

CORE

ASP.NET Core アプリに 2 要素認証を実装

by WebSurfer 2021年11月6日 12:32

Visual Studio 2019 のテンプレートで作成した ASP.NET Core 3.1 以降のアプリには TOTP ベースの認証アプリを用いて 2 要素認証を行うコードが実装されています。なので、開発の際プログラマは何も実装しなくても、Google Authenticator App などの認証アプリを用いた 2 要素認証を有効にできます。

EnableAuthenticator 画面

実は、最初、自分は Microsoft のドキュメント「ASP.NET Core での多要素認証」にいろいろ書いてあるのを見て惑わされて、一体何をどのように実装すればいいのか分からなかったです。(汗)

実際に試してみると、そのドキュメントの「MFA TOTP (時間ベースのワンタイム パスワード アルゴリズム)」セクションの 2 要素認証のコードは実装済みで、何も手を加える必要はなかったです。

唯一、認証アプリに秘密キーを共有させる画面(Manage/EnableAuthenticator 画面)には QR コードが表示されないのが難点でしたが、そこは別のドキュメント「ASP.NET Core での TOTP authenticator アプリの QR コード生成を有効にする」に qrcode.js という JavaScript ライブラリを使って表示できるようにする方法が書いてありました。

この記事の一番上の画像が、そのドキュメントに従って QR コードを表示できるようにした Manage/EnableAuthenticator 画面です。そうするだけで、テンプレートで実装済みの 2 要素認証は十分実用になると思います。

という訳で、実際にいろいろやってみましたので、その結果を備忘録として残しておきます。

(1) プロジェクトの作成

Visual Studio 2019 16.11.5 のテンプレートで、認証は「なし」にして .NET 5.0 の ASP.NET Core MVC アプリのプロジェクトを作成しました。

その後、スキャフォールディング機能を利用して ASP.NET Core Identity 関係のソースコードを一式全て実装します。(認証を「個別のユーザー」にすると、認証関係の機能は Razor Class Library (RCL) として提供され、ソースコードはプロジェクトには含まれません。後で必要な部分のみソースコードに差し替えることができますが、いろいろ面倒です)

(2) ユーザーの登録

新規ユーザーとして oz@mail.example.com (以下、oz と書きます) というユーザーを登録します。

ユーザー登録しただけの状態ですと、ASP.NET Core Identity が使う SQL Server データベースの AspNetUsers テーブルの当該ユーザーの TwoFactorEnabled 列は False となっています。

AspNetUsers テーブル

TOTP 用の秘密キー等は AspNetUserTokens テーブルに格納されますが、この時点ではユーザー oz のデータはありません。

(2) TOTP 秘密キーの生成

ログインして Manage/Index 画面に進み、[Two-factor authentication]をクリックして Manage/TwoFactorAuthentication 画面を表示して、そこで [Add authenticator app]をクリックします。

TwoFactorAuthentication 画面

この記事の一番上の画像の Manage/EnableAuthenticator 画面に遷移しますので、そこで認証アプリに秘密キーを渡すことができます。

ユーザー oz の TOTP 用の秘密キーが生成され、SQL Server データベースの AspNetUserTokens テーブルに格納されます。

AspNetUserTokens テーブル

ちなみに、もしここで認証アプリには秘密キーは渡さずログオフした場合、次回ログインして Manage/TwoFactorAuthentication 画面を表示すると上の Manage/TwoFactorAuthentication 画面とは違って以下のようになります。

TwoFactorAuthentication 画面

[Setup authnticator app]をクリックすると、この記事の一番上の画像の Manage/EnableAuthenticator 画面に遷移します。

[Reset authentication app] をクリックすると Manage/ResetAuthenticator 画面に遷移します。

ResetAuthenticator 画面

上の画面で[Reset authenticator key]をクリックすると、秘密キーが変更されてから、この記事の一番上の画像の Manage/EnableAuthenticator 画面に遷移します。

(3) TOTP 秘密キーの共有

この記事の一番上の画像の Manage/EnableAuthenticator 画面に表示された QR コードを認証アプリでスキャンすると 6 桁の数字のパスワードをが認証アプリに表示されますので、それを[Verification Code]欄に入力し[verify]ボタンをクリックすると以下の Manage/ShowRecoveryCodes 画面に遷移します。

ShowRecoveryCodes 画面

認証アプリをインストールしたデバイスを紛失するとアクセスできなくなるので、それに備えてリカバリーコードは記録しておいた方が良いとのことです。リカバリーコードは秘密キーと共に AspNetUserTokens テーブルに保存されます。

AspNetUserTokens テーブル

以上の操作により、ASP.NET Core アプリと認証アプリで秘密キーが共有され、AspNetUsers テーブルの TwoFactorEnabled は True に設定されて、認証アプリを利用しての 2 要素認証が可能になります。

(4) 2 要素認証でのログイン

一旦ログアウトしてから、まずログイン画面でのメールとパスワードによるログイン操作を行います。成功すると以下の Account/LoginWith2fa 画面に遷移しますので、認証アプリを起動してパスワードを取得し、[Authenticator code]欄に入力して[Log in]ボタンをクリックすれば 2 要素認証機能が働いでログインできます。

LoginWith2fa 画面

(5) その他

2 要素認証が有効になった後 Manage/TwoFactorAuthentication 画面を表示すると以下のようになります。

TwoFactorAuthentication 画面

そこで[Disable 2FA]ボタンをクリックすると Manage/Disable2fa 画面に遷移します。

Disable2fa 画面

上の画面で[Disable2fa]ボタンをクリックすると AspNetUsers テーブルの当該ユーザーの TwoFactorEnabled 列は False となり、ログイン画面でメールとパスワードでログイン操作を行うだけでログインできるようになります(2 要素認証は無効になります)。

再度 2 要素認証を有効にするには、ログイン画面でメールとパスワードでログイン操作を行ってログインしてから Manage/TwoFactorAuthentication 画面に進み、[Setup authenticator app]ボタンをクリックして Manage/EnableAuthenticator 画面を表示し、上のステップ (3) と同じ操作を行います。

[Reset recovery codes]ボタンをクリックすると Manage/GenerateRecoveryCodes 画面に遷移します。

GenerateRecoveryCodes 画面

そこで[Generate Recovery Codes]ボタンをクリックすると、上のステップ (3) にあるのと同様な Manage/ShowRecoveryCodes 画面に遷移し、再発行されたリカバリーコードが表示されます。同時に AspNetUserTokens テーブルの既存のリカバリーコードは画面に表示されたものに書き換えられます。


なお、MVC5 ではテンプレートに標準で実装されていた SMS を用いた 2 要素認証は非推奨だそうです。上に紹介した記事には "SMS を使用する方法は推奨されなくなりました。 この種の実装には、既知の攻撃ベクトルが多すぎます" と書いてあります。

Core 1.1 用には「SMS を使用した 2 要素認証 (ASP.NET Core)」というチュートリアルがありますが、.NET Core 3.1 とか .NET 5.0 の新しいバージョンのテンプレートで作ったプロジェクトにはそのチュートリアルのコードは実装されていません。

Tags: , , , , , ,

CORE

MySQL で Contoso University チュートリアル (CORE)

by WebSurfer 2021年10月19日 12:22

データベースに MySQL を利用して、ASP.NET Core MVC アプリを Micorsoft のチュートリアル「ASP.NET Core MVC と EF Core - チュートリアル シリーズ」に従って作成してみました。その際に気になったこと、憶えておいた方が良さそうなことを備忘録として残しておきます。

Contoso University アプリ

チュートリアルはデータベースに SQL Server (LocalDB) を使うことを前提に書かれています。それを MySQL に代えて、チュートリアルの「1. 開始するには」から「8. コンカレンシーの競合の処理」までの手順に従って実装しました。

この記事で使った MySQL サーバーは Windows 10 にインストールしたバージョン 8.0.19 です。詳しくは先の記事「MySQL をインストールしました(その 3)」を見てください。

以下に「1. 開始するには」から「8. コンカレンシーの競合の処理」の各手順において、気になったこと、憶えておいた方が良さそうなことを書いておきます。

一番気になっていたのは「8. コンカレンシーの競合の処理」の手順の楽観的同時実行制御の実装でしたが、モデルのプロパティの定義だけ変更すれば MySQL でも可能でした。その結果が上の画像です。

(1) 開始するには

この記事では、チュートリアルに書いてあるようなプロジェクトの新規作成はせず、先の記事「MySQL で Movie チュートリアル (CORE)」で作成した既存のプロジェクトをベースに使いました。

まず Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore を NuGet からインストールします。その他の必要な NuGet パッケージはベースに使った既存のプロジェクトにインストール済です。結果は以下のようになります。

NuGet パッケージ

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore の役割は "ASP.NET Core middleware for Entity Framework Core error pages. Use this middleware to detect and diagnose errors with Entity Framework Core migrations." だそうです。チュートリアルの「データベース例外フィルターを追加する」と関係があるようです (未確認ですが)。

Strtup.cs ファイルにある Startup クラスの ConfigureServices メソッドに SchoolContext を登録するコードを追加します。以下の通りです。

SchoolContext を登録

チュートリアルの手順「SchoolContext を登録する」に書いてあるコードは SQL Server 用で、これを MySQL 用に変更する必要がありますので注意してください。

次に appsettings.json ファイルに接続文字列を設定します。チュートリアルの例は LocalDB 用ですので MySQL 用に変更します。以下のような感じです (あくまで「感じ」です。MySQL の設定により異なります)。

接続文字列を設定

チュートリアルの「コントローラーとビューを作成する」の手順でスキャフォールディングを行う際、NuGet パッケージに Microsoft.EntityFrameworkCore.SqlServer が必要になるケースがありますので注意してください。エンティティクラスの定義によると思われます。ここ「(1) 開始するには」の手順では不要でしたが、下の「(6) 関連データの読み取り」では必要でした。

アプリを実行すると EF Code First の機能を使って、接続文字列で database=ContosoUniversity1 と指定した通り ContosoUniversity1 という名前のデータベースと、SchoolContext クラスで指定した Course, Enrollment, Student という名前のテーブルが生成されます。

さらにチュートリアルには初期データをシードするためのコードが含まれており、その通り実装すれば、最初にアプリを実行した時にコードに書かれた通り初期データが Course, Enrollment, Student テーブルに登録されます。

(2) 作成、読み取り、更新、削除の操作

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(3) 並べ替え、フィルター、ページング、グループ化

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(4) 移行

ここは Migration 操作を学ぶことを目的としているようです。

チュートリアルでは dotnet コマンドを使っていますが、Visual Studio を使っているならそのパッケージマネージャーコンソールを使った方が簡単だと思います。

NuGet パッケージ Microsoft.EntityFrameworkCore.Tool が必要ですが、上のステップ (1) の画像の通りインストール済みですので、このチュートリアルの操作にはパッケージマネージャーコンソールを使いました。

Drop-Database, Add-Migration, Update-Database という操作を行いますが、データベースが MySQL だから何か問題が出るということは、少なくともこのチュートリアルの操作ではありませんでした。(この後のセクション「(5) 複合データ モデルの作成」では 2 点問題がありましたが)

ベースに使った既存のプロジェクトには他の既存のコンテキストクラスが含まれています。そういう場合は、Drop-Database, Add-Migration, Update-Database 操作の際に、既存のコンテキストと区別するため、-Context SchoolContext オプションの追加が必要です。

(5) 複合データ モデルの作成

MySQL ではチュートリアルの通りにはできないということがありました。問題があったのは以下の 2 点です。

  1. Column 属性」のセクションで Student クラスの FirstMidName プロパティに付与した [Column("FirstName")] 属性。
  2. Department エンティティを作成する」のセクションで Department クラスの Budget プロパティに付与した [Column(TypeName = "money")] 属性。

いずれも Add-Migration の後の Update-Database コマンドで前者は NotImplementedException、後者は MySqlException がスローされ、MySQL のテーブルの変更・生成に失敗します。

前者は既存の Student テーブルの FirstMidName 列の名前を Migration 操作で FirstName という名前に変更しようというものです。NotImplementedException 例外がスローされるということは MySQL 用の Entity Framework にその実装がされてないということのようです。

後者は MySQL には TypeName = "money" で指定した money 型はないためのエラーです。

上記の操作に失敗で先に進めることができなくなったしまったので、やむを得ずゼロから作り直しました。Drop-Database で ContosoUniversity3 をドロップし、Add-Migration で生成された Migrations フォルダのファイルを削除し、チュートリアルに従ってコードの修正・追加を最後まで終わらせてから(途中で Migration 操作は行わないで)、最後に一気に Add-Migration, Update-Database コマンドでデータベースを生成しました。

問題となった Student クラスの FirstMidName プロパティに付与した [Column("FirstName")] 属性ですが、データベースを生成する最初の時点で付与しておけば指定した通り Student テーブルの当該列の名前は FirstName になります。チュートリアルのように最初に FirstMidName という列名で作って、後で Migration 操作で変更しようとすると、その操作に必要なメソッド等が実装されてないからか、NotImplementedException がスローされます。

Department クラスの Budget プロパティに付与した [Column(TypeName = "money")] 属性は [Column(TypeName = "decimal")] に変更が必要です。

チュートリアルの「移行を追加する」の手順に、Migrations フォルダに生成された ComplexDataModel クラスのコードに手を加えるように書かれていますが、それは先の操作でデータベースに作成済みの既存のレコードとの整合を取るためのもので、ゼロから一気に作るのであれば不要です。

(6) 関連データの読み取り

スキャフォールディング操作の際 NuGet パッケージに Microsoft.EntityFrameworkCore.SqlServer が必要になります。ちなみに「(1) 開始するには」の手順では不要でした。上の「(5) 複合データ モデルの作成」でいろいろ変更・追加したエンティティクラスの定義によると思われます。

そのため、最初のスキャフォールディング操作には失敗しますが、その際 Microsoft.EntityFrameworkCore.SqlServer が自動的に追加されますので、以下のようになっていることを確認し再度スキャフォールディングすれば成功すると思います。

NuGet パッケージの確認

上の画像の 1 番目から 4 番目のパッケージ間でのバージョンの不一致、ランタイムとのバージョンの不一致があるとスキャフォールディングに失敗することがありますので注意してください。

もう一つ、MySQL とは関係ないチュートリアルのミスですが、Courses Taught by Selected Instructor テーブルを表示する View のコードで selectedRow = "success"; となっていますが、selectedRow = "table-success"; にしないと Select しても背景色が変わらないので修正してください。

(7) 関連データの更新

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(8) コンカレンシーの競合の処理

チュートリアルの手順では Department テーブルに rowversion (Transact-SQL) 列を追加し、それを使って楽観的同時実行制御を実装するというものです。

MySQL には rowversion はなく、それに代わるものとして timestamp というものがありますが同じではありません。.NET 側では rowversion は byte[] 型、timestamp は DateTime 型になるのですが、そのような違いがあっても Entity Framework による楽観的同時実行制御に使えるかが懸念したところです。

チュートリアルには Department クラスに byte[] 型の RowVersion プロパティを追加して Migration 操作でデータベースの Department テーブルに RowVersion 列を追加するよう書かれています。まずそこを MySQL ように変更する必要があります。

ググって見つけた記事 entity framework 6 mysql rowversion を参考に以下のプロパティ定義を使いました。

[Timestamp]
[ConcurrencyCheck]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime TimeStamp { get; set; }

timestamp では分解能が不十分という話もあるようですが、11.3.1 DATE、DATETIME、および TIMESTAMP 型によると "DATETIME または TIMESTAMP 値には、マイクロ秒 (6 桁) までの精度で後続の小数秒部分を含めることができます" とのことですので十分ではないかと思います。なので今回は上のプロパティ定義で実装してみました。

マイクロ秒以内で同時実行が行われるケースも考えなければならないとか、どうしても分解能が気になるという場合は Better way to implement a row version with EF Core and MySQL? に紹介されている方法もありそうです。

Add-Migration, Update-Database コマンドで MySQL の Department テーブルには以下のようにそれらしい形で datetime 型の Timestamp 列が追加されます。timestamp 型でないのがちょっと気になりますが。

Department テーブル

特に気になっていたのはスキャフォールディング機能を使って Department テーブルの CRUD 用のコントローラーとビューを生成したとき TimeStamp 列を使って同時実行制御を行う機能が含まれるかということです。

結果は、SQL Server の場合と同様に、同時実行で DbUpdateConcurrencyException 例外がスローされ楽観的同時実行制御ができるようになりました。この記事の一番上の画像がその結果です。

なお、プロパティ名をチュートリアルの RowVersion から TimeStamp に変えましたので、チュートリアルの「Edit メソッドを更新する」の Edit アクションメソッドのコードで RowVersion, rowVersion となっているところは TimeStamp, timeStamp に変更が必要です。型の指定も byte[] となっているところを DateTime に変更する必要があります。

Edit 用の View のコードも同様で、「Edit ビューを更新する」のコードで RowVersion, rowVersion となっているところは TimeStamp, timeStamp に変更が必要です。

Delete については View の <input type="hidden" asp-for="RowVersion" /> の RowVersion を TimeStamp に変更します。


チュートリアルにはこの先に「9. 継承」と「10. 高度なトピック」がありますが、それはまた次の機会にということで・・・

Tags: , , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar