しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Sinatra ライクな .NET 用軽量 Web フレームワーク「Nancy」を使ってみた

f:id:shiba-yan:20130509212100p:plain

Nancy - Lightweight Web Framework for .net
Custodians of the Super-Duper-Happy-Path · GitHub

恥ずかしながら、今まで Nancy というものを知りませんでした。知ったきっかけは Scott Hanselman 氏のブログ記事です。

One ASP.NET: Nancy.Templates for Visual Studio - Scott Hanselman

Web Forms だけだった ASP.NET の頃とは大きく変わって、いろんなフレームワークが公開されるのは非常に良いことですね。サンプルコードを見ていると、結構おもしろそうだったので使ってみました。

実際に Nancy を使った ASP.NET アプリケーションを開発する場合には、空の ASP.NET アプリケーションを作成して NuGet から以下のパッケージをインストールするだけです。*1

Install-Package Nancy.Hosting.Aspnet

これで開発の準備が整ったので、サンプルコードを見ながら勉強していきましょう。

サンプルコードを見る限りでは、現在主流となりつつある ASP.NET MVC の開発スタイルとは大きく異なっていますね。このスタイルは Ruby の Sinatra というフレームワークに影響を受けたものらしいです。*2

public class SampleModule : Nancy.NancyModule
{
    public SampleModule()
    {
        Get["/"] = _ => "Hello World!";
    }
}

NancyModule というクラスは MVC で言うところの Controller クラスに役割は似ていますが、ルーティングの定義を Get/Post/Put/Delete というプロパティで組み立てていくところが大きく異なっています。

この時点で実行してみると、ちゃんとメッセージが表示されます。

f:id:shiba-yan:20130509214925p:plain

余談になりますが、Node.js でよく使われる Express というフレームワークを使った時のコードに似ていると思っていたんですが、Express 自体が Sinatra をインスパイアして開発されたフレームワークだったのですね。

Sinatra inspired web development framework for node.js -- insanely fast, flexible, and simple

GitHub - expressjs/express: Fast, unopinionated, minimalist web framework for node.

話を戻して、先程のサンプルコードでは Get プロパティに対して "/" というキーを追加しているので、この場合はドメインルートに対して GET リクエストが投げられた場合のハンドラを記述していることになります。

つまり、POST リクエストを処理したい場合には以下のようになります。

public class SampleModule : Nancy.NancyModule
{
    public SampleModule()
    {
        Get["/"] = _ => "Hello World!";

        Post["/"] = _ => "Post されました";
    }
}

RESTful な API も Nancy だけで簡単に構築できそうです。というか、軽量なので API の実装に結構向いてる気がします。

しかし、このルーティングの定義だと階層構造を持ったルートを定義する場合に、以下のように記述が冗長になってしまいそうですね。

public class AdminModule : Nancy.NancyModule
{
    public AdminModule()
    {
        Get["/admin"] = _ => "/admin です";
        Get["/admin/login"] = _ => "/admin/login です";
        Get["/admin/logout"] = _ => "/admin/logout です";
    }
}

でも大丈夫。NancyModule のコンストラクタにはモジュールの基本となるパスを指定できるので、すっきりと記述することが可能です。

public class AdminModule : Nancy.NancyModule
{
    public AdminModule()
        : base("/admin")
    {
        Get["/"] = _ => "/admin です";
        Get["/login"] = _ => "/admin/login です";
        Get["/logout"] = _ => "/admin/logout です";
    }
}

ASP.NET MVC の Areas っぽいですね。

そして Web アプリでは重要なクエリパラメータやフォームデータですが、NancyModule に Form と Query プロパティが用意されているので、これを使って取得します。この二つのプロパティは dynamic 型となっているので、実際に取得する場合は以下のような感じです。

public class SampleModule : Nancy.NancyModule
{
    public SampleModule()
    {
        Get["/"] = _ => "message: " + Request.Query.message;

        Post["/"] = _ => "message: " + Request.Form.message;
    }
}

見た目スッキリと書けるので Web Pages にも欲しいところですね。

そして Nancy でもルーティング定義には MVC の時と同様にパラメータを指定可能です。記法は MVC とほぼ同じです。

public class ProductModule : Nancy.NancyModule
{
    public ProductModule()
    {
        Get["/product/{id}"] = parameters => "id: " + parameters.id;
    }
}

ラムダ式の引数はルーティングで定義したパラメータが入ってくる仕組みです。これも dynamic 型なのですっきりと書けていますね。

最後に文字列だけではなくファイルとして用意されたビューを返してみます。例えば、ASP.NET MVC のようにビューを返したい場合には以下のように書きます。

public class SampleModule : Nancy.NancyModule
{
    public SampleModule()
    {
        // Index ビューを返す
        Get["/"] = _ => View["Index"];
    }
}

この View プロパティはインデクサを実装していて、3 つのオーバーロードが定義されています。引数は MVC の View メソッドとあまり変わりません。

実はこのままだとビューを返すことは出来ません。Nancy はデフォルトでビューエンジンを持っていないので、使いたいビューエンジンを NuGet からインストールする必要があります。

現在公開されている Nancy 用のビューエンジンは以下の通りです。

  • Nancy.Viewengines.Razor
  • Nancy.Viewengines.Spark
  • Nancy.Viewengines.DotLiquid
  • Nancy.Viewengines.NDjango
  • Nancy.Viewengines.Nustache
  • Nancy.ViewEngines.NHaml
  • Nancy.Viewengines.Parrot
  • Nancy.Viewengines.Markdown

これは NuGet のパッケージ ID となっているので、使いたいものをインストールしてください。今回は面白そうだったので Markdown を選択してみました。

Install-Package Nancy.Viewengines.Markdown

インストールが終わったらビューを作成します。Nancy は ASP.NET MVC のように専用のディレクトリを用意することなく、プロジェクトルートにべたでファイルを置いて問題ないようです。

作成した Markdown ビューは以下のような感じです。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>

# 見出し 1

## 見出し 2

サンプルテキストサンプルテキストサンプルテキスト

* リスト1
    * サブリスト1
* リスト2

*イタリック体イタリック体*
**太字太字太字**

[Nancy の公式サイト](http://nancyfx.org "Nancy")

</body>
</html>

これを実行すると、以下のようにちゃんと Markdown が展開された形で表示されます。

f:id:shiba-yan:20130509221234p:plain

今回はビューにデータを渡しませんでしたが、View プロパティで何らかのオブジェクトを渡した場合にはビューでは Razor っぽく利用できます。

# 見出し @Model.Name

他にも部分ビューやループなどもあるんですが、長くなったので今回はこれだけにしておきます。

何だか ASP.NET MVC をやっている人間からすると、たったこれだけのコードで動くのが不思議な感じです。

しかし Web Forms に比べると軽量な MVC ですが、実際のところはかなり巨大なフレームワークなので、Nancy のような簡単なルーティングが行えて自由度の高い軽量フレームワークは需要がありそうですね。

プラガブルで拡張性が高く、様々なプラグインが公開されそうなので、これからの開発が楽しみなフレームワークです。

追記

ホモで有名なだるだる先生が WebMatrix 3 を使って Nancy を試したようです。

オプションパラメータとか正規表現でのマッチングもできるのは便利そうですね。

*1:SelfHost や Owin 向けのライブラリも用意されている

*2:Web サイトのレイアウトも似ているような気がする