Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

以前こんな記事を書いたときに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アプリケーションの新規作成、または既存の移行のハードルが下がってくれたら何よりです。

duo
総合 ITベンチャーです。バーチャルライブ配信アプリ「IRIAM」、「ミライアカリ」をはじめとするバーチャルタレント事業、アミューズメント事業を展開しています。
https://zizai.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした