2010-07-29 24 views
21

Tôi có một số dịch vụ REST sử dụng đồng bằng cũ IHttpHandler s. Tôi muốn tạo các URL sạch hơn, do đó tôi không có .ashx trong đường dẫn. Có cách nào để sử dụng định tuyến ASP.NET để tạo ra các tuyến đường mà bản đồ để xử lý ashx? Tôi đã nhìn thấy các loại của các tuyến đường trước đây:Định tuyến ASP.NET có thể được sử dụng để tạo URL "sạch" cho trình xử lý .ashx (IHttpHander) không?

// Route to an aspx page 
RouteTable.Routes.MapPageRoute("route-name", 
    "some/path/{arg}", 
    "~/Pages/SomePage.aspx"); 

// Route for a WCF service 
RouteTable.Routes.Add(new ServiceRoute("Services/SomeService", 
    new WebServiceHostFactory(), 
    typeof(SomeService))); 

Cố gắng sử dụng RouteTable.Routes.MapPageRoute() tạo ra một lỗi (mà xử lý không có nguồn gốc từ Page). System.Web.Routing.RouteBase chỉ dường như có 2 lớp dẫn xuất: ServiceRoute cho các dịch vụ và DynamicDataRoute cho MVC. Tôi không chắc chắn những gì MapPageRoute() không (Reflector không hiển thị cơ thể phương pháp, nó chỉ cho thấy "Hiệu suất quan trọng để nội tuyến loại phương pháp này trên ranh giới hình ảnh NGEN").

Tôi thấy rằng RouteBase không niêm phong, và có một giao diện tương đối đơn giản:

public abstract RouteData GetRouteData(HttpContextBase httpContext); 

public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, 
    RouteValueDictionary values); 

Vì vậy, có lẽ tôi có thể làm cho riêng tôi HttpHandlerRoute. Tôi sẽ cung cấp cho một shot, nhưng nếu có ai biết về một cách hiện có hoặc xây dựng trong cách lập bản đồ các tuyến đường để IHttpHandlers, đó sẽ là tuyệt vời.

Trả lời

24

Ok, tôi đã tìm ra điều này vì tôi đã hỏi câu hỏi, và cuối cùng tôi đã có một giải pháp làm những gì tôi muốn. Tuy nhiên, một chút giải thích trước là do. IHttpHandler là một giao diện rất cơ bản:

bool IsReusable { get; } 
void ProcessRequest(HttpContext context) 

Không có tài sản được xây dựng để truy cập dữ liệu tuyến đường và dữ liệu tuyến đường cũng không thể tìm thấy trong ngữ cảnh hoặc yêu cầu. Đối tượng System.Web.UI.Page có thuộc tính RouteData, ServiceRoute s thực hiện tất cả công việc diễn giải UriTemplates của bạn và chuyển giá trị cho phương thức chính xác nội bộ và ASP.NET MVC cung cấp cách truy cập dữ liệu tuyến đường riêng. Ngay cả khi bạn đã có RouteBase, (a) xác định xem url đến có phù hợp với tuyến đường của bạn không và (b) đã phân tích url để trích xuất tất cả các giá trị riêng lẻ được sử dụng trong IHttpHandler của bạn, không có cách nào dễ dàng định tuyến dữ liệu cho IHttpHandler của bạn. Nếu bạn muốn giữ cho IHttpHandler của bạn "thuần khiết", vì vậy để nói, nó có trách nhiệm đối phó với url và cách trích xuất bất kỳ giá trị nào từ nó. Việc thực hiện RouteBase trong trường hợp này chỉ được sử dụng để xác định xem IHttpHandler của bạn có nên được sử dụng không.

Một vấn đề vẫn còn, tuy nhiên. Khi RouteBase xác định rằng url đến là một kết hợp cho tuyến đường của bạn, nó sẽ chuyển sang một IRouteHandler, nó tạo ra các cá thể của IHttpHandler mà bạn muốn xử lý yêu cầu của mình. Nhưng, một khi bạn đang ở trong IHttpHandler của bạn, giá trị của context.Request.CurrentExecutionFilePath là gây hiểu lầm. Đó là url đến từ khách hàng, trừ chuỗi truy vấn. Vì vậy, nó không phải là đường dẫn đến tập tin .ashx của bạn. Và, bất kỳ phần nào của tuyến đường của bạn không đổi (chẳng hạn như tên của phương thức) sẽ là một phần của giá trị đường dẫn tệp thực thi đó. Điều này có thể là một vấn đề nếu bạn sử dụng UriTemplates trong IHttpHandler của bạn để xác định phương thức cụ thể nào trong IHttpHandler của bạn sẽ đưa ra yêu cầu.

Ví dụ: Nếu bạn đã có một handler ashx tại /myApp/services/myHelloWorldHandler.ashx Và bạn đã có tuyến đường này mà ánh xạ tới các handler: "Dịch vụ/hello/{name}" Và bạn muốn chuyển đến url này , cố gắng gọi phương thức SayHello(string name) của trình xử lý của bạn: http://localhost/myApp/services/hello/SayHello/Sam

Sau đó, CurrentExecutionFilePath của bạn sẽ là:/myApp/services/hello/Sam. Nó bao gồm các phần của url tuyến đường, đó là một vấn đề. Bạn muốn đường dẫn tệp thực thi khớp với url tuyến đường của bạn. Các triển khai dưới đây của RouteBaseIRouteHandler giải quyết vấn đề này.

Trước khi tôi dán 2 lớp, đây là ví dụ sử dụng rất đơn giản. Lưu ý rằng những cài đặt này của RouteBase và IRouteHandler sẽ thực sự làm việc cho IHttpHandlers mà thậm chí không có tệp .ashx, khá thuận tiện.

// A "headless" IHttpHandler route (no .ashx file required) 
RouteTable.Routes.Add(new GenericHandlerRoute<HeadlessService>("services/headless")); 

Điều đó sẽ gây ra tất cả các url đến phù hợp với "dịch vụ/không đầu" lộ trình để được trao tắt để một trường hợp mới của HeadlessService IHttpHandler (HeadlessService chỉ là một ví dụ trong trường hợp này. Nó sẽ là bất cứ điều gì IHttpHandler thực hiện bạn muốn vượt qua).

Ok, vì vậy đây là việc triển khai lớp định tuyến, ý kiến ​​và tất cả:

/// <summary> 
/// For info on subclassing RouteBase, check Pro Asp.NET MVC Framework, page 252. 
/// Google books link: http://books.google.com/books?id=tD3FfFcnJxYC&pg=PA251&lpg=PA251&dq=.net+RouteBase&source=bl&ots=IQhFwmGOVw&sig=0TgcFFgWyFRVpXgfGY1dIUc0VX4&hl=en&ei=z61UTMKwF4aWsgPHs7XbAg&sa=X&oi=book_result&ct=result&resnum=6&ved=0CC4Q6AEwBQ#v=onepage&q=.net%20RouteBase&f=false 
/// 
/// It explains how the asp.net runtime will call GetRouteData() for every route in the route table. 
/// GetRouteData() is used for inbound url matching, and should return null for a negative match (the current requests url doesn't match the route). 
/// If it does match, it returns a RouteData object describing the handler that should be used for that request, along with any data values (stored in RouteData.Values) that 
/// that handler might be interested in. 
/// 
/// The book also explains that GetVirtualPath() (used for outbound url generation) is called for each route in the route table, but that is not my experience, 
/// as mine used to simply throw a NotImplementedException, and that never caused a problem for me. In my case, I don't need to do outbound url generation, 
/// so I don't have to worry about it in any case. 
/// </summary> 
/// <typeparam name="T"></typeparam> 
public class GenericHandlerRoute<T> : RouteBase where T : IHttpHandler, new() 
{ 
    public string RouteUrl { get; set; } 


    public GenericHandlerRoute(string routeUrl) 
    { 
     RouteUrl = routeUrl; 
    } 


    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     // See if the current request matches this route's url 
     string baseUrl = httpContext.Request.CurrentExecutionFilePath; 
     int ix = baseUrl.IndexOf(RouteUrl); 
     if (ix == -1) 
      // Doesn't match this route. Returning null indicates to the asp.net runtime that this route doesn't apply for the current request. 
      return null; 

     baseUrl = baseUrl.Substring(0, ix + RouteUrl.Length); 

