vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

ASP.NET Coreに触れてみる 1

はじめに

前から気にはなっていた、 ASP.NET Core を触ってみることにしました。

まずはチュートリアルから。。。と思ったのですが、Razor View にしろ MVC Web アプリにしろ、プロジェクトを Web Application のテンプレートで作った時点で複数ページ分ガッツリ作られてしまいます。

docs.microsoft.com

で、チュートリアルの内容としてもこれをベースに Model を追加したりすることになるため、おねがい、チョ待って!チョ待って!となりました(これはこれで進めたいのですが)。

折よく手に入れた本がプロジェクトを Empty で作って一つずつ見ていく、という内容だったこともあり、もうちょっと基本的なところから見てみたいと思い、その内容を気力が続く限り書き残しておきたいと思いました。

Programming ASP.NET Core

生成されたフォルダ・ファイル

まずはプロジェクトを Empty で作って、生成されるフォルダとファイルを見てみます。

  • wwwroot: フォルダ。CSS や画像などの静的ファイルを置く(有効にした場合)。 Web アプリのルートディレクトリとなる。
  • Program.cs: メインクラス。使用するサーバーや Startup として使用するクラスの指定などを行う。
  • Startup.cs: Startup クラス。DI の Inject 対象クラスの登録や Razor View の有効化などを行う。

プロジェクトを Web Application で生成すると、これに加えて Pages というフォルダが作られ、そこに Razor View のファイルが置かれます。

Razor View

先ほどから登場している Razor View 。

これは、 jspasp のように、 HTML にサーバーサイドの言語(ここでは C#VB.NET )で処理が書ける、といったもののようです。

Xamarin や WPF でいう XAML とコードビハインドのように、Index.cshtml <-> Index.cshtml.cs というセットで処理を書きます。

docs.microsoft.com

後述しますが、 Startup クラスで MVC が有効になっている場合、アクセスした URL のパスに合わせて Razor View が表示されます。

例えば Pages/About.cshtml がある場合、 localhost:5XXX/About にアクセスすると About.cshtml が表示されます。

またコードビハインドには OnGet メソッドがデフォルトで作成されており、ページアクセス時に呼ばれます。

ここから、(少なくともデフォルトでは) Routing の役割を担う Controller クラスは自作せず、自動で Routing されるらしいことがわかります。

また各ページごとに GET や POST の処理を行う、と。

特殊な Razor View

基本的な動作は上記の通りですが、いくつか特別な動きをするものがあります。

Index.cshtml

Index と名付けられたページは、トップドメインで表示されます( localhost:5XXX )。

ViewStart.cshtml と Layout.cshtml

head・body タグをはじめ、各ページ共通で表示したい要素がある場合 Layout にまとめることができます。

この Layout 、デフォルトでは _Layout.cshtml となっていますが、変更することができます。

どのファイルを Layout とするかは _ViewStart.cshtml で指定されており、これを変更することで別名のファイルが指定できます。

_ViewStart.cshtml

@{
    Layout = "_Layout";
}

ただし上記の場合では _Layout.cshtml というファイルがないと実行時に例外が発生します。

なおコンパイルエラーにはならないものの、 Visual Studio (または ReSharper )が赤く表示してくれるので事前の確認も可能ではあります。

また Layout ファイルでは、各ページで定義している表示内容をどこに表示するかを、 @RenderBody() で指定します。

_Layout.cshtml

< !DOCTYPE html>
< html>
< head>
    < meta charset="utf-8" />
< /head>
< body>
    < h1>世界さん、ちーっす< /h1>
    @RenderBody()
< /body>
< /html>

Layout ファイルに @RenderBody() が含まれていないと、これまた実行時に例外が発生します。

こちらは赤く表示もされないため、より注意が必要です。

Program.cs

前述の通り、メインクラスです。

デフォルトでは WebHost.CreateDefaultBuilder を実行し、埋め込みのサーバー( Kestrel )の使用やルートディレクトリの指定、 Startup クラスの指定などを行い、アプリを実行します。

Startup.cs

Startup クラスではデフォルトで2つのクラスが生成されます。

  • ConfigureServices
  • Configure

ConfigureServices に対して DI コンテナに追加したいクラスや MVC の登録を行い、Configure で有効化する、という動きをするようです。

また Configure では Error の発生時に表示するページを指定することもできます。

これにより開発時には詳細なエラー内容がわかる開発者向けのページを表示したり、本番環境では不要な情報を見せない、といったことが可能になります。

Startup.cs

public void ConfigureServices(IServiceCollection services) {
    // DI コンテナへのクラス( interface )の追加(サービスに登録).
    services.AddSingleton();
    // MVC を追加する( Razor View を使うため).
    services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
    if (env.IsDevelopment()) {
        // 開発環境においては開発者向けのエラーページを表示.
        app.UseDeveloperExceptionPage();
    }
    // wwwroot に置かれている静的ファイルを有効にする.
    app.UseStaticFiles();
    // MVC を有効にし、URL アクセス時に Razor View が表示されるようにする.
    app.UseMvc();
}
  • ConfigureServices で IServiceCollection に対して AddMvc() を実行せずに Configure で UseMvc() を実行すると InvalidOperationException が発生します。
  • Configure で UseMvc() を実行しないと、真っ白のページが表示されます( Razor View 以外に表示するものを用意していない場合)。

その他 Startup でできる(やるべき)ことはまだまだたくさんあるようですが、おいおい調べていきたいと思います。

docs.microsoft.com

Dependency Injection

ASP.NET Core では Dependency Injection を、

  1. Startup.cs の ConfigureServices で対象クラス( interface )をコンテナに追加(サービスに登録)
  2. 1.のクラスを利用するクラスのコンストラクタで受け取る

という流れで実現しています。

今回は任意で interface とその実装クラスを作り、それを Index.cshtml.cs で受け取ってみます。

IDiSample.cs

  • DI で Inject する interface
namespace WebApplication1
{
    public interface IDiSample {
        void Say();
    }
}

DiSample

  • IDiSample.cs の実装クラス
using System;

namespace WebApplication1 {
    public class DiSample: IDiSample {
        public void Say() {
            Console.WriteLine("Hello DI");
        }
    }
}

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace WebApplication1 {
  public class Startup {
    public void ConfigureServices(IServiceCollection services) {

        // サービスに Inject する interface, 実装クラスを(ここではシングルトンとして)追加.
        services.AddSingleton< IDiSample, DiSample>();

        services.AddMvc();
    }
    ~省略~
  }
}

Index.cshtml.cs

using System;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace WebApplication1.Pages
{
    public class IndexModel : PageModel {

        // コンストラクタで登録されたクラスを受け取り.
        public IndexModel(IDiSample s) {
            s.Say();
        }
        
        public void OnGet() {
            Console.WriteLine("hello");
        } 
    }
}

おわりに

プロジェクト生成時に Web Application を選ぶと、数ページ分ガッツリコードが出てくるのには面喰いましたが、一つ一つたどっていくとちょっとは理解できたような気がします。

次回は Model を Scaffold で生成する辺りの話。。。のはず。

参照