ASP.NET Core で Riot.js その１


ASP.NET Core が RTM になったので試しに Riot.js を使ってみます。
今回はプロジェクトを作成して Riot.js を使って Hello World するところまでやります。

* ASP.NET Core で Riot.js その１
* [ASP.NET Core で Riot.js その２](http://qiita.com/t-koyama/items/727d06107a1e9e00f5d2)

# 開発環境
Windows 10 Pro
Visual Studio Code

# 必要なもの
以下のツールが必要ですのでインストールしておいてください。

* Node.js
* Yeoman
* Bower
* generator-aspnet
* .NET Core


# プロジェクトの作成
Web API アプリケーションのプロジェクトを作成します。

```bash
yo aspnet webapi [作成するプロジェクト名]
```

作成したプロジェクトのディレクトリに移動して Visual Studio Code で開きます。

```bash
cd [作成するプロジェクト名] & code .
```

Visual Studio Code が起動したら、ショートカットキーか  [表示] - [Toggle Integrated Terminal] で エディタ内にターミナルを開いておきます。

| OS       | ショートカットキー |
| :------ | :-------------------- |
| Win     | Ctrl + @                    |
| Mac    | ^`                  |
| Linux  | Ctrl + `             |

# パッケージの追加

実行時に html ファイルを表示したいので  project.json の `dependencies` に Microsoft.AspNetCore.StaticFiles を追加します。

```json:project.jsonの一部
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Mvc": "1.0.0",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Configuration.CommandLine": "1.0.0",
    "Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0"
  },
```

保存したらターミナルで以下のコマンドを入力してパッケージを入手します。

```bash
dotnet restore
```

# Startup.cs の変更
静的ファイルを扱うように Startup.cs を変更します。

```csharp:Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace AspRiotApp
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework 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, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseMvc();

            // wwwroot/index.html を起動時に表示するようにする
            app.UseDefaultFiles();
            
            // 静的ファイルを扱えるようにする
            app.UseStaticFiles();
        }
    }
}
```

`UseDefaultFiles()` 拡張メソッドで実行時に index.html を呼び出すようにして `UseStaticFiles()` 拡張メソッドを呼んで静的コンテンツを扱えるようにしています。

# index.html の追加
wwwroot フォルダに index.html を追加します。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ASP.NET Core で Riot.js</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/riot/2.5.0/riot+compiler.min.js"></script>
</head>
<body>
    <!-- app タグ -->
    <app title="ASP.NET Core"></app>
    
    <!-- app タグを作成 -->
    <script type="riot/tag">
        <app>
            <h1>Hello { title } With RiotJS</h1>
            this.title = opts.title;
        </app>
    </script>
    
    <!-- 作成したタグをマウントする -->
    <script>
        riot.mount('*');
    </script>
</body>
</html>
```

app タグを追加して `title` 属性に値をセットしています。

```html
<app title="ASP.NET Core"></app>
```

タグを作成する場合は script タグの type 属性に `"riot/tag"` を指定します。

```html
<script type="riot/tag">
...
</script>
```

上記 script タグ にタグとして展開する内容を記述します。html 内で タグを定義する場合はタグ内の script タグは省略します。省略しないで記述する場合は以下のようになります。

```html
<app>
    <h1>Hello { title } with RiotJS</h1>
    <script>
        this.title = opts.title;
    </script>
</app>
```

属性に設定されている値は `opts` でアクセスします。テンプレートへの値のバインディングは `{...}` でおこないます。

とりあえずこの状態で実行します。ターミナルから以下のコマンドを入力します。

```bash
dotnet run
```

実行した状態でブラウザから localhost に接続すると

>Hello ASP.NET Core with RiotJS

と表示されます。

localhost を停止するときはターミナル上で `Ctrl + C` すれば停止できます。

# tag ファイルを作成して読込ませる
tag はindex.html 内だけではなくて、別のファイルに定義して使用することもできます。
試しに index.html 内で定義している app タグを app.tag ファイルを作成して移動してみます。

## app.tag の作成
wwwroot フォルダに app.tag を追加します。

```html:app.tag
<app>
    <h1>Hello { title } with RiotJS</h1>
    this.title = opts.title;
</app>
```

## index.html の修正
合わせてapp.tag を読込むように index.html を修正します。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ASP.NET Core で Riot.js</title>
    <!-- app.tag の読込み -->
    <script src="app.tag" type="riot/tag"></script>
    <!-- riot+compiler.min.js の読込み -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/riot/2.5.0/riot+compiler.min.js"></script>
</head>
<body>
    <!-- app タグ -->
    <app title="ASP.NET Core"></app>
    
    <!-- 作成したタグをマウントする -->
    <script>
        riot.mount('*');
    </script>
</body>
</html>
```

app.tag を読込むように script タグを追加しています。tag ファイルの読込は riot+compiler.min.js ファイルよりも先となるようにしておきます。

この状態で実行して localhost に接続すると何も表示されないと思います。

ブラウザコンソールを開くと

>HTTP404: 見つかりません - 要求された URI (Uniform Resource Identifier) に一致するものがサーバーに見つかりませんでした。

と表示されるかと思います。デフォルトの状態だと tag ファイルを静的コンテンツとして扱ってもらえないので Startup.cs に tag ファイル を扱えるように処理を追加する必要があります。

## Statup.cs の修正

```csharp:Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace AspRiotApp
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework 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, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseMvc();

            // .tag ファイルを扱えるようにする
            var provider = new FileExtensionContentTypeProvider();
            provider.Mappings[".tag"] = "riot/tag";

            // wwwroot/index.html を起動時に表示するようにする
            app.UseDefaultFiles();

            // 静的ファイルを扱えるようにする            
            app.UseStaticFiles(new StaticFileOptions
            {
                ContentTypeProvider = provider
            });
        }
    }
}
```

`FileExtensionContentTypeProvider` インスタンスを作成して .tag 拡張子のファイルをマッピングしてあげます。

```csharp
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".tag"] = "riot/tag";
```

次に、 `StaticFileOptions` インスタンスを作成して `ContentTypeProvider` プロパティ に先ほど作成した `provider` を設定して    `UseStaticFiles()` に引数で渡してあげれば .tag ファイルを静的コンテンツとして扱えるようになります。

```csharp
app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider });
```

これでエラーなく最初に実行したときに表示された内容が表示されるようになります。

# 次回
Web API から データを取得して一覧を表示するところをやります。