     // This is kind of a hack. There's no way to access the route data (or even the route url) from an IHttpHandler (which has a very basic interface). 
     // We need to store the "base" url somewhere, including parts of the route url that are constant, like maybe the name of a method, etc. 
     // For instance, if the route url "myService/myMethod/{myArg}", and the request url were "http://localhost/myApp/myService/myMethod/argValue", 
     // the "current execution path" would include the "myServer/myMethod" as part of the url, which is incorrect (and it will prevent your UriTemplates from matching). 
     // Since at this point in the exectuion, we know the route url, we can calculate the true base url (excluding all parts of the route url). 
     // This means that any IHttpHandlers that use this routing mechanism will have to look for the "__baseUrl" item in the HttpContext.Current.Items bag. 
     // TODO: Another way to solve this would be to create a subclass of IHttpHandler that has a BaseUrl property that can be set, and only let this route handler 
     // work with instances of the subclass. Perhaps I can just have RestHttpHandler have that property. My reticence is that it would be nice to have a generic 
     // route handler that works for any "plain ol" IHttpHandler (even though in this case, you have to use the "global" base url that's stored in HttpContext.Current.Items...) 
     // Oh well. At least this works for now. 
     httpContext.Items["__baseUrl"] = baseUrl; 

     GenericHandlerRouteHandler<T> routeHandler = new GenericHandlerRouteHandler<T>(); 
     RouteData rdata = new RouteData(this, routeHandler); 

     return rdata; 
    } 


    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) 
    { 
     // This route entry doesn't generate outbound Urls. 
     return null; 
    } 
} 



public class GenericHandlerRouteHandler<T> : IRouteHandler where T : IHttpHandler, new() 
{ 
    public IHttpHandler GetHttpHandler(RequestContext requestContext) 
    { 
     return new T(); 
    } 
} 

Tôi biết câu trả lời này đã được khá dài hơi, nhưng nó không phải là một vấn đề dễ dàng để giải quyết. Logic cốt lõi là đủ dễ dàng, lừa bằng cách nào đó làm cho IHttpHandler của bạn nhận biết "url cơ sở", để nó có thể xác định đúng các phần của url thuộc về tuyến đường, và phần nào là đối số thực tế cho cuộc gọi dịch vụ.

Các lớp này sẽ được sử dụng trong thư viện C# REST sắp tới của tôi, RestCake. Tôi hy vọng rằng con đường của tôi xuống lỗ thỏ định tuyến sẽ giúp bất cứ ai khác quyết định RouteBase, và làm những thứ tuyệt vời với IHttpHandlers.

+1

Đây là một câu trả lời thực sự xuất sắc và chi tiết. – tallseth

4

Vâng, tôi cũng nhận thấy điều đó. Có lẽ có một cách built-in ASP.NET để làm điều này, nhưng thủ đoạn này đối với tôi là để tạo ra một lớp mới có nguồn gốc từ IRouteHandler:

using System; 
using System.IO; 
using System.Reflection; 
using System.Text.RegularExpressions; 
using System.Web; 
using System.Web.Routing; 

namespace MyNamespace 
{ 
    class GenericHandlerRouteHandler : IRouteHandler 
    { 
     private string _virtualPath; 
     private Type _handlerType; 
     private static object s_lock = new object(); 

     public GenericHandlerRouteHandler(string virtualPath) 
     { 
      _virtualPath = virtualPath; 
     } 

     #region IRouteHandler Members 

     public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext) 
     { 
      ResolveHandler(); 

      IHttpHandler handler = (IHttpHandler)Activator.CreateInstance(_handlerType); 
      return handler; 
     } 

     #endregion 

     private void ResolveHandler() 
     { 
      if (_handlerType != null) 
       return; 

      lock (s_lock) 
      { 
       // determine physical path of ashx 
       string path = _virtualPath.Replace("~/", HttpRuntime.AppDomainAppPath); 

       if (!File.Exists(path)) 
        throw new FileNotFoundException("Generic handler " + _virtualPath + " could not be found."); 

       // parse the class name out of the .ashx file 
       // unescaped reg-ex: (?<=Class=")[a-zA-Z\.]* 
       string className; 
       Regex regex = new Regex("(?<=Class=\")[a-zA-Z\\.]*"); 
       using (var sr = new StreamReader(path)) 
       { 
        string str = sr.ReadToEnd(); 

        Match match = regex.Match(str); 
        if (match == null) 
         throw new InvalidDataException("Could not determine class name for generic handler " + _virtualPath); 

        className = match.Value; 
       } 

       // get the class type from the name 
       Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies(); 
       foreach (Assembly asm in asms) 
       { 
        _handlerType = asm.GetType(className); 
        if (_handlerType != null) 
         break; 
       } 

       if (_handlerType == null) 
        throw new InvalidDataException("Could not find type " + className + " in any loaded assemblies."); 
      } 
     } 
    } 
} 

