「Instant Nancy Web Development」を写経してみた

先日、C# + NancyのWebアプリを作ってみて、Nancyは扱いやすく書きやすいと感じました。ただ、自分にとって必要なところだけを実装しており、Nancy風に書けているのか分かりませんでした。

そこで、Nancyの機能をひと通り試せるような書籍がないかを探してみたところ、「Instant Nancy Web Development | Packt」(Christian Horsdal著、Packt Publishing刊、2013年9月発行)がありました。

自分はPackt Publishingのサイトより電子書籍版(pdf, ePub, mobi形式)を買いました。電子書籍を出版社から直接Kindleへと飛ばせるなど、なかなか便利でした。

Amazonだと紙版・Kindle版の両方がありました。

Instant Nancy Web Development

Instant Nancy Web Development

 

感想など

本書は全部で74ページながら、

  • TodoNancyというNancyアプリをテップバイステップ形式で作成する
  • xUnit.net + Nancy.Testing+ FakeItEasyを使ったテストファーストで進める
  • ViewやModelの使い方、認証やリクエストなどのフック、クラウドへのデプロイなどをNancyでどう実装するのかを学べる
  • ViewEngineはRazor、Nancyの主なホスティングとしてASP.NET(IIS)を使用する
  • いろいろなNuGetパッケージやデータストアとしてMongoDBを使用する

という充実した内容で書かれています。そのため、Nancyアプリを初めて作るけど何から手を付けたら良いのかわからないのであれば、この一冊から初めてみるのが良いと感じました。

洋書であることから英語で書かれていますが、英文が平易なのとソースコードを読むのがメインなので、だいたい理解できました。

自分の場合だと、11章の「ASP.NET HostingとSelfHostingを1つのソリューションで実装する」というのがちょうど気になっていた部分だったので、とてもためになりました。また、自作のアプリはSSVEとOWIN SelfHostingを使っていたため、内容が重複せずに学べたことも大きかったです。

 

目次

出版社のサイトなどで目次が見つからなかったので、残しておきます。「Using async handlers」の章はベータ版であり今後変わるかもしれないと書かれていましたが、自分の写経した感じではあまり大きな変化はなさそうでした。

  1. Building and running your first Nancy application (Simple)
  2. Nancy testing - your first Nancy tests (Intermediate)
  3. Routes and model binding (Intermediate)
  4. Taking a dependency - introducting the bootstrapper (Intermediate)
  5. Content negotiation and more model binding(Advanced)
  6. Adding views (Intermediate)
  7. Adding static content (Intermediate)
  8. Hosting Nancy on the Cloud (Intermediate)
  9. Handling cross-cutting concerns - Before, After, and Error hooks (Intermediate)
  10. Authenticating users (Intermediate)
  11. Separating applications and hosting (Advanced)
  12. Using async handlers (Advanced)

 

写経環境

書籍のNancyのバージョンは0.17.1でした。

書籍が発行されてから一年少々経過していたこともあり、写経当初のバージョンは0.23.0、途中から1.00で写経しました。それでも、写経を進める上で問題になったり詰まったりすることはなく、ひと通り流すことができました。

写経時の主なNuGetパッケージとバージョンは以下の通りです。

パッケージ名 バージョン
Nancy 1.0.0
Nancy.Hosting.Aspnet 1.0.0
Nancy.Hosting.Self 1.0.0
Nancy.Viewengines.Razor 1.0.0
Nancy.SimpleAuthentication 0.3.14
mongocsharpdriver 1.9.2
NLog 3.2.0.0
protobuf-net 2.0.0.668
SimpleAuthentication.Core 0.3.14
Nancy.Testing 1.0.0
FakeItEasy 1.25.1
xunit 1.9.2
xunit.extensions 1.9.2

 
ロギングのNuGetパッケージとしては、Nancy.Elmahも紹介されていました。

 

写経の方法

今回はKindle Paperwhite + 本立を使って写経をしてみました。初めてKindle Whitepaperを使いましたが、特に見づらいということはありませんでした。

また、以前の方法に加え、以下の記事で見たGitHubのプルリクエストを使用した方法で写経を行いました。ブログ記事にもある通りいろいろとコメントが残せるので、あとからの振り返りやブログへのまとめに便利でした。
「パーフェクト Ruby on Rails」をパーフェクトに写経した & 僕の写経のやり方について - えいのうにっき

 
結果はこちら
thinkAmi-sandbox/syakyo_Instant_Nancy_Web_Development · GitHub

 

