読者です 読者をやめる 読者になる 読者になる

C# + NancyでJSONを返すWebアプリを作り、Heroku Buttonを使ってHerokuへデプロイしてみた

C# Heroku Nancy

以前、Ruby + HerokuでJSONを返すWebアプリを作りました。
Ruby + Heroku + Highchartsで食べたリンゴの割合をグラフ化してみた - メモ的な思考的な

最近C#を触っているため、C#で同様なものを作る方法を探してみたところ、C# + Nancy + Herokuで実現できそうなことが分かりました。

そこで、Herokuまわりで気になっていたHeroku Buttonとともに、C# + NancyをHerokuへデプロイすることを試してみました。

 

環境

 

事前調査

Nancyとは

NancyとはRubySinatraみたいなもので、C#でもさくっとWebアプリを作れるものでした。
NancyFx/Nancy - GitHub

 

Nancyのホスト

NancyのホストとしてASP.NETなどがありますが、今回はHerokuで動かすためSelf Hostingという方法を使いました。

そこで、Self Hostingができるライブラリを調べてみたところ、

  • OWIN - Microsoft.Owin.HostingとNancy.Owin
  • 非OWIN - Nancy.Hosting.Self

などがありました。

OWINはRubyのRackみたいなものということで、以下を参考に勉強がてらOWINを使う方法で試してみることにしました。

 

HerokuでNancyを動かす方法

Herokuの3rd party buildpackを使えば、MonoでNancyを動かせそうでした。
Third-Party Buildpacks | Heroku Dev Center

buildpackはいくつかありましたが、公式よりリンクされているものを使うことにしました。現時点では、Mono 3.2.8で動くようです。
friism/heroku-buildpack-mono · GitHub

 

Nancyアプリの作成

以下を参考にNancyアプリを作成します。
Running OWIN/Katana apps on Heroku – Randoom

 

コンソールアプリケーションプロジェクトの作成

今回はSelf Hostingで動かすため、コンソールアプリケーションのプロジェクトを作成します。

 

Gitリポジトリの用意
.gitignoreファイルの追加

gitignore.ioVisualStudioテンプレートのままで問題ないので、ソリューションファイルと同じフォルダに入れておきます。

 

Gitリポジトリの作成

とりあえず、SourceTreeのコンソールなどでGitリポジトリを作成しておきます。

 

NuGetより必要なパッケージをインストール

以下の必要なパッケージをインストールします。

 
なお、インストールしただけでは、Herokuにpushした時にNuGetパッケージがインストールできずにエラーとなります。

そのため、ソリューションの右クリックから「NuGetパッケージの復元の有効化」を選択して、.nugetフォルダ以下をソリューションに追加しておきます。

 

OWINによるNancy起動の実装

OWINを使ったNancyを実装します(Program.cs, Startup.cs)。ほとんど定型っぽいです。

 

Program.cs

特に意味はありませんが、localhostはポート9876で待ち受けるようにしてみました。

using System;

using Microsoft.Owin.Hosting;
using System.Threading;

namespace NancyOwinHeroku_sample
{
    class Program
    {
        private static ManualResetEvent _quitEvent = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            var port = 9876;
            if (args.Length > 0)
            {
                int.TryParse(args[0], out port);
            }


            Console.CancelKeyPress += (sender, eArgs) =>
            {
                _quitEvent.Set();
                eArgs.Cancel = true;
            };


            using (WebApp.Start<Startup>(string.Format("http://+:{0}", port)))
            {
                Console.WriteLine("Running port: {0}", port);
                Console.WriteLine("Started");
                _quitEvent.WaitOne();
            }
        }
    }
}

 

Startup.cs

定型なので、そのままです。

using Owin;

namespace NancyOwinHeroku_sample
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseNancy();
        }
    }
}

 

GETリクエストの処理の実装

プロジェクトにModulesフォルダを追加し、フォルダの中にGETリクエストを処理するクラス(HomeModule.cs)を実装します。

今回は、

ようにしてみました。

 

Modules\HomeModule.cs
using Nancy;

namespace NancyOwinHeroku_sample.Modules
{
    public class HomeModule : NancyModule
    {
        public HomeModule()
        {
            Get["/"] = _ => "Hello World";

            Get["/json"] = _ =>
            {
                var ringo = new[]
                {
                    new { Id = 1, ItemName = "トキ"},
                    new { Id = 2, ItemName = "秋映"}
                };

                return Response.AsJson(ringo);
            };
        }
    }
}

 
なお、Response.AsJson()メソッドで返ってくるJSONレスポンスは

[{"id":1,"itemName":"トキ"},{"id":2,"itemName":"秋映"}]

のように、lowerCamelCaseでした。バージョンアップにより変更がなされたのかもしれません。
NancyFxでWebAPIを作るときに必ず参照すべきライブラリ - らびたろちゃんにっき

 

Procfileの追加

ソリューションファイル(*.sln)と同じフォルダに、Herokuで使うProcfileを用意します。

今回のアプリケーション名は「NancyOwinHeroku-sample.exe」でしたが、環境に応じてexeファイル名も修正します。

web: mono NancyOwinHeroku-sample.exe $PORT

 

ローカルでの動作確認

名前空間予約の構成の追加

Self Hostingの場合、ローカルで実行するには名前空間予約の構成の追加が必要になります。
HTTP および HTTPS の構成 - MSDN

 
PowerShellを管理者で起動し、まずは実行中のユーザーを確認します。

> whoami

 
次に、そのユーザーに対して、パーミッションの追加を行います。<UserName>は、上記のwhoamiで取得したものをコピペすれば良いです。

> netsh http add urlacl url=http://+:9876/ user=<UserName>

 