Để tạo một con đường cho một ashx:

IRouteHandler routeHandler = new GenericHandlerRouteHandler("~/somehandler.ashx"); 
Route route = new Route("myroute", null, null, null, routeHandler); 
RouteTable.Routes.Add(route); 

Đoạn mã trên có thể cần được tăng cường để làm việc với các đối số tuyến đường của bạn, nhưng đó là điểm bắt đầu. Bình luận chào mừng.

11

Tôi thực sự thích giải pháp của Joel tốt hơn, vì nó không yêu cầu bạn phải biết loại trình xử lý trong khi bạn đang cố thiết lập tuyến đường của mình. Tôi muốn upvote nó, nhưng than ôi, tôi không có danh tiếng yêu cầu.

Tôi thực sự đã tìm thấy một giải pháp mà tôi cảm thấy tốt hơn cả được đề cập. Tôi có thể tìm thấy mã nguồn ban đầu mà tôi lấy được từ ví dụ http://weblogs.asp.net/leftslipper/archive/2009/10/07/introducing-smartyroute-a-smarty-ier-way-to-do-routing-in-asp-net-applications.aspx.

Đây là mã ít hơn, loại bất khả tri và nhanh.

public class HttpHandlerRoute : IRouteHandler { 

    private String _VirtualPath = null; 

    public HttpHandlerRoute(String virtualPath) { 
    _VirtualPath = virtualPath; 
    } 

    public IHttpHandler GetHttpHandler(RequestContext requestContext) { 
    IHttpHandler httpHandler = (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(_VirtualPath, typeof(IHttpHandler)); 
    return httpHandler; 
    } 
} 

Và một ví dụ thô sử dụng

String handlerPath = "~/UploadHandler.ashx"; 
RouteTable.Routes.Add(new Route("files/upload", new HttpHandlerRoute(handlerPath))); 
+2

Mặc dù bạn có thể không phải biết loại triển khai IHttpHandler, bạn phải biết đường dẫn đến tệp ashx. Giải pháp của tôi không yêu cầu một tệp ashx nào cả, và đó là kiểm tra thời gian biên dịch. Giải pháp của bạn sẽ gây ra lỗi thời gian chạy nếu thiết lập không tốt. Nó chỉ là dễ dàng để tham khảo các loại của một xử lý như nó là để chỉ đường dẫn của ashx. An toàn hơn. Nó dễ hiểu hơn nhiều, và điều đó thật tuyệt. Đơn giản là tốt. Tôi sẽ dính với tôi chỉ như vậy, bởi vì tôi không muốn có tập tin ashx. Tôi chỉ cần tạo một lớp đơn giản thực hiện IHttpHandler. –

4

Tất cả những câu trả lời rất tốt. Tôi yêu sự đơn giản của lớp học của ông Meacham là GenericHandlerRouteHandler<T>. Đó là một ý tưởng tuyệt vời để loại bỏ một tham chiếu không cần thiết đến một đường dẫn ảo nếu bạn biết lớp cụ thể HttpHandler. Tuy nhiên, lớp GenericHandlerRoute<T> là không cần thiết. Lớp hiện tại Route có nguồn gốc từ RouteBase đã xử lý tất cả sự phức tạp của kết hợp tuyến đường, thông số, v.v., vì vậy chúng tôi chỉ có thể sử dụng nó cùng với GenericHandlerRouteHandler<T>.

Dưới đây là phiên bản được kết hợp với ví dụ sử dụng thực tế bao gồm các thông số tuyến đường.

Đầu tiên là các trình xử lý tuyến đường. Có hai được bao gồm, ở đây - cả hai đều có cùng tên lớp, nhưng một cái chung chung và sử dụng thông tin kiểu để tạo một cá thể của HttpHandler cụ thể như trong cách sử dụng của Mr. Meacham, và sử dụng đường dẫn ảo và BuildManager để tạo một phiên bản của HttpHandler thích hợp như trong cách sử dụng của shellscape. Tin tốt là .NET cho phép cả hai sống cạnh nhau tốt, vì vậy chúng ta chỉ có thể sử dụng bất cứ cái gì chúng ta muốn và có thể chuyển đổi giữa chúng như chúng ta mong muốn.

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)); 
    } 

} 