写経をする上で悩んだことなど

自分が試した時に悩んだものをまとめておきます。もしかしたら勘違いな内容があるかもしれませんが...。

 

書籍内のコードとサンプルコード、現在のバージョン間で異なる部分

基本的にはサンプルコードを読む形となります。書籍よりもサンプルコードの方がテストコードが充実していたりします。

Taking a dependency - introducting the bootstrapper
  • 位置No.477のAssertCalledTryAddOnDataStoreWithメソッド
    • サンプルコードの方が正しく、Assertionsクラスを追加する必要あり
  • 位置No.504のテストコードがサンプルコードに残っていない
    • サンプルコードは最終形のみ残されているため
  • MongoDataStoreクラスで使っているMongoDatabase.Createが古い
  • 位置No.537にて、var mongoDataStoreでポート27010としているが、27017typo
    • MongoDBの起動ポートは27017であるため

 

Content negotiation and more model binding
  • 位置No.595の6.以降、テスト自体でSystem.InvaridOperationExceptionが発生
    • 書籍とサンプルコードでコードの意味自体が変わっていたため、Exceptionを無視して進めた
      • 書籍はapplication/xmlだが、サンプルコードはapplication/json
  • 位置No.657のCanDeserializeメソッドがインタフェースIBodyDeserializerの要件を満たさない
  • 位置No.657のMediaRange.FromString("application/x-protobuf")が古い形式
    • VisualStudioでコンストラクタを使えと表示が出たため、new MediaRange("application/x-protobuf") へと変更

 

Adding views
  • 位置No.695のactual.Body["title"].ShouldContain("Todos");にて古い形式であるとの警告
    • actual.Body["title"].AllShouldContain("Todos"); に置き換え
  • Razor ViewEngineを使う時の設定が不足
  • Bootstrapperでprivate Nancy.ViewEngines.Razor.RazorViewEngine ensureRazorIsLoaded;を用意してるが、現在のバージョンでは未使用なので削除
  • GETのレスポンスで .WithModel(todoStore.GetAll()) だと、コンパイルは通るがテストコードの実行時に以下のエラー発生
    • mongocsharpdriverが変更になりIEnumerableを返せていないようなので、.WithModel(todoStore.GetAll().ToArray())へと変更
      • この修正を行うことで、今までのテストで4個落ちていたものが、1個落ちるだけになった
Nancy.RequestExecutionException: Oh noes! ---> System.InvalidCastException: 型 'MongoDB.Driver.MongoCursor`1[ToDoNancy.Todo]' のオブジェクトを型 'ToDoNancy.Todo[]' にキャストできません。

 

Adding static content
  • overview.htmというファイルの拡張子は、overview.htmlへと変更
    • VisualStudioからHTMLページを追加する時は、拡張子.htmlとなるため

 

Handling cross-cutting concerns - Before, After, and Error hooks
  • 位置No898のテストコードShouldLogErrorOnFailingRequestで日本語環境だとエラー内容が異なりテストコードが通らないため、環境に合わせて修正
    • 書籍: Input string was not in a correct format
    • 日本語環境: 入力文字列の形式が正しくありません。

 

Authenticating users
  • 書籍ではNancy.Authentication.WorldDominationを使っているが、NuGetパッケージからは削除済
  • No.992で、Response.AddCookieを使っているが古い形式
    • 代わりに Response.WithCookieを使う
  • No.985でTestingModuleがいきなり出てきて迷った
    • サンプルコードのAuthenticationTests.csの一番最後にTestingModuleクラスがあった
    • 分かりにくかったので、TestingAuthenticationModule.csとして別ファイルのクラスに切り出した
  • No.1008にもある通り、TokenServiceクラスはセキュアではなくプロダクションでは使わないこと
  • No.1041で、 new Todo { ... userName = ... }とあるが、ここまでにTodoクラスのプロパティuserNameを追加する記載なし
    • 追加しないとコンパイルが通らないので、プロパティuserNameを追加しておく
  • No.1050で、MongoDatastoreの変更に言及してるが、本文では変更後のコードが掲載されていない
    • サンプルコードを実装しないとコンパイルエラーになる

 

