これらの答えはすべて非常に優れています。GenericHandlerRouteHandler<T>
ミーチャム先生の授業のシンプルさが大好きです。HttpHandler
特定のクラスがわかっている場合は、仮想パスへの不要な参照を削除することをお勧めします。ただし、GenericHandlerRoute<T>
クラスは必要ありません。Route
から派生する既存のクラスRouteBase
は、ルート マッチング、パラメータなどの複雑さをすべて処理しているため、 と一緒に使用することができますGenericHandlerRouteHandler<T>
。
以下は、ルートパラメーターを含む実際の使用例と組み合わせたバージョンです。
最初はルート ハンドラです。ここには 2 つ含まれています。どちらも同じクラス名ですが、1 つはジェネリックであり、型情報を使用してHttpHandler
Meacham 氏の使用法のように特定のインスタンスを作成し、もう 1 つは仮想パスを使用しBuildManager
てインスタンスを作成します。HttpHandler
shellscape の使用法と同様に適切です。幸いなことに、.NET では両方を問題なく共存させることができるため、必要な方を使用したり、必要に応じて切り替えることができます。
using System.Web;
using System.Web.Compilation;
using System.Web.Routing;
public class HttpHandlerRouteHandler<T> : IRouteHandler where T : IHttpHandler, new() {
public HttpHandlerRouteHandler() { }
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
return new T();
}
}
public class HttpHandlerRouteHandler : IRouteHandler {
private string _VirtualPath;
public HttpHandlerRouteHandler(string virtualPath) {
this._VirtualPath = virtualPath;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
return (IHttpHandler) BuildManager.CreateInstanceFromVirtualPath(this._VirtualPath, typeof(IHttpHandler));
}
}
HttpHandler
仮想フォルダーの外部のリソース (場合によってはデータベースから) からユーザーにドキュメントをストリーミングする を作成し、単にダウンロードを提供するのではなく、特定のファイルを直接提供しているとユーザーのブラウザーに信じ込ませたいと仮定しましょう。 (つまり、ユーザーにファイルの保存を強制するのではなく、ブラウザーのプラグインがファイルを処理できるようにします)。はHttpHandler
、提供するドキュメントを検索するためのドキュメント ID を期待する場合があり、ブラウザに提供するファイル名 (サーバーで使用されるファイル名とは異なる場合がある) を期待する場合があります。
以下は、 でこれを達成するために使用されるルートの登録を示していますDocumentHandler
HttpHandler
。
routes.Add("Document", new Route("document/{documentId}/{*fileName}", new HttpHandlerRouteHandler<DocumentHandler>()));
パラメータがオプションのキャッチオール パラメータとして機能する ことを許可する{*fileName}
だけでなく、使用しました。{fileName}
fileName
この によって提供されるファイルの URL を作成するには、クラス自体 HttpHandler
など、そのようなメソッドが適切なクラスに次の静的メソッドを追加できます。HttpHandler
public static string GetFileUrl(int documentId, string fileName) {
string mimeType = null;
try { mimeType = MimeMap.GetMimeType(Path.GetExtension(fileName)); }
catch { }
RouteValueDictionary documentRouteParameters = new RouteValueDictionary { { "documentId", documentId.ToString(CultureInfo.InvariantCulture) }
, { "fileName", DocumentHandler.IsPassThruMimeType(mimeType) ? fileName : string.Empty } };
return RouteTable.Routes.GetVirtualPath(null, "Document", documentRouteParameters).VirtualPath;
}
この例を単純にするためにMimeMap
、 and との定義を省略しました。ただし、これらは、特定のファイルの種類がファイル名を URL で直接提供する必要があるかどうか、またはHTTP ヘッダーIsPassThruMimeType
で提供する必要があるかどうかを判断することを目的としています。Content-Disposition
一部のファイル拡張子は、IIS または URL スキャンによってブロックされるか、ユーザーに問題を引き起こす可能性のあるコードが実行される可能性があります (特に、ファイルのソースが悪意のある別のユーザーである場合)。このタイプのリスクにさらされていない場合は、このロジックを他のフィルタリング ロジックに置き換えるか、そのようなロジックを完全に省略することができます。
この特定の例では、ファイル名が URL から省略されている可能性があるため、明らかに、どこかからファイル名を取得する必要があります。この特定の例では、ドキュメント ID を使用してルックアップを実行することでファイル名を取得できます。URL にファイル名を含めることは、ユーザー エクスペリエンスを向上させることのみを目的としています。そのため、DocumentHandler
HttpHandler
は URL にファイル名が指定されているかどうかを判別できます。指定されていない場合は、単純にContent-Disposition
HTTP ヘッダーを応答に追加できます。
トピックにとどまり、上記のコード ブロックの重要な部分は、ルート登録プロセス中に作成したオブジェクトRouteTable.Routes.GetVirtualPath()
から URL を生成するためのルーティング パラメータの使用です。Route
これは、クラスの簡素化されたバージョンですDocumentHandler
HttpHandler
(わかりやすくするために、かなり省略されています)。このクラスはルート パラメータを使用して、可能な場合はドキュメント ID とファイル名を取得していることがわかります。それ以外の場合は、クエリ文字列パラメーターからドキュメント ID を取得しようとします (つまり、ルーティングが使用されていないと仮定します)。
public void ProcessRequest(HttpContext context) {
try {
context.Response.Clear();
// Get the requested document ID from routing data, if routed. Otherwise, use the query string.
bool isRouted = false;
int? documentId = null;
string fileName = null;
RequestContext requestContext = context.Request.RequestContext;
if (requestContext != null && requestContext.RouteData != null) {
documentId = Utility.ParseInt32(requestContext.RouteData.Values["documentId"] as string);
fileName = Utility.Trim(requestContext.RouteData.Values["fileName"] as string);
isRouted = documentId.HasValue;
}
// Try the query string if no documentId obtained from route parameters.
if (!isRouted) {
documentId = Utility.ParseInt32(context.Request.QueryString["id"]);
fileName = null;
}
if (!documentId.HasValue) { // Bad request
// Response logic for bad request omitted for sake of simplicity
return;
}
DocumentDetails documentInfo = ... // Details of loading this information omitted
if (context.Response.IsClientConnected) {
string fileExtension = string.Empty;
try { fileExtension = Path.GetExtension(fileName ?? documentInfo.FileName); } // Use file name provided in URL, if provided, to get the extension.
catch { }
// Transmit the file to the client.
FileInfo file = new FileInfo(documentInfo.StoragePath);
using (FileStream fileStream = file.OpenRead()) {
// If the file size exceeds the threshold specified in the system settings, then we will send the file to the client in chunks.
bool mustChunk = fileStream.Length > Math.Max(SystemSettings.Default.MaxBufferedDownloadSize * 1024, DocumentHandler.SecondaryBufferSize);
// WARNING! Do not ever set the following property to false!
// Doing so causes each chunk sent by IIS to be of the same size,
// even if a chunk you are writing, such as the final chunk, may
// be shorter than the rest, causing extra bytes to be written to
// the stream.
context.Response.BufferOutput = true;
context.Response.ContentType = MimeMap.GetMimeType(fileExtension);
context.Response.AddHeader("Content-Length", fileStream.Length.ToString(CultureInfo.InvariantCulture));
if ( !isRouted
|| string.IsNullOrWhiteSpace(fileName)
|| string.IsNullOrWhiteSpace(fileExtension)) { // If routed and a file name was provided in the route, then the URL will appear to point directly to a file, and no file name header is needed; otherwise, add the header.
context.Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", HttpUtility.UrlEncode(documentInfo.FileName)));
}
int bufferSize = DocumentHandler.SecondaryBufferSize;
byte[] buffer = new byte[bufferSize];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, bufferSize)) > 0 && context.Response.IsClientConnected) {
context.Response.OutputStream.Write(buffer, 0, bytesRead);
if (mustChunk) {
context.Response.Flush();
}
}
}
}
}
catch (Exception e) {
// Error handling omitted from this example.
}
}
Utility
この例では、いくつかの簡単なタスクを簡素化するためのクラスなど、いくつかの追加のカスタム クラスを使用します。しかし、うまくいけば、あなたはそれを取り除くことができます. もちろん、現在のトピックに関してこのクラスで唯一本当に重要な部分は、 からのルート パラメータの取得ですcontext.Request.RequestContext.RouteData
。しかし、他の場所で、サーバーのメモリを消費せずに を使用して大きなファイルをストリーミングする方法を尋ねる投稿をいくつか見たHttpHandler
ので、例を組み合わせるのは良い考えのように思えました。