// 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的路由,那将是巨大的。
解决方法
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做酷的东西。