先日、C# + NancyのWebアプリを作ってみて、Nancyは扱いやすく書きやすいと感じました。ただ、自分にとって必要なところだけを実装しており、Nancy風に書けているのか分かりませんでした。
そこで、Nancyの機能をひと通り試せるような書籍がないかを探してみたところ、「Instant Nancy Web Development | Packt」(Christian Horsdal著、Packt Publishing刊、2013年9月発行)がありました。
自分はPackt Publishingのサイトより電子書籍版(pdf, ePub, mobi形式)を買いました。電子書籍を出版社から直接Kindleへと飛ばせるなど、なかなか便利でした。
- 作者: Christian Horsdal
- 出版社/メーカー: Packt Publishing
- 発売日: 2013/09/25
- メディア: ペーパーバック
- この商品を含むブログを見る
感想など
本書は全部で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」の章はベータ版であり今後変わるかもしれないと書かれていましたが、自分の写経した感じではあまり大きな変化はなさそうでした。
- Building and running your first Nancy application (Simple)
- Nancy testing - your first Nancy tests (Intermediate)
- Routes and model binding (Intermediate)
- Taking a dependency - introducting the bootstrapper (Intermediate)
- Content negotiation and more model binding(Advanced)
- Adding views (Intermediate)
- Adding static content (Intermediate)
- Hosting Nancy on the Cloud (Intermediate)
- Handling cross-cutting concerns - Before, After, and Error hooks (Intermediate)
- Authenticating users (Intermediate)
- Separating applications and hosting (Advanced)
- 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
としているが、27017
のtypo- MongoDBの起動ポートは
27017
であるため
- MongoDBの起動ポートは
Content negotiation and more model binding
- 位置No.595の6.以降、テスト自体でSystem.InvaridOperationExceptionが発生
- 書籍とサンプルコードでコードの意味自体が変わっていたため、Exceptionを無視して進めた
- 書籍は
application/xml
だが、サンプルコードはapplication/json
- 書籍は
- 書籍とサンプルコードでコードの意味自体が変わっていたため、Exceptionを無視して進めた
- 位置No.657の
CanDeserialize
メソッドがインタフェースIBodyDeserializer
の要件を満たさない- メソッドの引数に
BindingContext
を追加
- メソッドの引数に
- 位置No.657の
MediaRange.FromString("application/x-protobuf")
が古い形式- VisualStudioでコンストラクタを使えと表示が出たため、
new MediaRange("application/x-protobuf")
へと変更
- VisualStudioでコンストラクタを使えと表示が出たため、
Adding views
- 位置No.695の
actual.Body["title"].ShouldContain("Todos");
にて古い形式であるとの警告actual.Body["title"].AllShouldContain("Todos");
に置き換え
- Razor ViewEngineを使う時の設定が不足
IRazorConfiguration
を実装したクラスを用意しておけば、自動ロードされるとのこと- TodoNancyプロジェクトに、
RazorConfig
クラスを用意
- Bootstrapperで
private Nancy.ViewEngines.Razor.RazorViewEngine ensureRazorIsLoaded;
を用意してるが、現在のバージョンでは未使用なので削除 - GETのレスポンスで
.WithModel(todoStore.GetAll())
だと、コンパイルは通るがテストコードの実行時に以下のエラー発生- mongocsharpdriverが変更になり
IEnumerable
を返せていないようなので、.WithModel(todoStore.GetAll().ToArray())
へと変更- この修正を行うことで、今までのテストで4個落ちていたものが、1個落ちるだけになった
- mongocsharpdriverが変更になり
Nancy.RequestExecutionException: Oh noes! ---> System.InvalidCastException: 型 'MongoDB.Driver.MongoCursor`1[ToDoNancy.Todo]' のオブジェクトを型 'ToDoNancy.Todo[]' にキャストできません。
Adding static content
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パッケージからは削除済- NuGet Gallery | WorldDomination: Nancy Web Authentication 0.19.2
- WorldDomination自体もNuGetの更新が停止しているのと、プロジェクトページが削除されている
Nancy.SimpleAuthentication
で置き換えたとのこと
- 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でプロジェクトの新規作成とファイルの移動をしていることについて
- 位置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がとても参考になりました。
- Windows x64版 (現時点では、2.6.7)
- インストール先:
C:\mongodb
- ポートをオープン
- DB用のディレクトリを
C:\mongodb\data\db
として作成 - 起動:
C:\mongodb\bin\mongod.exe --dbpath C:\mongodb\data\db
- 停止:
Ctrl + C
クラウドへのデプロイについて
書籍では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のためできず - 念のため、ユーザーを新規作成して、パスワード設定
- 接続文字列が表示されるので覚えておく
- 本来ならAzureのMARKETPLACEで
- スタートアッププロジェクトになるのを防ぐため、念のため、ソリューションから
HelloNancy
を削除 - Azureで、Websitesの構成 > アプリケーション設定に、MongoLabへ接続する文字列をセット
- キーは
MONGOLAB_URI
、値はMongoLabで表示されたもの
- キーは
- pushして、動作を確認