起動と動作確認

VIsualStudioをデバッグ実行して、コンソールを起動します。

その状態で http://localhsot:9876 へアクセスし、エラーとならないかを確認します。

 
エラーがなければ、ここまでで実装は一段落したため、GItリポジトリにコミットしておきます。

 

WindowsでのHeroku環境セットアップ

Nancyアプリが完成したため、Herokuへとデプロイしますが、今まではUbuntu上でしかHeroku環境を作っていなかったため、今回新しくWindowsでの環境を作ります。

 

Heroku Toolbeltのインストール

以下のURLよりダウンロードします。
Heroku Toolbelt - Heroku

 
インストール先は、念のためデフォルトから以下へと変更しておきます。

C:\Heroku

 
また、インストール途中で、Custome Installを選択するとインストール対象を選べるようになります。

今回は、Gitは手元にある & foreman使わないことから、Heroku Clientのみ選択しました。

 

Herokuのセットアップ

今回はSourceTreeのコンソールを使ってセットアップします。

まずはHerokuにログインします。

$ heroku login
Enter your Heroku credentials.

# Herokuのメールアドレスを入力
Email: <Herokuアカウントのメールアドレス>

# Herokuのパスワードを入力
Password (typing will be hidden): <Herokuアカウントのパスワード>
Authentication successful.

 
公開鍵の確認をします。

$ heroku keys

 
Heroku用の公開鍵が登録されていない場合、以下のHeroku向けの公開鍵設定を行います。

設定を行わない場合、「Permission denied (publickey)」などでHerokuへのpushが失敗します。

   

Heroku向けの公開鍵設定

既存の公開鍵を使いまわしても良いですが、今回はHeroku専用の公開鍵(鍵名:heroku_rsa)を用意することにしました。

 

公開鍵の生成

<%USERPROFILE%>部分は自分の環境に合わせます。

$ ssh-keygen -t rsa
Generating public/private rsa key pair.  

# 公開鍵のフルパスを入力
Enter file in which to save the key (<%USERPROFILE%>/.ssh/id_rsa): <%USERPROFILE%>/.ssh/heroku_rsa

# 必要に応じて鍵に対するパスワードを設定(空白も可)
Enter passphrase (empty for no passphrase):<任意のパスワード>

# パスワードを再入力
Enter same passphrase again:<同上>  
Your identification has been saved in <%USERPROFILE%>/.ssh/heroku_rsa.  
Your public key has been saved in <%USERPROFILE%>/.ssh/heroku_rsa.pub.  
The key fingerprint is:  
<fingerprint> <user@pc>

 

configファイルの作成

今回はHeroku専用の公開鍵を用意したため、configファイルを作成・設定を行います。

以下の内容で、 %USERPROFILE%\.ssh\config ファイルを作成します。

Host heroku.com
 User git
 port 22
 Hostname heroku.com
 IdentityFile ~/.ssh/heroku_rsa
 TCPKeepAlive yes
 IdentitiesOnly yes

 

Herokuへ公開鍵を登録
$ heroku keys:add
Could not find an existing public key at ~/.ssh/id_rsa.pub

# Heroku専用の公開鍵は生成済なので、[n]  
Would you like to generate one? [Yn] n
Found an SSH public key at <%USERPROFILE%>/.ssh/heroku_rsa.pub  

# 今回生成したキーなので、[Y]
Would you like to upload it to Heroku? [Yn] Y
Uploading SSH public key <%USERPROFILE%>/.ssh/heroku_rsa.pub... done

 

Herokuへのデプロイ

引き続き、SourceTreeのコンソールを使って、herokuコマンドからデプロイできるかを試してみます。

 

Herokuアプリの作成

アプリ名は後から変更できるため、とりあえず作成します。

$ heroku create

 

buildpackの設定を追加

今回使用するbuildpackを追加します。

$ heroku config:add BUILDPACK_URL=https://github.com/friism/heroku-buildpack-mono

 

デプロイ
$ git push heroku master
Fetching repository, done.  
...  
-----> Compressing... done, 63.6MB  
-----> Launching... done, v8  
       http://<アプリ名>.herokuapp.com/ deployed to Heroku

無事にデプロイが終わりました。

 

動作確認

http://<アプリ名>.herokuapp.com/http://<アプリ名>.herokuapp.com/json へとブラウザでアクセスし、

 
が表示されるかを確認します。

 

Heroku Buttonの作成

以下を参考に、Heroku Button用のapp.jsonファイルをソリューションファイルと同じフォルダに作成します。
Deploy to Heroku / Webアプリケーションのポータビリティ再び - naoyaのはてなダイアリー

app.json
{
  "name": "NancyOwinHeroku-sample",
  "env": {
    "BUILDPACK_URL": "https://github.com/friism/heroku-buildpack-mono"
  }
}

 
作成が終わったら、コミットしてGitHubへpushしておきます。

 
次に、以下のようなHeroku Buttonを用意します。

そして、Herokuにログインした状態でHeroku Buttonをクリックすると、「NancyOwinHeroku-sample」のデプロイが始まるため、動作確認を行います。

 

NancyOwinHeroku-sampleアプリ用のHerokuButton

Deploy

 

HerokuButtonのコード

templateに自分のGitHubリポジトリを指定します。

<p><a href="https://heroku.com/deploy?template=https://github.com/thinkAmi-sandbox/NancyOwinHeroku-sample"> <img alt="Deploy" src="https://www.herokucdn.com/deploy/button.png"></a></p>

 

ソースコード

GitHubへとアップしておきました。
thinkAmi-sandbox/NancyOwinHeroku-sample

 

参考

OWINまわり

 

Nancyまわり