Hatena::Groupsubtech

ういはるかぜの化学

Tuesday, December 25, 2012

ASP.NETWebサーバーを自分のアプリケーションに組み込む 07:22 ASP.NETとWebサーバーを自分のアプリケーションに組み込む - ういはるかぜの化学 を含むブックマーク はてなブックマーク - ASP.NETとWebサーバーを自分のアプリケーションに組み込む - ういはるかぜの化学

このエントリはOne ASP.NET Advent Calendar 2012の25日目です。いわゆる最終日です。

せっかくの最終日なのにいそがしくてネタに走れず割と普通(そのままの意味)の内容になってしまってごめんなさい。

アプリケーションでWebサーバーを立ててASP.NETをホストしたい

というわけで今回は普通のアプリケーションつまり一般的な実行形式(.exe)なアプリケーションでWebサーバーを立てて、そこでASP.NETをホストしたいなーという話です。

どういうときにそんなものを使うのかというと例えばアプリケーションだけどリモートからちょっといじるようにWeb機能がついてるとかありますよね。VLCとか録画用ソフトとか。でも大抵そのようなアプリケーションを自分で作った時にWebサーバーがあれこれ返すための実装はテンプレートエンジンがーとか色々と結構泥臭くなりがちです。

つまりIIS ExpressIISを使うまでもない超簡易的なWebサーバー機能を持たせたい、けれどWebサーバー機能のアプリケーションもASP.NETとかで書きたい、といった感じです。

Webサーバーを組み込む

というわけでまずはアプリケーションにWebサーバーを組み込んでみましょう。

.NET FrameworkにはHttpListenerクラスというWebサーバー機能を組み込むための便利なものがあるのでこれを使います。

ちなみにこれはHTTP.sysというHTTPサーバー機能を提供するWindowsのカーネルモジュールを使うクラスです。HTTP.sysはIISやWinRMとか色々なもので使われています。能楽堂で使ってるEnnouとかもそうですね。

というわけで書いてみたのがこちら。

static void Main(string[] args)
{
    var httpListener = new HttpListener();
    httpListener.Prefixes.Add("http://localhost:38080/");
    httpListener.Start();
 
    while (true)
    {
        var ctx = httpListener.GetContext();
        ctx.Response.ContentType = "text/plain; charset=UTF-8";
        using (var writer = new StreamWriter(ctx.Response.OutputStream))
        {
            writer.Write("爆ぜろリアル!弾けろシナプス!");
        }
    }
}

とりあえず固定文字列を返すだけのものがさくっとできました。これで起動するとポート38080で待ち受けてブラウザでアクセスできるようになります。すでに夢が広がり始めましたね。

ちょっと注意なところとしてHttpListenterの仕様上localhost以外を指定した場合(例えばIPアドレスや*など)や特定のポートを指定した場合には管理者権限などで起動してあげるか、下記のようにnetshコマンドを管理者権限で実行して許可する必要があります。

netsh http add urlacl url=http://*:37564/ user=HOMURA\Tomoyo

ASP.NETをホストする

では次はASP.NETをアプリケーションでホストする方法についてです。ASP.NETIISで動かす以外にもアプリケーションでホストする方法が用意されています。実はHTTPサーバーなしでも処理はできたりします。

ホストするにはApplicationHostクラスCreateApplicationHostメソッド というものがあるのでこれを使います。

このメソッドはASP.NET用のAppDomainを作ってその中で指定したクラスをホスト用のインスタンスとして作って返します。というわけでこれもさくっと例を。

class Program
{
    static void Main(string[] args)
    {
        // GACかbinディレクトリに自分自身がないとアプリケーションのホストを生成できない
        // (AppDomainが生成されるときにNotFoundになる)のでコピーする。
        var assemblyPath = Assembly.GetEntryAssembly().Location;
        var wwwDir = Path.GetDirectoryName(assemblyPath);
        var wwwBinDir = Path.Combine(wwwDir, "bin");
        Directory.CreateDirectory(wwwBinDir);
        File.Copy(assemblyPath, Path.Combine(wwwBinDir, Path.GetFileName(assemblyPath)), true);

        // アプリケーションホストをつくる
        var appHost = (MyAppilcationHost)ApplicationHost.CreateApplicationHost(typeof(MyAppilcationHost), "/", wwwDir);

        // テスト用のaspxファイルを作る
        File.WriteAllText("test.aspx""DateTime.Now = <%: DateTime.Now %>");

        // test.aspx を実行する
        var writer = new StringWriter();
        appHost.ProcessRequest("test.aspx", writer);

        // 結果を出力してみる
        Console.WriteLine(writer.ToString());
        Console.ReadLine();
    }
}
class MyAppilcationHost : MarshalByRefObject
{
    public void ProcessRequest(String path, TextWriter outputWriter)
    {
        HttpRuntime.ProcessRequest(new SimpleWorkerRequest(path, "", outputWriter));
    }
}

簡単に説明すると…

ちなみにCreateApplicationHostメソッドでWebアプリケーションのルートディレクトリを指定します。その指定したディレクトリ以下のファイルを実行することになります(IISのアプリケーションディレクトリみたいなもの)。

まとめ: WebサーバーとASP.NETのホスティングをくっつける

というわけで必要な要素はそろったのでこれを合体させます。

合体させるのはそんな大変ではなくて HttpWorkerRequestクラス を実装する必要があるぐらいです(SimpleWorkerRequestだとちょっと足りないのです)。合体させると普通のWeb FormsもそうですがASP.NET MVCアプリケーションも動作します。たぶんWeb FormsにしろASP.NET MVCにしろそのままでは完全に動作しないアプリケーションもあると思いますがそれでも全く動かないより全然よいと思います。

さすがにコードはちょっと長くてここに書けないのでGitHubに上げておきましたのでご覧ください。大した分量でもなくコメントつけまくったので大体安心です。

no title

ビルドしてあるASP.NET MVC 4のアプリケーションが動くサンプルはこちらです。

https://github.com/mayuki/SimpleAspNetWebHost/raw/master/Build/SimpleAspNetWebHost-Sample-20121225.zip

SimpleAspNetWebHost.exeを実行すると自動的にブラウザが開きます。wwwrootというフォルダにASP.NET MVC 4のテンプレートから生成されたものが入ってますが、適当にアプリを放り込めば自由に動かすことができます。

ということでぜひご活用ください。

もしもう少しちゃんとしたWebサーバーがーとか安定性がーとかであればIIS Expressを展開するのがよいのではないでしょうか。

ちなみに

何でこんなことを調べたのかというとこういうのを作ろうとしていたからでした。

f:id:mayuki:20121225071811p:image:w640

iTunesの曲をそのままWeb経由で再生するもので、iPadとかみたいに共有できない端末でも何とか聞けるようにみたいなのをやりたいなーと。そういうツールなので起動するだけのお手軽なものだけどアプリ的な部分はWebなのでASP.NET使いたい!!みたいな感じでした。

本当は間に合えばセットで公開するつもりでしたが余裕がなかったのでいつか…。

トラックバック - http://subtech.g.hatena.ne.jp/mayuki/20121225