Giả sử rằng chúng tôi tạo ra một HttpHandler mà dòng văn bản cho người sử dụng từ một nguồn bên ngoài thư mục ảo của chúng tôi, thậm chí có thể từ một cơ sở dữ liệu, và rằng chúng ta muốn đánh lừa trình duyệt của người dùng tin rằng chúng tôi đang trực tiếp phục vụ tệp cụ thể thay vì chỉ cung cấp tải xuống (nghĩa là cho phép trình cắm của trình duyệt xử lý tệp thay vì buộc người dùng lưu tệp). HttpHandler có thể mong đợi một id tài liệu để xác định vị trí tài liệu cần cung cấp và có thể mong đợi tên tệp để cung cấp cho trình duyệt - một tên có thể khác với tên tệp được sử dụng trên máy chủ.

Sau đây cho thấy việc đăng ký các tuyến đường sử dụng để thực hiện điều này với một DocumentHandlerHttpHandler:

routes.Add("Document", new Route("document/{documentId}/{*fileName}", new HttpHandlerRouteHandler<DocumentHandler>())); 

tôi đã sử dụng {*fileName} thay vì chỉ {fileName} để cho phép các tham số fileName để hoạt động như một nhận tất cả tùy chọn tham số.

Để tạo một URL cho một tập tin phục vụ bởi HttpHandler này, chúng ta có thể thêm các phương pháp tĩnh sau để một lớp học nơi một phương pháp như vậy sẽ phù hợp, ví dụ như trong lớp HttpHandler, bản thân:

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; 
} 

Tôi bỏ qua các định nghĩa của MimeMap và và IsPassThruMimeType để giữ ví dụ này đơn giản. Nhưng chúng nhằm xác định xem các loại tệp cụ thể có nên cung cấp tên tệp của họ trực tiếp trong URL hay không trong tiêu đề HTTP Content-Disposition. Một số phần mở rộng tập tin có thể bị chặn bởi IIS hoặc Quét URL, hoặc có thể làm cho mã thực thi có thể gây ra sự cố cho người dùng - đặc biệt nếu nguồn của tệp là một người dùng độc hại khác. Bạn có thể thay thế logic này bằng một số logic lọc khác, hoặc bỏ qua logic đó hoàn toàn nếu bạn không bị loại rủi ro này.

Vì trong ví dụ cụ thể này tên tệp có thể bị bỏ qua khỏi URL, sau đó, hiển nhiên, chúng tôi phải truy xuất tên tệp từ đâu đó. Trong ví dụ cụ thể này, tên tệp có thể được truy xuất bằng cách thực hiện tra cứu bằng id tài liệu và bao gồm tên tệp trong URL chỉ nhằm mục đích cải thiện trải nghiệm của người dùng. Vì vậy, DocumentHandlerHttpHandler có thể xác định xem tên tệp có được cung cấp trong URL hay không và nếu không, thì có thể chỉ cần thêm tiêu đề HTTP Content-Disposition vào phản hồi.

Ở về chủ đề, một phần quan trọng của khối mã trên là việc sử dụng RouteTable.Routes.GetVirtualPath() và các thông số định tuyến để tạo ra một URL từ đối tượng Route mà chúng tôi tạo ra trong quá trình đăng ký lộ trình.

Đây là phiên bản được tưới nước của lớp DocumentHandlerHttpHandler (bị bỏ qua nhiều vì lợi ích của sự rõ ràng). Bạn có thể thấy rằng lớp này sử dụng các tham số tuyến đường để lấy id tài liệu và tên tệp khi có thể; nếu không, nó sẽ cố gắng truy lục id tài liệu từ tham số chuỗi truy vấn (tức là, giả định rằng định tuyến không được sử dụng).

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. 
    } 
} 