Separating applications and hosting
  • 位置No.1111でプロジェクトの新規作成とファイルの移動をしていることについて
    • 移動するとcshtmlなどの静的ファイルのプロパティも変更されるため、忘れずに「出力ディレクトリに常にコピー」へと変更
    • ToDoNancyTestsのプロジェクト参照についての記載がないため、コンパイルできない
      • ToDoNancyAspNetの参照をやめて、ToDoNancyの参照を追加する
    • 新規作成のToDoNancyコンソールアプリではSystem.Configuration.dllへの参照が不足
  • 位置No.1119で TodosModule artificiaReference を宣言しているが、使用していないので、削除
  • 位置No.1128で、app.configの変更をToDoNancyと書いてあるが、正しくはToDoNancySelfHostプロジェクトになる
    • サンプルコードは正しい
  • そのままのコードだと、ToDoNancySelfHostは動作するが、ToDoNancyAspNetはエラーになる
    • Unable to resolve type: Nancy.SimpleAuthentication.IAuthenticationCallbackProvider
    • Nancy.SimpleAuthenticationまわりの例外なので、今回は先に進んだ
  • SelfHostは動作するが、AspNetHostは以下の例外が出て動作しない
    • 写経時点では両方とも動作する環境を作れなかったものの、宿題として先に進んだ
Nancy.RequestExecutionException: Oh noes! ---> Nancy.ViewEngines.ViewNotFoundException: Unable to locate view 'Todos'
Currently available view engine extensions: sshtml,html,htm,cshtml,vbhtml
Locations inspected: views/todos/Todo/Todos-ja,views/todos/Todo/Todos,todos/Todo/Todos-ja,todos/Todo/Todos,views/todos/Todos-ja,views/todos/Todos,todos/Todos-ja,todos/Todos,views/Todo/Todos-ja,views/Todo/Todos,Todo/Todos-ja,Todo/Todos,views/Todos-ja,views/Todos,Todos-ja,Todos
Root path: path\to\project\ToDoNancyAspNet\
If you were expecting raw data back, make sure you set the 'Accept'-header of the request to correct format, for example 'application/json'

 

テストランナーのxUnit.net Visual Studio Runnerについて

書籍ではVisualStudioの拡張でしたが、以下にもある通り、現在ではNuGetパッケージでインストールする形となります。
Running xUnit.net tests in Visual Studio > xUnit.net

写経時のバージョンは2.0.0-rc1-build1030とプレリリース版だったため、NuGetコンソールでのインストール方法は以下の通りです。

Install-Package xunit.runner.visualstudio -Pre

また、テストウィンドウは、テスト > ウィンドウ > テストエクスプローラー で開きます。

 

名前空間について

書籍ではusingディレクティブが省略されているため、そのまま写経するとコンパイルが通らないため、サンプルコードを読む必要があります。

主に悩んだところは以下のところです。

あとで読み返すときのために、usingディレクティブを使っていても初めての出現時は名前空間を省略しないようにして写経を進めました。

 

MongoDBのインストール・起動・停止について

[MogoDB]MongoDBをWindows環境にインストールする - Netplanetesがとても参考になりました。

 

クラウドへのデプロイについて

書籍ではAppHarbor - .NET Cloud Platform as a Serviceへとデプロイしていましたが、自分はAzureのアカウントを持っていたため、Azure Websitesへとデプロイしました。

Azureのための設定
  • 開発マシンかのチェックは、書籍にある端末名からOS名へと変更
  • 接続文字列はWeb.Release.configなどのNancyアプリ側には保存せず、Azureのアプリケーション設定に接続文字列をセット
  • テストを通すために、テストプロジェクトのapp.configにMongoDBへの接続情報を記入
  • デプロイ時にエラー内容を確認するため、以下をBootstrapperに追加
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
    base.ApplicationStartup(container, pipelines);
    StaticConfiguration.DisableErrorTraces = false;
}

 

Azureへのデプロイ
  • AzureでWebsitesを作成
  • AzureでローカルのGitをリポジトリとして追加
  • ローカルのGitリポジトリにazureをremoteとして追加
  • MongoLabにてFreePlanで、データベース名をtodosとして作成
    • 本来ならAzureのMARKETPLACEでMongoLabを追加できるはずだが、Internal Server Errorのためできず
    • 念のため、ユーザーを新規作成して、パスワード設定
    • 接続文字列が表示されるので覚えておく
  • スタートアッププロジェクトになるのを防ぐため、念のため、ソリューションからHelloNancyを削除
  • Azureで、Websitesの構成 > アプリケーション設定に、MongoLabへ接続する文字列をセット
    • キーはMONGOLAB_URI、値はMongoLabで表示されたもの
  • pushして、動作を確認