ASP.NET で HTTP ハンドラーを作成する

以前に投稿した静的コンテンツへの要求を動的に処理するという記事に関連して、
今回は HTTP ハンドラーを作成する方法を紹介します。

ASP.NET で HTTP 要求に対して応答を返す方法としては、
IHttpHandler インターフェイスを実装したクラスを作成する方法があり、
.aspx や MVC を使うよりも原始的です (最も原始的な方法は Global.asax)。
ProcessRequest メソッドの引数で HttpContext オブジェクトが渡されます。
HTTP 要求の情報はここからすべて取得でき、応答もこのオブジェクトに格納します。
これにより、任意の処理を実装できます。

IHttpHandler インターフェイスを利用する方法として、
.ashx ファイルを作成する方法もありますが、これでは拡張子が .ashx に固定されてしまいます。
任意の拡張子を利用するには、通常のクラスとして IHttpHandler インターフェイスを実装し、
Web.config で拡張子や HTTP メソッド (GET など) とのマッピングを指定します。

以下では、.txt へのアクセスに対する HTTP ハンドラーを実装します。

サンプルの全体は HttpHandlerWeb (GitHub) にあります。
なお、このサンプルでは Web アプリケーション プロジェクトではなく、Web サイトを利用しています。
そのため、App_Code フォルダーの下にクラスを配置しています。

これで、.txt へのアクセスを受け付けられるようになりました。

file1.txt

 

以前のバージョン (IIS 6.0 以前?) では、Web.config の system.web/httpHandlers セクションで設定していました。

 

前回:静的コンテンツへの要求を動的に処理する

作成したサンプル
HttpHandlerWeb (GitHub)

バージョン情報
.NET Framework 4.5

参照
チュートリアル : 同期 HTTP ハンドラーの作成 (MSDN)

広告
カテゴリー: .NET Framework. タグ: . 1 Comment »

ASP.NET Web API の Tips (2)

ASP.NET Web API を利用する際の注意点や備忘録です。ほぼ箇条書きです。
基本的な説明は省略しています。

例外処理

  • 戻り値が HttpResponseMessage または IHttpActionResult の場合、Request.CreateErrorResponse メソッドなどで HttpResponseMessage を生成する
  • 戻り値が HttpResponseMessage でも IHttpActionResult でもない場合、HttpResponseException を返すことで同様の効果が得られる
  • HttpError を利用して、JSON のエラー メッセージを含めることができる

公式解説: Exception Handling in ASP.NET Web API

Help Page

コードの XML ドキュメントから、ユーザー向けのヘルプ ページを自動的に生成する機能です。
Visual Studio でプロジェクトを作成するときに Web API を選択すると、Help Page もインストールされます。
あとから追加するには、NuGet で Microsoft.AspNet.WebApi.HelpPage をインストールします。