Ví dụ này sử dụng một số lớp tùy chỉnh bổ sung, chẳng hạn như lớp Utility để đơn giản hóa một số nhiệm vụ tầm thường. Nhưng hy vọng bạn có thể vượt qua điều đó. Phần thực sự quan trọng trong lớp này liên quan đến chủ đề hiện tại, tất nhiên, là việc thu hồi các tham số tuyến đường từ context.Request.RequestContext.RouteData. Nhưng tôi đã thấy một số bài đăng ở nơi khác yêu cầu cách truyền các tệp lớn bằng cách sử dụng HttpHandler mà không cần nhai bộ nhớ máy chủ, do đó, có vẻ như một ý tưởng hay là kết hợp các ví dụ.

6

CHỈNH SỬA: Tôi vừa chỉnh sửa mã này vì tôi có một số vấn đề với mã cũ. Nếu bạn đang sử dụng phiên bản cũ, vui lòng cập nhật.

Chủ đề này hơi cũ nhưng tôi đã viết lại một số mã ở đây để làm điều tương tự nhưng theo cách thanh lịch hơn, sử dụng phương pháp mở rộng.

Tôi đang sử dụng trên ASP.net Webforms và tôi muốn có tệp ashx trên một thư mục và có thể gọi cho họ bằng cách sử dụng định tuyến hoặc yêu cầu bình thường.

Vì vậy, tôi lấy khá nhiều mã của shellscape và tạo một phương thức mở rộng thực hiện thủ thuật. Cuối cùng tôi cảm thấy rằng tôi cũng nên hỗ trợ truyền đối tượng IHttpHandler thay vì Url của nó, vì vậy tôi đã viết và quá tải phương thức MapHttpHandlerRoute cho điều đó.

namespace System.Web.Routing 
{ 
public class HttpHandlerRoute<T> : IRouteHandler where T: IHttpHandler 
{ 
    private String _virtualPath = null; 

    public HttpHandlerRoute(String virtualPath) 
    { 
    _virtualPath = virtualPath; 
    } 

    public HttpHandlerRoute() { } 

    public IHttpHandler GetHttpHandler(RequestContext requestContext) 
    { 
    return Activator.CreateInstance<T>(); 
    } 
} 

public class HttpHandlerRoute : IRouteHandler 
{ 
    private String _virtualPath = null; 

    public HttpHandlerRoute(String virtualPath) 
    { 
    _virtualPath = virtualPath; 
    } 

    public IHttpHandler GetHttpHandler(RequestContext requestContext) 
    { 
    if (!string.IsNullOrEmpty(_virtualPath)) 
    { 
    return (IHttpHandler)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(IHttpHandler)); 
    } 
    else 
    { 
    throw new InvalidOperationException("HttpHandlerRoute threw an error because the virtual path to the HttpHandler is null or empty."); 
    } 
    } 
} 

public static class RoutingExtension 
{ 
    public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, string physicalFile, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null) 
    { 
    var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(physicalFile)); 
    routes.Add(routeName, route); 
    } 

    public static void MapHttpHandlerRoute<T>(this RouteCollection routes, string routeName, string routeUrl, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null) where T : IHttpHandler 
    { 
    var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute<T>()); 
    routes.Add(routeName, route); 
    } 
} 
} 

Tôi đặt nó vào cùng một không gian tên của tất cả các đối tượng định tuyến gốc để nó sẽ tự động có sẵn.

Vì vậy, để sử dụng này, bạn chỉ phải gọi:

// using the handler url 
routes.MapHttpHandlerRoute("DoSomething", "Handlers/DoSomething", "~/DoSomething.ashx"); 

Hoặc

// using the type of the handler 
routes.MapHttpHandlerRoute<MyHttpHanler>("DoSomething", "Handlers/DoSomething"); 

Thưởng thức, Alex

+0

Cảm ơn Alex, tôi đang sử dụng các phương pháp của bạn mà họ trông sạch sẽ và làm việc tuyệt vời cho tôi. –

+0

Xin chào bạn đời, Cảm ơn bạn đã phản hồi. Tôi vừa cập nhật mã này do sự cố khi lưu trữ yêu cầu. Vui lòng sử dụng phiên bản mới này. Nếu bạn muốn theo dõi hãy xem blog của tôi: http://www.instanceofanobject.com Cảm ơn! – AlexCode

+0

Cảm ơn Alex đã cập nhật, tôi đang sử dụng mã được cập nhật ngay bây giờ. –

Các vấn đề liên quan