LoginSignup
17
15

More than 5 years have passed since last update.

ASP.NET CoreでもHttpContext.Current使えるようにしてみた

Last updated at Posted at 2017-02-13

以前こんな記事を書いたときにASP.NET Coreでの開発で辛いことの一つで、『HttpContext.Currentがない』なんてことを書きましたが、ないなら作ればいいじゃないの精神で作ってみました!

まぁ実際なくても個人的にはそこまで困ることもなかったんですが、需要なんてあるのかしら的なことを言ったら@neuecc先生にこんなこと言われたので作った次第です。

image

AspNetCoreCurrentRequestContext

という名前でライブラリを作成しました。

パッケージはNuGet、ソースはGithubで公開しています。

使い方

NuGetからパッケージをインストールします。

PM > Install-Package AspNetCoreCurrentRequestContext

ASP.NET Coreアプリケーションのプロジェクトにパッケージの参照を追加したらStartupクラスのConfigureメソッドで、IApplicationBuilderCurrentRequestContextMiddlewareを追加します。

標準で拡張メソッドを用意しているのでそれを使った方が楽です。

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        // ミドルウェアを追加
        app.UseCurrentRequestContext();
    }
}

ミドルウェアが追加できたら後は好きな箇所でHttpContext.Currentを呼ぶだけ、って言いたいところですが現在の.NET言語の機能に静的プロパティを拡張する機能がないため、新しくAspNetCoreHttpContextというクラスを用意しています。

このクラスのCurrentという静的プロパティを呼び出すことで、これまでのASP.NETと同様に現在のリクエストのHttpContextを取得することが出来ます。

using AspNetCoreCurrentRequestContext; // using追加は必須!!

public class Foo
{
    public string GetRequestUrl()
    {
        // このプロパティで現在のHttpContextを取得できる
        var context = AspNetCoreHttpContext.Current;

        return context.Request.Path.Value;
    }
}

ただしCurrentRequestContextMiddlewareの後に追加されたミドルウェアではいつでもAspNetCoreHttpContext.CurrentHttpContextを取得できますが、それよりも前に追加されたミドルウェア内では取得できないことに注意して下さい。

内部実装

内部実装も至ってシンプルで実際のコード行数は100行もありません。

@neueccさんが作成したOWINで使えるHttpContext.CurrentのライブラリCallContextという、同じ実行コンテキストを持つスレッド間でデータを共有することが出来るクラスを使用して実装していましたが、.NET Core向けライブラリのcorefxにはCallContextがないため使用できません。

その代わりに、.NET Framework 4.6で追加されたAsyncLocal<T>というクラスを使用しています。

このAsyncLocal<T>はスレッド単位のストレージとして使用することが出来ます。

子スレッドが派生したとしても親スレッドのストレージを参照することができ、子スレッドで値を書き換えた場合は子スレッドのストレージのみが書き換えられ、親スレッドのストレージに影響を及ぼすことはありません。

public class Program
{
    public static void Main(string[] args)
    {
        var storage = new AsyncLocal<int>();

        storage.Value = 1;

        Console.WriteLine($"Parent - {storage.Value}"); // 1

        new Thread(_ =>
        {
            Console.WriteLine($"Child - {storage.Value}"); // 1
        }).Start();

        new Thread(_ =>
        {
            storage.Value = 2;

            Console.WriteLine($"Child - {storage.Value}"); // 2
        }).Start();

        Console.WriteLine($"Parent - {storage.Value}"); // 1

        Console.ReadLine();
    }
}

AspNetCoreCurrentRequestContextではCurrentReuqestContextMiddlewareAspNetCoreHttpContext内に保持されているAsyncLocal<HttpContext>の変数に値を代入していて、以降はその変数の値を返すようにしています。

まとめ

AspNetCoreCurrentRequestContextを使えば、リクエスト内の統計情報やリクエスト中だけキャッシュしたい値のやり取りが楽になると思います。

これまでASP.NETでHttpContext.Currentに頼っていたためにASP.NET Coreに壁を感じていた方でも、ASP.NET Coreアプリケーションの新規作成、または既存の移行のハードルが下がってくれたら何よりです。

17
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
15