ただし、既定では機能が有効になっていません。

  • 有効にする手順:
    • プロジェクトのプロパティで、XML ドキュメントの出力を有効にする
    • HelpPageConfig.cs のコメントアウトを解除する ( // を取る)

SetDocumentationProvider

  • アクション メソッドの戻り値が HttpResponseMessage または IHttpActionResult の場合、
    [ResponseType(typeof(string))] のように属性でデータの型を指定する
  • Areas\HelpPage にソースコードがあるため、カスタマイズ可能
  • ヘルプ ページ (Help/Index) を既定のページに設定するには、HelpPageAreaRegistration.cs でルーティングの設定を追加するとよい
  • ASP.NET Core Web API では、Help Page を使えない
    • Swashbuckle (Swagger の .NET 向け実装) を使う

HelpPage

公式解説: Creating Help Pages for ASP.NET Web API

Web API の呼び出し

.NET アプリケーションから Web API を呼び出すには、HttpClient クラスを利用するとよいでしょう。
また、サービス側で実装されたカスタム データ型も、サービス コントラクトとして利用できます。
すなわち、応答メッセージに対して response.Content.ReadAsAsync<T>() を呼び出せば T 型としてデシリアライズできます。

公式解説: Call a Web API From a .NET Client (C#)

CORS

  • NuGet で Microsoft.AspNet.WebApi.Cors をインストールする
  • WebApiConfig.cs で config.EnableCors メソッドを呼び出すことで機能を有効にする
    • 引数に EnableCorsAttribute を渡すとグローバルに設定できる
    • コントローラー、アクションのレベルでは [EnableCors] を指定する

公式解説: Enabling Cross-Origin Requests in ASP.NET Web API 2

JSONP

未検証。

  • MediaTypeFormatter を利用する
    • WebApiContrib.Formatting.Jsonp など

フォーマット

ASP.NET Web API は、既定で JSON と XML をサポートします。
要求の Accept ヘッダーに何が指定されているかで結果のフォーマットが変わります。
Google Chrome 上で Web API を直接呼び出すと結果が XML 形式で返ってきます。
これは、要求の Accept ヘッダーに application/xml が含まれているためと考えられます。
これを JSON 形式にするには、text/html が含まれている場合に JSON を返す設定や XML 形式を無効にする設定が考えられます。

解説: How do I get ASP.NET Web API to return JSON instead of XML using Chrome?

 

前回: ASP.NET Web API の Tips (1)

作成したサンプル

バージョン情報

  • .NET Framework 4.5
  • ASP.NET Web API 5.2.3
  • ASP.NET MVC 5.2.3
  • ASP.NET Web API Help Page 5.2.3
  • ASP.NET Web API CORS 5.2.3

参照

カテゴリー: .NET Framework. タグ: , . 1 Comment »

ASP.NET Web API の Tips (1)

ASP.NET Web API を利用する際の注意点や備忘録です。ほぼ箇条書きです。
基本的な説明は省略しています。

ルーティング

WebApiConfig.cs にルーティングの設定が記述されています。
既定のテンプレートは api/{controller}/{id} となっており、REST スタイルを想定したものとなっています。
ただし、ASP.NET MVC と同様に {action} も利用可能であり、RPC スタイルの API も構成できます。

  • [RoutePrefix], [Route], [ActionName] などの属性を利用することで柔軟に構成できる
  • [Route] を複数設定できる

公式解説: Web API Routing

HTTP メソッド

  • 主に REST スタイルの場合、Get、Post などのメソッド名で解決される (CoC)。
    この場合、[HttpGet] などの属性を指定する必要はない
  • 主に RPC スタイルで任意のアクション名を利用するには、[HttpGet] などの属性を指定する

引数

  • DateTime, Guid などの型も扱える
  • 引数の [FromBody] は、エンティティ型なら付ける必要はない
  • ルーティングで {i:int:range(0,100)} のような制約を追加できる
  • 引数に既定値を指定すると、URL のパラメーターを省略可能
    • ルーティングで指定する場合は {i:int?} のようにする
    • range などを使う場合、省略可能にできない
      • int? と range は同時に指定できない
  • 引数で / を使う場合、引数名の前に * を指定する (下の DateTime 型の例)

公式解説: Attribute Routing in ASP.NET Web API 2

戻り値

戻り値として、通常のデータ型以外に次のものを指定できます。

  • HttpResponseMessage
    • 生の応答データ
  • IHttpActionResult
    • HttpResponseMessage をラップしたインターフェイス

これらを利用することにより、応答データを柔軟に設定できます。

  • JSON や XML に限らず、テキストや画像などの任意の形式のコンテンツを返せる
  • 主な IHttpActionResult の実装は System.Web.Http.Results 名前空間で定義されている

以下は、テキスト (Content-Type: text/plain) を返す例です。

公式解説: Action Results in Web API 2

 

次回: ASP.NET Web API の Tips (2)

作成したサンプル

バージョン情報

  • .NET Framework 4.5
  • ASP.NET Web API 5.2.3

参照

カテゴリー: .NET Framework. タグ: , . 2 Comments »

メソッドチェーンでアスペクト指向プログラミング

透過プロキシでアスペクト指向プログラミング (1) では、ログ出力やデータベース トランザクションなどの横断的関心事を、

public class NorthwindBusiness : MarshalByRefObject
{
    [TraceLog]
    [TransactionScope]
    public short SelectUnitsInStock()
    {
    }
}

のように、属性として記述することができました。
今回は属性を使わずに、LINQ と同様にメソッドチェーンを使って横断的関心事を記述してみます。

まず、戻り値を持つ (Func<TResult> に相当する) 処理を IProxyable<TResult> インターフェイスとして定義して、
後ろにメソッドチェーンを追加することで処理を上書きできるようにします。

これを実行すると出力は次のようになり、元の処理の前後に処理が追加されていることがわかります。

Before
Body
After

次に、戻り値を持たない (Action に相当する) 処理を表す IProxyable インターフェイスを、
IProxyable<TResult> インターフェイスの特別な場合とみなして継承させます。

あとは、ログ出力やデータベース トランザクションなどの横断的関心事を拡張メソッドとして作成すれば完成です。

実行結果です:

ProxyableConsole

 

使い道としては、

  • .NET で透過プロキシを使いたくないとき (処理速度を上げたい、など)
  • .NET の属性のような仕組みを持たない別のプラットフォーム

などの場合が考えられるでしょう。

前回:透過プロキシでアスペクト指向プログラミング (2)

作成したサンプル
ProxyableConsole (GitHub)

バージョン情報
C# 7.0
.NET Framework 4.5

参照
アスペクト指向プログラミング

カテゴリー: .NET Framework, データベース. タグ: . 1 Comment »

透過プロキシでアスペクト指向プログラミング (2)

前回の記事で、NorthwindBusiness クラスの透過プロキシを生成するときに、

var nw = CrossCuttingProxy.CreateProxy<NorthwindBusiness>();

というコードを記述していましたが、
MarshalByRefObject の代わりに ContextBoundObject クラスを継承させると、通常のコンストラクターを利用することができます。
ただし、クラスに属性を付ける必要があります。
次のように実装します。

以上で、

var nw = new NorthwindBusiness();

と記述できるようになりました。
なお、上記のコードには現れていませんが、
コンストラクターが呼び出されたときに、CrossCuttingProxy クラスの Invoke メソッドが呼び出されます。

 

前回:透過プロキシでアスペクト指向プログラミング (1)
次回:メソッドチェーンでアスペクト指向プログラミング

作成したサンプル
CrossCuttingConsole (GitHub)

バージョン情報
C# 7.0
.NET Framework 4.5

参照
RealProxy クラス
アスペクト指向プログラミング (Wikipedia)

透過プロキシでアスペクト指向プログラミング (1)

前回のインターフェイスに対する透過プロキシでは、RealProxy クラスを利用してインターフェイスに対する透過プロキシを生成し、
その実装クラスが存在していなくてもメソッドに処理を割り当てることができました。

改めて、透過プロキシ (transparent proxy) の主な特徴を整理すると次のようになります。

  • 対象となる型は、MarshalByRefObject を継承したクラス、またはインターフェイス
  • 各メンバーが呼び出されたときの挙動を上書きできる

今回は MarshalByRefObject を継承したクラスの既存の処理を透過プロキシで拡張して、
アスペクト指向プログラミング (AOP) を実践してみます。

一般的にアプリケーション設計においては、
ログ出力やデータベース トランザクションなど、いろいろなビジネス ロジックに共通する横断的関心事があります。
例えばデータベース トランザクションであれば、以前にファントム リードとその解決方法などで書いている通り、
ビジネス ロジックごとに TransactionScope に関する同じようなコードを繰り返し記述することになります。
この部分をアスペクト (側面) として分離し、属性として記述できるようにすることで再利用性を高めることを目指します。

まず次のコードで示す通り、RealProxy を継承した CrossCuttingProxy クラスと、アスペクトを表す属性を定義します。

ログ出力を表す TraceLogAttribute クラスとデータベース トランザクションを表す TransactionScopeAttribute クラスを用意し、
既存の処理の前後に割り込むようにしてそれぞれの処理を追加しています。

以上のようにすれば、ビジネス ロジックを次のように記述するだけで済むようになります。

実行結果です。ログが出力されています:

CrossCuttingConsole

 

前回:インターフェイスに対する透過プロキシ
次回:透過プロキシでアスペクト指向プログラミング (2)

作成したサンプル
CrossCuttingConsole (GitHub)

バージョン情報
C# 7.0
.NET Framework 4.5

参照
RealProxy クラス
アスペクト指向プログラミング (Wikipedia)

インターフェイスに対する透過プロキシ

前回の DLR で名前付き引数を使うでは、メソッドに引数として「キーと値のペア」を渡す方法を考え、
動的言語ランタイム (DLR) を利用する方法を紹介しました。
しかし、同じ処理を複数の場所で呼び出す必要があるなど、再利用性を重視する場合は、
なるべく静的にシグネチャを解決する方法がよいでしょう。

そこで今回は、呼び出すサービスをインターフェイスとして定義し、透過プロキシ (transparent proxy) を利用してみます。
先にコードを示します。

このコードでは、RealProxy クラスを継承した HttpProxy<IService> クラスを作成しています。
RealProxy はプロキシの実体となるものであり、
その外層である透過プロキシを RealProxy.GetTransparentProxy メソッドで取得できます。

一方、利用する側の Program.cs では、ICgisService インターフェイスを定義しておきます。
その透過プロキシを生成すれば、ICgisService インターフェイスを実装するクラスが存在しなくてもメソッドを呼び出すことができ、
実体は HttpProxy<IService> クラスの Invoke メソッドとなります。

また、BaseUriAttribute クラスを定義しており、
呼び出される Web API のベースとなる URI を属性で指定できるようにしています。

WCF における契約プログラミングでは、クライアント側とサーバー側で同一のインターフェイスを利用し、
クライアント側からのアクセスを上記のように透過プロキシで実装する方法があります。
方法 : ChannelFactory を使用する にある通り、ChannelFactory.CreateChannel メソッドで透過プロキシを生成します。

前回:DLR で名前付き引数を使う
次回:透過プロキシでアスペクト指向プログラミング (1)

作成したサンプル
TransparentHttpConsole (GitHub)

バージョン情報
C# 7.0
.NET Framework 4.5

参照
RealProxy クラス
方法 : ChannelFactory を使用する