可以使用ASP.NET路由为.ashx(IHttpHander)处理程序创建“干净”的URL吗?

我有一些REST服务使用简单的老式IHttpHandlers。我想要生成更干净的URL,这样我就没有路径中的.ashx。有没有办法使用ASP.NET路由来创建映射到ashx处理程序的路由?我以前看过这些类型的路由:

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

尝试使用RouteTable.Routes.MapPageRoute()生成错误(处理程序不从页面派生)。 System.Web.Routing.RouteBase只有两个派生类:ServiceRoute用于服务,DynamicDataRoute用于MVC。我不知道MapPageRoute()是做什么的(反射器没有显示方法体,它只是显示“性能关键到内联这种类型的方法跨越NGen图像边界”)。

我看到RouteBase没有密封,并且有一个比较简单的界面:

public abstract RouteData GetRouteData(HttpContextBase httpContext);

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

所以也许我可以做自己的HttpHandlerRoute。我会给出一个镜头,但是如果有人知道现有或内置的映射到IHttpHandlers的路由,那将是巨大的。

好的,我一直在想这个,因为我原来问这个问题,我终于有一个解决方案,只是我想要的。然而,有一些前面的解释是由于。 IHttpHandler是一个非常基本的界面:

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

没有内置的访问路径数据的属性,路由数据也不能在上下文或请求中找到。 System.Web.UI.Page对象具有RouteData属性,ServiceRoutes执行解释您的UriTemplates的所有工作,并将值传递到内部正确的方法,ASP.NET MVC提供了自己访问路由数据的方式。即使您有一个RouteBase,(a)确定传入的URL是否与您的路由匹配,(b)解析该URL以从您的IHttpHandler中提取要使用的所有个别值,则没有简单的方法可以通过路由数据到您的IHttpHandler。如果你想保持你的IHttpHandler“纯”,可以这么说,它负责处理url,以及如何从中提取任何值。在这种情况下,RouteBase实现仅用于确定是否应该使用您的IHttpHandler。

然而,一个问题依然存在。一旦RouteBase确定传入的URL与您的路由匹配,就会传递给一个IRouteHandler,它会创建要处理您的请求的IHttpHandler的实例。但是,一旦你在你的IHttpHandler,context.Request.CurrentExecutionFilePath的价值是误导的。这是来自客户端的URL,减去查询字符串。所以这不是你的.ashx文件的路径。而且,您的路由的任何部分(如方法的名称)都将是该路径的一部分,这些部分将是执行文件路径值的一部分。如果您在IHttpHandler中使用UriTemplates来确定IHttpHandler中哪个特定方法应该处理请求,那么这可能是一个问题。

示例:如果您在/myApp/services/myHelloWorldHandler.ashx中有.ashx处理程序
你有这条路线映射到处理程序:“services / hello / {name}”
你导航到这个url,试图调用你的处理程序的SayHello(string name)方法:
http://localhost/myApp/services/hello/SayHello/Sam

那么你的CurrentExecutionFilePath将是:/ myApp / services / hello / Sam。它包含路由url的一部分,这是一个问题。您希望执行文件路径与您的路由网址匹配。 RouteBase和IRouteHandler的以下实现处理这个问题。

在粘贴2个类之前,这里有一个非常简单的用法示例。请注意,RouteBase和IRouteHandler的这些实现将实际上适用于甚至没有.ashx文件的IHttpHandlers,这是非常方便的。

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

这将导致所有匹配“服务/无头”路由的传入URL被切换到HeadlessService IHttpHandler的新实例(HeadlessService只是这种情况下的一个例子,这将是您想要传递的任何IHttpHandler实现) 。

好的,所以这里是路由类的实现,注释和全部:

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

我知道这个答案已经很长时间了,但这不是一个容易解决的问题。核心逻辑是很容易的,诀窍在某种程度上使您的IHttpHandler知道“基本URL”,以便它可以正确地确定url的哪些部分属于路由,哪些部分是服务调用的实际参数。

这些类将用于我即将到来的C#REST库,RestCake.我希望我的路径下的路径兔洞可以帮助决定RouteBase的任何人,并使用IHttpHandlers做酷的东西。

http://stackoverflow.com/questions/3359816/can-asp-net-routing-be-used-to-create-clean-urls-for-ashx-ihttphander-handl

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:可以使用ASP.NET路由为.ashx(IHttpHander)处理程序创建“干净”的URL吗?