NancyFxチュートリアル「3.1. モジュール(コントローラ)を作成する (ルーティング編)」

この記事は、Nancy Advent Calendar 2013の8日目の記事です。

書けない日の分を書き続けるのもアレですので、かけていない日の分を空けます(^^;

前回は、プロジェクトの細かい設定と、モデルの作成をしました。

今回からモジュール(コントローラ)を作成していこうと思います。モジュールについては少し細かく書いていこうと思います。

もくじ

  • ルーティングの構文
  • HTTPメソッド
  • パターン
  • 複数のパターンが重複した場合
  • アクション

ルーティングの構文

ドキュメントは以下です。

Defining routes · NancyFx/Nancy Wiki · GitHub

ルーティングについては、AC2日目の記事で動作確認をした際に、簡単にご紹介しました。

public class HomeModule : NancyModule
{
    public HomeModule()
    {
        // GET "/"
        Get["/"] = parameter => {
            return "Hello NancyFx !!";
        };
    }
}

このように、ルーティングをはモジュールクラスのコンストラクタで指定します。指定する構文は、HTTPメソッド[パターン] = Func<dynamic, dynamic>です。

それぞれの項目に指定できる値をみてみます。

HTTPメソッド

HTTPメソッドには、GET POST PUT DELETE と、OPTIONS HEAD PATCHが指定できます。HEAD以外はそれぞれの名前のインデクサーが定義されています。HEADについては、GETのインデクサーに定義したものが使われるようです。

パターン

パターンは、ルートからの相対URLを指定します。パターン構文はNancyFxである程度用意されており、それで十分こと足りると思いますが、独自に定義することも可能です。

また、パターンにはスコアによる優先順位があります。同じようなルーティングが定義されている場合は、カッコ内の数値が高い順に優先されます。

NancyFxで用意されているルーティングは以下のとおりです。

  • リテラル (10,000) - /some/literal/segument のように、URLに完全一致する場合に指定します。一番優先順位が高いです。

  • キャプチャー (1,000) - /{name} のように、変数にキャプチャーできるパターンです。/blog/{date}/comments/{id}のように、リテラルと組み合わせて使用します。

  • キャプチャー(Optional) (1,000) - /{name?}のように、キャプチャー名に?をつけることで、そのURLを省略可能にできます。

  • キャプチャー(Optional + デフォルト値) (1,000) - /{name?unnamed}のように、省略可能なキャプチャー変数にデフォルト値を設定することができます。あまり使い道が浮かばない・・・。

  • 正規表現 (1,000) - /(?<age>[\d]{1,2})のように、正規表現を利用して値をキャプチャーします。

  • 可変長キャプチャ (0) - /{name*}のように、キャプチャー変数名に*をつけることで、スラッシュ以降のセグメントを可変長引数のようにキャプチャーします。

  • 可変長正規表現キャプチャ (100) - ^(?<name>[a-z]{3,10}(?:/{1})(?<action>[a-z]{5,10}))$のように、スラッシュ以降のパスを全て正規表現でキャプチャーします。難しい・・・。

  • マルチキャプチャー (100) - /{file}.{extension}/{file}.xmlのように、キャプチャーとリテラルを組み合わせる方法です。jsonやxmlのように拡張子を指定したREST APIなどを実装する際に使えます。

複数のパターンが重複した場合

さきほど説明したとおり、複数のパターンが重複した場合はスコアによる順位付けがなされます。たとえば、以下のようなルーティングを定義したとします。

Get["/{category}"] = _ => {
    return "My category is " + _.category;
};

Get["/sayhello"] = _ => {
    return "Hello from Nancy";
};

/{category}/sayhelloのルーティングを定義しましたが、ブラウザから/sayhelloとアクセスした場合に重複してますね。このようにルーティングが重複した場合はスコアの大きいほうが優先されますので、スコア10,000の/sayhelloが適用されます。結果は実際にブラウザからアクセスして試してみてください。

アクション

ルーティングアクションには、リクエストが指定したルーティングに一致した場合に呼び出される動作を指定します。

引数にはdynamic型インスタンスが渡され、URLでキャプチャした値を取得することができます。

リクエストを処理するために必要な値などについては、引数以外にRequestContextなどのプロパティが定義されています。

レスポンスを返すためにはResponseというプロパティを使いレスポンスを生成するか、Viewプロパティを使いView経由で返す、あるいな独自のレスポンスを定義することもできます。

また、ViewBagを利用することもできます。利用方法はASP.NET MVCと同じです。

まとめ

今回は、コントローラと同じ働きをするモジュールについて、ルーティングを定義する方法を中心にまとめました。基本的なルーティングの定義については、ここでまとめた内容でまかなえると思います。

これ以外にも、条件付きルーティングや、非同期処理などの機能もあります。非同期処理についてはどこかでまとえるかもしれませんが、それ以外についてはドキュメントを参照してみてください。

次回は、NancyFxで定義されているレスポンスを作成する方法についてまとめ、実際の処理を記述していこうと思います。

NancyFxチュートリアル「2. プロジェクトの細かい設定をする(+ モデルを作成する)」

この記事は、Nancy Advent Calendar 2013の3日目の記事です。

前回は、プロジェクトを作成して簡単な動作確認をしました。

今回は、前回触れなかったプロジェクトの細かい設定と、ToDoアプリのモデルクラスを作っていこうと思います。

もくじ

  • 静的コンテンツを配置するディレクトリを追加する
  • すべてのViewのベースとなるViewを定義する
  • モデルを作成する

静的コンテンツを配置するディレクトリを追加する

NuGetを使いJavaScriptCSSのパッケージをインストールした際、それぞれ以下のディレクトリにインストールされます。

  • JavaScriptScriptsディレクトリ以下
  • CSSContentディレクトリ以下

これらのディレクトリに配置されますが、NancyFxではデフォルトでContentディレクトリ以下のファイルのみを静的コンテンツとして配信します。そのため、JavaScriptを配信するためには、Contentディレクトリ以下に移動するか、静的コンテンツを配信するディレクトリを追加する必要があります。

ここでは、静的コンテンツを配信するディレクトリを追加する方法について説明します。

Managing static content · NancyFx/Nancy Wiki · GitHub

NancyFxでは、フレームワークの設定を変更するためのクラスNancy.DefaultNancyBootstrapperを用意しています。このクラスを継承することで、アプリケーションの動作はもちろん、フレームワーク自身の動作もほぼ全てカスタマイズすることができます。このクラスに、静的コンテンツなどを登録できるメソッドvoid ConfigureConventions(NancyConventions)があるため、それをオーバーライドして登録します。

Bootstrapper · NancyFx/Nancy Wiki · GitHub

プロジェクトのルートに、クラスTodoApp.TodoAppBootstrapperを作成し、クラスNancy.DefaultNancyBootstrapperを継承して以下のとおり実装します。

using Nancy;
using Nancy.Conventions;

namespace TodoApp
{
    public class TodoAppBootstrapper : DefaultNancyBootstrapper
    {
        protected override void ConfigureConventions(NancyConventions nancyConventions)
        {
            base.ConfigureConventions(nancyConventions);

            // Scriptsディレクトリ以下のファイルを静的コンテンツとして扱うようにする。
            nancyConventions.StaticContentsConventions.Add(
                StaticContentConventionBuilder.AddDirectory("Scripts")    
            );
        }
    }
}

void ConfigureConventions(NancyConventions)をオーバーライドし、上記の通りScriptsディレクトリ以下のファイルを静的コンテンツとして配信するように設定しています。

この設定をすることで、Scriptsディレクトリ以下にあるJavaScriptファイルが配信されるようになりました。jQueryなどのJavaScriptファイルを参照して動作確認してみてください。

Bootstrapperクラスではこれ以外にも、アプリケーション起動時の処理を記述するメソッドvoid ApplicationStartup(TContainer, IPipelines)や、リクエスト開始時の処理を記述するメソッドvoid RequestStartup(TContainer, IPipelines, NancyContext)などがオーバーライドできます。

これらのメソッドや引数はユーザ認証の設定をする際などに登場しますので、ここでは説明を省略します。

すべてのViewのベースとなるViewを定義する

ASP.NET MVCでは、適用するレイアウトを定義する際に_ViewStart.cshtml_ViewStart.vbhtmlといったファイルを作成して指定します。この指定方法はNancyFxでも利用することができますが、少し注意が必要です。

レイアウトを指定するため、Views/_ViewStart.cshtmlファイルとViews/Shared/_Layout.cshtmlファイルを作成します。途中のSharedディレクトリも一緒に作成してください。

Views/Shared/_Layout.cshtmlには、他のテンプレートのベースとなるレイアウトを指定することができます。この詳細については、ASP.NET MVC 3の記事ですが以下のページが参考になります。

第1回 Controller−View開発のキモを押さえる − @IT

上の記事でも、レイアウトはViews/_ViewStart.cshtmlファイルで指定するとありますが、NancyFxでは指定の方法が若干異なります。

ASP.NET MVCでは、Layoutを~/Views/...のように指定しますが、NancyFxではその部分を省略して、Views以下のディレクトリから指定します。

@{
    // ASP.NET MVC は "~/Views/Shared/_Layout.cshtml" と指定する。
    Layout = "Shared/_Layout.cshtml";
}

この件については、ドキュメントを見つけられませんでした。

モデルを作成する

モデルについては、NancyFxでのサポートがありませんので、自由に選択することができます。今回はEntityFrameworkを使ってモデルファースト開発をしてみようと思います。

EntityFrameworkは、@shibayanさんのASP.NET MVC 3 開発入門を参考にしました。

ASP.NET MVC 3 開発入門 (3) - モデルをコードファーストで作成 - しばやん雑記

まずはNuGetで、EntityFramework 6 と、データベースにSQLServer CEを使うためのパッケージをインストールします。

NuGetのダイアログを開き、EntityFrameworkで検索をして以下のパッケージをインストールします。どちらも2013年12月3日時点ではバージョン6.0.1でした。

  • EntityFramework
  • EntityFramework.SqlServerCompact

f:id:rabitarochan:20131204012238p:plain

インストール後、モデルの作成に入っていきます。

Modelsディレクトリを作成し、モデルとなるクラスTodoApp.Models.TaskTodoApp.Models.Commentを追加します。クラスの定義は以下のとおりです。命名規則などは参考にした@shibayanさんの記事を参考にしてください。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace TodoApp.Models
{
    public class Task
    {
        public int TaskId { get; set; }

        [Required]
        [StringLength(128)]
        [DisplayName("タイトル")]
        public string Title { get; set; }

        [StringLength(1024)]
        [DisplayName("詳細")]
        public string Detail { get; set; }

        [DisplayName("期限日")]
        public DateTime DueDate { get; set; }

        public virtual ICollection<Comment> Comments { get; set; } 
    }
}
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace TodoApp.Models
{
    public class Comment
    {
        public int CommentId { get; set; }

        [Required]
        [DisplayName("コメント")]
        public string Message { get; set; }

        public DateTime CreatedAt { get; set; }
    }
}

モデルクラスができたので、それらを操作するためのデータコンテキストクラスTodoApp.Models.TodoAppContextと、Web.configファイルに接続文字列の設定をします。

using System.Data.Entity;

namespace TodoApp.Models
{
    public class TodoAppContext : DbContext
    {
        public DbSet<Task> Tasks { get; set; }
        public DbSet<Comment> Comments { get; set; } 
    }
}
<!-- Web.config の一部 -->
<connectionStrings>
  <add name="TodoAppContext"
       connectionString="Data Source=|DataDirectory|\TodoApp.sdf"
       providerName="System.Data.SqlServerCe.4.0" />
</connectionStrings>

さいごに、データベースの自動生成をするために、TodoAppBootstrapperクラスを修正します。

アプリケーション起動時の処理を記述するメソッドvoid ApplicationStartup(TContainer, IPipelines)をオーバーライドし、以下のように実装します。

// TodoAppBootstrapper の一部
protected override void ApplicationStartup(Nancy.TinyIoc.TinyIoCContainer container, Nancy.Bootstrapper.IPipelines pipelines)
{
    base.ApplicationStartup(container, pipelines);

    // EntityFramework のコードファースト設定。モデルに変更がある場合にDBを作り直す。
    Database.SetInitializer(new DropCreateDatabaseIfModelChanges<TodoAppContext>());
}

これでモデルを利用するための準備が整いました。これらのクラスを利用する際は、テストなどを考慮してリポジトリパターンを利用してアクセスすることになります。リポジトリパターンについては、これも@shibayanさんのブログが参考になります。

ASP.NET MVC 3 開発入門 (4) - リポジトリパターンを適用する - しばやん雑記

まとめ

今回は、プロジェクトの細かい設定と、モデルクラスを作成しました。

プロジェクトの設定の中でも特に_ViewStart.cshtmlについては、ドキュメントや情報が少なく、間違った指定をした際のエラーがよく分からない(NullReferenceException)こともあり、つまづきやすいポイントとなっています。

次回からは、コントローラの実装に入っていきます。

NancyFxチュートリアル「1. プロジェクトを作成する」

この記事は、Nancy Advent Calendar 2013の2日目の記事です。

(さっそく遅れてます・・・)

今日から何日かに渡って、NancyFxのチュートリアルということで簡単なWebアプリケーションを作ってみようと思います。

完成イメージ

チュートリアルっぽく、Todoアプリを作ってみようと思います。

  • タスクには、タイトルと詳細、期限日が登録できる。
  • タスクには、コメントがつけられる。
  • タスクは、期限日の昇順、登録順でソートされる。

f:id:rabitarochan:20131203175832p:plain

チュートリアルの環境

チュートリアルは、以下の環境で進めていきます。

それ以外の環境でも問題なく動くと思いますが、動かない場合はコメントかTwitterで連絡ください。

プロジェクトを作成する

さっそくプロジェクトを作成します。

新しいプロジェクトのテンプレートから「Visual C#」→「Web」→「ASP.NET 空の Web アプリケーション」を選択します。

プロジェクトの名前を入力し(今回は TodoApp)、OKボタンをクリック。何もファイルが生成されていないASP.NETプロジェクトが作成されます。

f:id:rabitarochan:20131203140350p:plain

NuGetで必要なパッケージをダウンロードする

NuGetを使い、今回必要となるパッケージをダウンロードします。NuGetについては@ITの記事などを参考にしてください。

VisualStudioのメニューにある「ツール」→「ライブラリ パッケージ マネージャー」→「ソリューションの NuGet パッケージの管理」をクリックします。

オンラインのパッケージソースを選択して「Nancy」で検索します。検索結果の中から以下3つのパッケージをインストールします。

  • Nancy
    NancyFxのコアライブラリです。

  • Nancy.Hosting.Aspnet
    NancyFxのWebアプリケーションをASP.NET上で動作させるためのライブラリです。ホスティングライブラリはこれ以外にも、AzureやWCF、SelfHosting(コンソールアプリなどと一緒に動作させる場合)などがあります。

  • Nancy.Viewengines.Razor
    NancyFxのビューエンジンに「Razor」を利用するためのライブラリです。NancyFxでは、標準でSuper Simple View Engineという、単純なテンプレートのみを実装したビューエンジンが利用できます。
    The Super Simple View Engine · NancyFx/Nancy Wiki · GitHub
    今回は使い慣れたRazorを利用するために、このライブラリを追加します。

f:id:rabitarochan:20131203141726p:plain

今回はこれ以外に、以下のパッケージをインストールします。NancyFx以外の部分は極力ふれない予定ですので、ここは任意ということで。

最初のモジュールを作成する

NancyFxの導入ができたため、簡単な動作確認をします。文字列を返すモジュール(NancyFxでは、コントローラではなくモジュールと呼ぶようです。意味はほぼ同じです。)を作成します。

Modulesディレクトリを作成し、その下にHomeModuleクラスを作成します。

作成後、クラスを以下の通りに実装します。

using Nancy;

namespace TodoApp.Modules
{
    public class HomeModule : NancyModule
    {
        public HomeModule()
        {
            // GET "/"
            Get["/"] = parameter => {
                return "Hello NancyFx !!";
            };
        }
    }
}

デバッグ実行しブラウザでアクセスすると、以下のような画面が表示されます。ちゃんと動作していることが確認できましたね!!

f:id:rabitarochan:20131203142746p:plain

最初のViewを作成する

ついでに、Viewも作ってしまいましょう。

Views/Homeディレクトリを作成し、その下にindex.cshtmlファイルを作成します。

作成後、cshtmlファイルの中身を編集します。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>NancyFxチュートリアル</title>
  <body>
    <h1>Hello NancyFx !!</h1>
  </body>
</html>

HomeModuleが、文字列を返す実装になっているので、Viewを返すように変更します。

Get["/"] = parameter => {
    return View["index"];
};

上記のように、View["ビュー名"]と実装すると、ビューエンジンを介したレスポンスを返すようになります。

ビューのテンプレートは、Views/モジュール名から"Module"を抜いた名前/指定した名前.拡張子のパスにあるテンプレートを利用します。今回の例ではViews/Home/index.cshtmlです。それ以外にも検索されるパスはありますが、後日別の記事としてまとめます。

修正後、再度ブラウザからアクセスすると、ちゃんとHTMLが表示されることが確認できます!!

f:id:rabitarochan:20131203144953p:plain

まとめ

今回は、プロジェクトの作成から、最低限必要なパッケージの導入と、簡単な動作確認をしました。

ソースも含めて1から作成しましたが、そこまで難しくなかったと思います。今回のような感じでゆっくりと進めていこうと思います。

ソースコードについては整理してからGithubで公開します。しばしお待ちください。

NancyFxとは

この記事は、Nancy Advent Calendar 2013の1日目の記事です。

(寝落ちしてしまったので2日になってしまいましたが・・)

NancyFxの勉強をかねて、(ひとり)NancyFxアドベントカレンダーを作成しました。

NancyFxを使うひとが一人でも増えてくれたら嬉しいです。ともだちぼしゅうちゅう。

NancyFxとは

NancyFxとは、.NET Framework / Mono上で動作するように作られた軽量Webアプリケーションフレームワークで、RubySinatraのようなDSLを提供しています。 フルスタックなフレームワークではないので、DBアクセスなどは好きなライブラリが利用できます。

ちなみに、Nancyとは、フランク・シナトラの娘の名前だそうですw

NancyFxを知ったきっかけは、C#を使ってWebアプリを作ろうと思い、軽量(Sinatraライク)なフレームワークを求めてググったらひっかかりました。

軽量なフレームワークといいつつ、普通のWebページやREST APIなどもちゃんと実装できるよう、機能も豊富に用意されています。

どんなフレームワーク?

HTTPのGETやPOST、ルーティングを指定して、値などのレスポンスを返すだけという、ラムダ式で簡単に記述ができます。C#はライトウェイトなんだ!!

public class HomeModule : NancyModule
{
    public HomeModule()
    {

        /* GET / */
        Get["/"] = parameter => {
            return "Hello NancyFx!!";
        };

    }
}

それ以外にも、数値を返すとHTTPステータスコードとして返してくれるなど、レスポンスを返す方法にもいろいろあります。

公式サイトやリソースなど

ソースはGithubにホストされています。毎週10以上のコミットがあり、活発に開発されています。
書いてから気づきましたが、11/24の週はコミット数が2ですね・・・。

おわりに

AdventCalendarのはじめは、簡単なアプリケーションを作成するチュートリアル的に進めていこうと思います。残った部分は試してみたいことやTipsを書いていこうと思います。

ひとりで完走できるようにがんばりますが、参加してくださる方がいたらぜひお願いします!

次からはNancyFxのチュートリアルです。最初は基本的なプロジェクトの作り方をまとめます。

それでは、よろしくお願いします!

NancyFxでWebAPIを作るときに必ず参照すべきライブラリ

1年以上ぶりにNancyFxネタ。日本語の情報があまりないので、色々書いて行こうと思います。
ユーザが増えてくれれば嬉しいですね。

さてNancyFxでは、デフォルトでJsonレスポンスを返すことが出来ます。

Get["/json"] = _ => {
    var data = new {
        Id = 1,
        Name = "rabitarochan",
        BlogUrl = "http://rabitarochan.hatenablog.com/"
    };

    return Response.AsJson(data);
};

ブラウザからこのURLにアクセスすると、以下のようなJsonが返ります。

{
    Id: 1,
    Name: "rabitarochan",
    BlogUrl: "http://rabitarochan.hatenablog.com/"
}

よくよく見てみると、キーがC#のプロパティ名と同じUpperCamelCaseとなっていますね。
そのため、JavaScript側でも同じくUpperCamelCaseで扱う必要があります。

ただし、JavaScriptではほとんどの場合、lowerCamelCaseが使われていると思いますので、JavaScript側がちょっと気持ち悪い感じになってしまいます。

できればNancyFxからJsonを返す際に、キーをlowerCamelCaseに変換してくれればベストです。
NancyFxのソースを読んでみましたが、残念ながらキーとなる文字列を変換するオプション等はありませんでした。

(ちなみに、NancyFxは独自のJsonシリアライザを使っています。)

そこでNancy.Serialization.JsonNetの出番です!

リポジトリはこちら。NuGetからもインストール可能です。

https://github.com/NancyFx/Nancy.Serialization.JsonNet

このライブラリは、Jsonレスポンスを返す際のシリアライザとして、C#からJsonを扱う際に一番使われていると思われるライブラリJson.Netを使うように変更してくれるだけのものです。

そして、Json.NetJsonSerializerクラスを少し変更するだけで、キーをlowerCamelCaseに変換することが可能です!!

設定方法

設定はとても簡単で、シリアライザを継承したクラスを1つ作成することと、それをDIコンテナに登録することだけです。

リアライザを継承したクラスを作成する

Json.Net用のシリアライザを継承したクラスを作成します。今回と同じ目的の場合は、以下のコードをコピペでOKです。
詳細については、Json.Netのドキュメントを参照してください。

public class CustomJsonSerializer : JsonSerializer
{
    public CustomJsonSerializer()
    {
        this.ContractResolver = new CamelCasePropertyNamesContractResolver();
    }
}

DIコンテナに登録する

Bootstrapperクラスにて、DIコンテナTinyIocに先ほど作成したクラスを登録します。

public class Bootstrapper : DefaultNancyBootstrapper
{
    protected override void ConfigureApplicationContainer(Nancy.TinyIoc.TinyIoCContainer container)
    {
        base.ConfigureApplicationContainer(container);

        container.Register(typeof(JsonSerializer), typeof(CustomJsonSerializer));
    }
}

仕組み

Nancy.Serialization.JsonNetは、JsonのシリアライザとしてJson.Netを利用するように変更してくれるプラグインのようなものです。

NancyFxでは、コンテントタイプ毎に任意のシリアライザが登録できる仕組みになっているようです。
(その仕組みまではまだ見ていません。)

デフォルトではJson.NetJsonSerializerをそのまま使用しますが、BootStrapperクラスにて今回作成したようなクラスを登録してあげることで、そちらのクラスが使用される、ということです。

最初に書いたソースはそのままで同じURLにアクセスすると、ちゃんとlowerCamelCaseに変換されていることが分かります。

{
    id: 1,
    name: "rabitarochan",
    blogUrl: "http://rabitarochan.hatenablog.com/"
}

シリアライズは??

PostされたJsonをC#のクラスに変換する場合は、UpperCamelCaselowerCamelCase関係なく変換してくれます。

変換には、NancyFxが提供しているModelBindingという仕組みを利用します。

以下の例では、フォームからPostされたパラメータをクラスに変換し、そのままJsonレスポンスとして返しています。

namespace NancyJsonTest.Modules
{
    using Nancy;
    using Nancy.ModelBinding; // クラスに変換するための拡張メソッド用。

    public class HomeModule : NancyModule
    {
        // フォーム値用のクラス
        class TestForm
        {
            // input type=text name=userName
            public string UserName { get; set; }
            
            // input type=password name=password
            public string Password { get; set; }
        }
        
        public HomeModule()
        {
            Get["/json"] = _ => {
                var data = new {
                    Id = 1,
                    Name = "rabitarochan",
                    BlogUrl = "http://rabitarochan.hatenablog.com/"
                };

                return Response.AsJson(data);
            };

            Post["/json"] = _ => {
                var form = this.Bind<TestForm>();

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

まとめ

このライブラリを利用することで、C#JavaScriptのプロパティ名の違いがほぼ吸収できると思います。

NancyFxでWebAPIを作成するときは、ぜひこのライブラリを利用してみてください。

プロキシサーバを指定してgit cloneできるコマンドを作りました

タイトル通りですが、jgitの勉強がてら、プロキシサーバを指定してgitリポジトリをクローンできる「jgit-proxy-clone」を作りました。名前もそのまんまです。

リポジトリはこちら。 https://github.com/rabitarochan/jgit-proxy-clone

作ったきっかけ

以下のツイートを見たのがきっかけです。

・・・実は同じようなことで悩んでいて、ちょっと前にツイートしてましたw

会社のネットワーク環境にはプロキシサーバが設置されているため、外部のgitリポジトリを見に行く場合はそのプロキシを経由しなければいけません。
しかし、社内のgitリポジトリについてはプロキシを経由するとアクセスできなくなるため、クローンする度に.gitconfigファイルを修正していました。

今回作ったもの

jgitを使って、プロキシサーバを指定して(または、プロキシサーバを経由しないようにして)gitリポジトリをクローンするだけのコマンドを作りました。

コマンド実行時に指定したプロキシ設定については、自動的にリポジトリのconfigファイルに登録するようにしています。

そのため、クローンする時だけこのコマンドを使えばよくて、それ以降は通常のgitリポジトリとして使うことができます。

使い方

最初のリリース版を作成していますので、以下のページから「jgit-proxy-clone-0.1.jar」ダウンロードして、任意のディレクトリに保存します。

https://github.com/rabitarochan/jgit-proxy-clone/releases

jarの実行方法は以下のとおりです。

(プロキシサーバを指定する場合)
> java -jar jgit-proxy-clone-0.1.jar <リモートURL> [<ディレクトリ>] -h <プロキシホスト名 or IPアドレス> -p <プロキシポート番号>

(プロキシサーバを使わない場合)
> java -jar jgit-proxy-clone-0.1.jar <リモートURL> [<ディレクトリ>]

上記のとおり、プロキシサーバのホスト名とポート番号を指定せず、リモートURLのみを指定して実行した場合、 .gitconfigのプロキシ設定やシステムのプロキシ設定を使わず、直接接続してクローンを実行します。

その点だけ注意してもらえれば、最低限使えるものになっています。

今後

最低限使えるものとして、とりあえずリリースした状況です。ですので、ドキュメントだったり、実行時の表示だったりがほぼ出来ていない状態となっています。

とりあえずは使い方の記述と、動作がわかるような画面表示を実装使用と思います。

それと、現在は認証が必要なリポジトリにはアクセス出来ません。これについても早めに実装しようと思っています。

まとめ

こんな状態のままリリースしてしまいましたが、最低限は動くものとなっています。
プロキシ環境が複雑でgitリポジトリのクローンに困っている方、ぜひお使いいただきフィードバック頂ければと思います。

pullreqお待ちしてます!

play2でフィルター処理を実現する

前回の記事 をベースに考えて、なんとなく動くフィルター処理を作ってみました。

https://github.com/rabitarochan/play2-filter-sandbox

フィルターの書き方についてはだいたいイメージ通りに行きました。

object Application extends Controller with FilterController {
  
  def index = LoggingFilter >> WithAction { req =>
    Ok(views.html.index("Your new application is ready."))
  }
  
  case class User(name: String, password: String)
  
  case object UserKey extends AttributeKey[User]

  /**
   * 認証もどきのフィルター。
   * 
   * QueryString に user と password が設定されていて、
   * その値が同じであれば User を作成して次の処理へ進む。
   * それ以外の場合は認証エラー (Unauthorized) を返す。 
   */
  def AuthFilter = Filter { req: AttributedRequest[AnyContent] =>
    val userOpt = req.getQueryString("user")
    val passwordOpt = req.getQueryString("password")
    
    (userOpt, passwordOpt) match {
      case (Some(user), Some(password)) if user == password => {
        continueWith(UserKey -> User(user, password))
      }
      case _ => Unauthorized("user is not equal password.")
    }
  }
  
  /**
   * ログを出力するだけのフィルター
   */
  def LoggingFilter = Filter { req: AttributedRequest[AnyContent] =>
    Logger.debug(s"[${req.method}] ${req.path}")
    continue
  }
  
  
  def test = 
    LoggingFilter >>
    AuthFilter >>
    WithAction { implicit request =>
      val user = UserKey.get
      Ok(s"Action: ${user}")
    }
  
}

次のフィルター、アクションにパラメータを渡すあたりの実装は、t2v/stackable-controllerをかなり参考にさせていただきました。

なぜこれを作ったのか

参考にさせていただいた stackable-controller は、StackableControllerを継承したトレイトをControllerにmixinしていくことで、同じように共通処理を実行できます。

ただ、このフィルターのように、実際どのような処理が行われているのかが見えるほうが分かりやすいのではないかと思い、作ってみることにしました。

ふと思いついた、という理由が大きかったり。

課題

テスト等もまだまだ不十分ですし、これを使って何かを作ったわけではないです。それはこれからとして、今の時点でどうにかならないか考えていることが何個かあります。

1. Filterのリクエストの型を明示しない方法を作りたい

上記のコードの通り、全てのFilter処理のリクエストに型を明示しています。これを書かないとコンパイルが通らないため書いていますが、どうにか明示しない方法がないかを模索しています。

完全に書かないわけにはいかないと思うので、フィルターの型パラメータにするなどで、省略出来ればいいかなーと考えています。

// Filterの型パラメータとして
def HogeFilter = Filter[java.io.File] { req => 
  continue
}

// 省略したら AnyContent とみなす

def HogeFilter = Filter { req =>
  continue
}

2. 内部処理はなるべくPlay2標準のActionを利用するようにしたい

現在はFilter処理を保持するためにFilterActionというクラスを作っていますが、独自のものよりは標準で用意されているものを使ったほうがいいのかなと思っています。

3. 英語・・・

scaladocを英語で書きたかったのですが、読めるけど書けないというなんとも残念な感じなので、どうにかしたいところです。


とりあえず動くところまでは行ったので、あとは使いながら修正していこうかなと思います。もう少ししっかり作れば、最低限のフィルター処理的なものに使えると思うので、実用出来るところまで持って行きたいです。