返回自定义IHttpHandler中的默认路由处理程序

时间:2014-03-02 17:08:48

标签: asp.net-mvc webforms routes ihttphandler

在上一个问题How to route web pages on a mixed MVC and Web Forms中,我试图进一步扩展我将所有Web窗体(* .aspx)路由到网站上的特定子文件夹的想法。基本思想是检查所有请求以查看它们是否映射到指定的“Web表单页面”文件夹中的现有.aspx页面。例如,如果所有.aspx页面都存在于'〜/ WebPages'的文件夹结构中......

  

/MyPage.aspx => /WebPages/MyPage.aspx

     

/SubFolder/MyotherPage.aspx => /WebPages/SubFolder/MyOtherPage.aspx

此外,我想通过删除.aspx扩展来简化URL,所以

  

/ MyPage => /网页/我的页面

     

/SubFolder/MyotherPage.aspx => /WebPages/SubFolder/MyotherPage.aspx

为此,我需要考虑每个请求,因为我认为不可能定义特定的路由或约束。为此,我实现了以下路由配置。

路线配置

public class RouteConfig
{
   public static void RegisterRoutes(RouteCollection routes)
   {
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

      routes.Add(
         "rootforms",
         new Route(
            "{*pathInfo}",
            new DirectoryRouteHandler(virtualDir: "~/WebPages")
         )
      );

      routes.MapRoute(
         name: "Default",
         url: "{controller}/{action}/{id}",
         defaults: new
         {
               controller = "Home",
               action = "Index",
               id = UrlParameter.Optional
         },
         namespaces: new[] {
               "MultiApp.Web.Controllers"
         }
      );

   }
}

并定义了自定义路由处理程序,如下所示。

IRouteHandler

public class DirectoryRouteHandler : IRouteHandler
{
   private readonly string _virtualDir;

   public DirectoryRouteHandler(string virtualDir)
   {
      _virtualDir = virtualDir;
   }

   public IHttpHandler GetHttpHandler(RequestContext requestContext)
   {
      var routeValues = requestContext.RouteData.Values;

      if (!routeValues.ContainsKey("pathInfo"))
      {
         return null; /* this doesn't work - must be a RouteHandler */;
      }

      var path = routeValues["pathInfo"] as string;

      if (String.IsNullOrWhiteSpace(path))
      {
         path = "Default.aspx";
      }

      // add the .aspx extension if required    
      if (!path.EndsWith(".aspx")) { path += ".aspx"; }

      // build the test path    
      var pageVirtualPath = string.Format("{0}/{1}", _virtualDir, path);

      string filePath = requestContext.HttpContext.Server.MapPath(pageVirtualPath);

      // check to see if the physical .aspx file exists, if not exit this handler
      if (!File.Exists(filePath))
      {
         return null; /* this doesn't work - must be a RouteHandler */;
      }

      return new PageRouteHandler(pageVirtualPath, true).GetHttpHandler(requestContext);
   }
}

我希望能够做的是返回一个PageRouteHandler对象(如前一个问题所示),或退出此处理程序,将责任传递回默认路由机制。

这是可能的,还是我完全吠叫了错误的树?我只是注册一个简单的IHttpHandler并单独留下路由会更好吗?

1 个答案:

答案 0 :(得分:0)

我想我找到了解决方法。不是通过管理路由来尝试实现此功能,而是使用自定义HttpModule。这是代码。

HTTP模块

public class WebFormsHttpHandler
   : IHttpModule
{

   // All Web Form (*.aspx) pages will exist in this folder hierarchy
   public const string WebFormsRootFolder = "~/WebPages";


   #region Constructor
   public WebFormsHttpHandler()
   {
   }
   #endregion Constructor


   #region IHttpModule Methods
   public void Dispose()
   {
      /* Nothing to dispose */
   }
   #endregion Constructor


   // Wire up the interesting events
   public void Init(HttpApplication context)
   {
      context.BeginRequest += context_BeginRequest;
      context.AuthorizeRequest += context_AuthorizeRequest;
      context.PreRequestHandlerExecute += context_PreRequestHandlerExecute;
   }


   #region BeginRequest
   // Capture the original request path so we can restore it to the browser
   // in the PreRequestHandlerExecute event if required
   void context_BeginRequest(object sender, EventArgs e)
   {
      var context = ((HttpApplication)sender).Context;

      var path = context.Request.Url.AbsolutePath;

      context.Items["originalUrl"] = path;
   }
   #endregion BeginRequest


   #region AuthorizeRequest
   void context_AuthorizeRequest(object sender, EventArgs e)
   {
      var context = ((HttpApplication)sender).Context;

      string requestPath = context.Request.Url.AbsolutePath ?? "";
      string filePath = context.Server.MapPath(requestPath);

      // Check for physical existence
      if (File.Exists(filePath))
      {
         return;
      }

      var virtualPath = WebFormsRootFolder;
      if (!requestPath.StartsWith("/")) { virtualPath += "/"; }
      virtualPath += requestPath;

      filePath = context.Server.MapPath(virtualPath);

      // Is the virtualPath a directory?
      if (Directory.Exists(filePath))
      {
         if (!virtualPath.EndsWith("/")) { virtualPath += "/"; }
         virtualPath += "Default.aspx";
      }
      else if (!virtualPath.EndsWith(".aspx")) {
         virtualPath += ".aspx";
      }

      filePath = context.Server.MapPath(virtualPath);

      if (!File.Exists(filePath))
      {
         return;
      }

      /*
      ** Should I set a context.Response.StatusCode here?
      */

      context.RewritePath(virtualPath);
   }
   #endregion AuthorizeRequest


   #region PreRequestHandlerExecute
   // Restore the original request path on the browser - the user doesn't
   // need to know what's happened
   void context_PreRequestHandlerExecute(object sender, EventArgs e)
   {
      var context = ((HttpApplication)sender).Context;

      string originalUrl = context.Items["originalUrl"] as string;

      if (originalUrl != null)
      {
         context.RewritePath(originalUrl);
      }
   }
   #endregion PreRequestHandlerExecute

}

在Web.config中注册HttpModule

<system.webServer>
    <modules>
        <add name="WebFormsHttpHandler" type="MultiApp.Web.WebFormsHttpHandler" />
    </modules>
</system.webServer>

样本/测试路由配置

public static void RegisterRoutes(RouteCollection routes)
{
   routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

   // WebForm Route Definitions
   routes.MapPageRoute(
      "product-details",
      "product/{productid}",
      "~/WebPages/SubFolder/Product.aspx"
   );

   // MVC Route Definitions
   routes.MapRoute(
      name: "Default",
      url: "{controller}/{action}/{id}",
      defaults: new
      {
         controller = "Home",
         action = "Index",
         id = UrlParameter.Optional
      },
      namespaces: new[] {
         "MultiApp.Web.Controllers"
      }
   );

}

我已经使用各种各样的URL构造对其进行了测试,它似乎可以按要求运行。如果有人有任何改进建议,请告诉我。

我要感谢Rahul Rajat Singh的项目Implementing HTTPHandler and HTTPModule in ASP.NET,这是一个很大的帮助。