だるろぐ

明日できることは、今日しない。

空のアプリケーションから ASP.NET Core Razor Page を始める

f:id:daruyanagi:20170815033116p:plain

前回は ASP.NET Core Razor Page をチラ見してみましたが、ひな型(サンプル)プロジェクトが割とゴツい。これでは全体的な見通しが悪くて理解が進まないし、また「Razor Pages はカンタン!」というのも伝わりにくいと思いました。

blog.daruyanagi.jp

そこで、今回は Visual Studio 2017.3 を利用して“空”のアプリケーションから Razor Pages で Hello! World するまでをステップバイステップでやっていこうかなと思います。

空のアプリケーションを作成する

f:id:daruyanagi:20170815033122p:plain

まず、[新しいプロジェクト]コマンドで .NET Core、ASP.NET Core Web アプリケーションを作成。フレームワークを .NET Core へ、.NET のバージョンを 2.0.0 に切り替えて、“空”のアプリケーションを作成します。

f:id:daruyanagi:20170815033129p:plain

このアプリケーションには、ソリューションファイルなどを除くと

  • Program.cs
  • Startup.cs
  • 静的ファイルを置く wwwroot フォルダー

の3つしかありません。シンプルだね! コードも最低限で、

f:id:daruyanagi:20170815033136p:plain

[F5]キーで実行すると“Hello! World”が表示されるだけです。まずはここから始めましょう。

Program.cs と Startup.cs

Program.cs は、アプリケーションを実行する際、最初に処理されます(エントリポイントってやつだな)。

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace WebApplication3
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}

WebHost をビルドして実行しているようですが、よくわかんないし、今回はとりあえずそのままにしておいていいです。

一方、Startup.cs は初期化を担当しています。空のアプリケーションの場合はこんな感じになっています。

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

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

        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Hello World!");
            });
        }
    }
}

リクエストがあったらレスポンスに“Hello! World”と書いて送るだけのようです。

Razor Pages を使う

Razor Pages は MVC に含まれています。なので、まず Startup.cs で MVC を有効にします。

namespace WebApplication3
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            // もう要らないのでコメントオフ
            // app.Run(async (context) =>
            // {
            //     await context.Response.WriteAsync("Hello World!");
            // });

            app.UseMvc();
        }
    }
}

これだけだと実行して何も表示されないので、ビューも追加しましょう。

f:id:daruyanagi:20170815041428p:plain

まず、アプリケーションルートに Pages フォルダーを作成。Razor Pages の *.cshtml はここに保存します(設定で変更可能できるらしいけど)。

f:id:daruyanagi:20170815035213p:plain

次に Pages フォルダーのコンテキストメニュー から“MVC ビュー”を追加。これは要するに *.cshtml ファイルです。“Razor ページ”でも *.cshtml を追加できますが、ビューモデルの *.cs ファイルもくっついてくるので(これはこれで便利だけど、まだお子さまには早い!)、今回は“MVC ビュー”にしておきました。

ちなみに、名前は Index.cshtml にしておきます。/ にアクセスすると、/Pages/Index.cshtml が自動で表示される仕組みになっていますので(ルーティングの規則については、また機会を改めましょう)。

内容はこんな感じ。ごちゃごちゃ書いてあるのは、[Ctrl]+[A]して[Delete]しちゃって下さい。

@page

<p>Hello! World</p>

[F5]キーで実行すると、こんな感じになるはずです。

f:id:daruyanagi:20170815035710p:plain

できた\(^o^)/

でも、これだけでは Razor Pages を使っている意味がないので、変数を使ってみます(文法に関しては説明しません。ググってね! めっちゃ簡単だよ)。

@page

@{ 
    var message = "Hello! World";
}

<p>@message by Razor Pages</p>

ページの先頭に「Razor Pages だよ!」ってことを知らせるディレクティブ @page を追加している以外は ASP.NET Web Pages(Razor)とまったく変わりません。

保存してブラウザーをリロードするとこんな感じになるでしょう。

f:id:daruyanagi:20170815040013p:plain

あんまり面白くないですかね? こうしてみますか。

@page "{message?}"

@{ 
    var message = Request.Query["message"];
}

<p>@message by Razor Pages</p>

f:id:daruyanagi:20170815040646p:plain

@pages のあとに routing constraint を書けるのは新しいかもしれません。この例では正直意味がないのですが(消してもそのまま動くしな)、モデルで void OnGet(string message) などと受け取れるので覚えておくといい感じ。

@page "{message?}"
@using Microsoft.AspNetCore.Mvc.RazorPages
@model IndexModel

@functions 
{ 
    public class IndexModel : PageModel
    {
        public string Message { get; private set; }

        public void OnGet(string message)
        {
            Message = string.IsNullOrEmpty(message)
                ? "Hello! World"
                : message;
        }
    }
}

<p>@Model.Message by Razor Pages</p>

もっと複雑な型データを受け取るときに役立つでしょう。ちなみに @functions の部分は *.cs ファイルに分離することもできます(NEW!)。

@page "{message?}"
@model IndexModel

<p>@Model.Message by Razor Pages</p>

ちょうど WPF や WinForm のビュー(デザイナー)+コードビハインドみたいな感じで、スッキリしていいですね(こういうことがしたい場合は、最初から“MVC ビュー”ではなく“Razor ビュー”を追加するといいです)。

もっと詳しいことが知りたい場合は、チュートリアルページを参照してください。自分もあとでゆっくり読んでみようと思います。

docs.microsoft.com

docs.microsoft.com

おまけ

f:id:daruyanagi:20170815041046p:plain

空のアプリケーションには 2 つの NuGet パッケージが含まれています。

  • Microsoft.AspNetCore.All
  • Microsoft.NETCore.App

初期状態ではどっちもプレビュー版で、前者のみ v2.0.0 正式版へのアップデートが提供されています。

f:id:daruyanagi:20170815041223p:plain

でも、片っぽだけアップデートすると実行時にコケるみたい。もうちょっと待ちますかね。