zoukankan      html  css  js  c++  java
  • 在ASP.NET MVC应用程序中实现Server.Transfer()类似的功能

      在ASP.NET MVC应用程序中,如果使用Server.Transfer()方法希望将请求转发到其它路径或者Http处理程序进行处理,都会引发“为xxx执行子请求时出错”的HttpException异常。而在最终实现Server.Transfer()操作的方法内部,我看到这样几行代码。

    else if (!(handler is Page))
    {
            error = new HttpException(0x194, string.Empty);
    }

      很明显,在方法内部,所有的IHttpHandler都将被当作Page类型来处理。如果传入的处理程序不是Page类型则引发异常!即使是你传入的Url或IHttpHandler对应一个物理的ashx文件也不例外。而且这种做法在.NET 4.5框架下也未改变。

      我们知道在ASP.NET程序中,除了WebService,所有对Http请求的处理都是从IHttpHandler的ProcessRequest()方法开始的,在MVC模式下也是如此;这样就给我们自已实现类似于Server.Transfer()这样的提供了条件。我们只要得到用于处理新请求的IHttpHandler的实例,并对请求的上下文做适当的修改,这样我们就可以调用ProcessRequest来强制把请求的处理切换到新的IHttpHandler中执行。

      在ASP.NET MVC程序中,处理请求的IHttpHandler是从IRouteHandler的GetHttpHandler()方法获取到的;所以为了能从这个方法得到新的处理程序,我们必须创建新的路由请求上下文信息"RequestContext"。

      第一步:得到目标请求处理程序所在的路由(Route)实例,创建新的路由数据(RouteData):

      得到路由实例的方式有两种,一种是根据路由名称从路由表中获取;一种是根据Url、和路由处理程序,创建新的路由实例。在以上两种方式里,都必须给出明确的路由参数的值,这些值用于决定路由的目标Url。

      第一种方式:根据路由名称和值生成路由数据

            public void ToRoute(string routeName, object values)
            {
                //获得路由实例
                var route = RouteTable.Routes[routeName];
                if (route == null)
                    throw new Exception(string.Format("路由表中不存在名为 "{0}" 的路由", routeName));
                //创建路由数据
                var routeData = new RouteData(route, new MvcRouteHandler());
                //添加路由参数/值
                foreach (var pair in new RouteValueDictionary(values))
                {
                    routeData.Values[pair.Key] = pair.Value;
                }
                Route(routeData);
            }

      第二种方式:根据给定的URL和值生成路由数据。(在ASP.NET MVC应用程序中,IRouteHandler的实现即是System.Web.Mvc.MvcRouteHandler

            public void ToUrl(string url, object values)
            {
                //创建路由处理程序实例
                var routeHandler = new MvcRouteHandler();
                //创建路由数据
                var routeData = new RouteData(new Route(url, routeHandler), routeHandler);
                //添加路由参数/值
                foreach (var pair in new RouteValueDictionary(values))
                {
                    routeData.Values[pair.Key] = pair.Value;
                }
                Route(routeData);
            }

      第二步:创建新的路由请求上下文信息(RequestContext),重写内部路径,获取IHttpHandler并调用它的ProcessRequest方法

            void Route(RouteData routeData)
            {
                var requestContext = new RequestContext(Context, routeData);
    
                //重写内部请求路径
                var newPath = routeData.Route.GetVirtualPath(requestContext, null).VirtualPath;
                requestContext.HttpContext.RewritePath(newPath);
    
                //获取处理程序,处理请求
                IHttpHandler handler = routeData.RouteHandler.GetHttpHandler(requestContext);
                if (handler == null)
                    throw new Exception("未能从指定路由中获取到 IHttpHandler");
                handler.ProcessRequest(HttpContext.Current);
            }

      完整的实现代码:

        using System.Web.Mvc;
        using System.Web.Routing;
    
        public class RequestRouter
        {
            readonly HttpContextBase _Context;
    
            public HttpContextBase Context
            {
                get { return this._Context; }
            }
    
            public RequestRouter(HttpContextBase context)
            {
                this._Context = context;
            }
    
            void Route(RouteData routeData)
            {
                var requestContext = new RequestContext(Context, routeData);
    
                //重写内部请求路径
                var newPath = routeData.Route.GetVirtualPath(requestContext, null).VirtualPath;
                requestContext.HttpContext.RewritePath(newPath);
    
                //获取处理程序,处理请求
                IHttpHandler handler = routeData.RouteHandler.GetHttpHandler(requestContext);
                if (handler == null)
                    throw new Exception("未能从指定路由中获取到 IHttpHandler");
                handler.ProcessRequest(HttpContext.Current);
            }
    
            public void ToRoute(string routeName, object values)
            {
                //获得路由实例
                var route = RouteTable.Routes[routeName];
                if (route == null)
                    throw new Exception(string.Format("路由表中不存在名为 "{0}" 的路由", routeName));
                //创建路由数据
                var routeData = new RouteData(route, new MvcRouteHandler());
                //添加路由参数/值
                foreach (var pair in new RouteValueDictionary(values))
                {
                    routeData.Values[pair.Key] = pair.Value;
                }
                Route(routeData);
            }
    
            public void ToUrl(string url, object values)
            {
                //创建路由处理程序实例
                var routeHandler = new MvcRouteHandler();
                //创建路由数据
                var routeData = new RouteData(new Route(url, routeHandler), routeHandler);
                //添加路由参数/值
                foreach (var pair in new RouteValueDictionary(values))
                {
                    routeData.Values[pair.Key] = pair.Value;
                }
                Route(routeData);
            }
        }
    View Code

      第三步:在控制器的Action中转发请求

    public ActionResult Index()
    {
        var routeRequest = new RequestRouter(HttpContext);
        routeRequest.ToRoute("Default", new { controller = "Home", action = "About" });
        return new EmptyResult();
    }

      这样一来,请求上面的控制器中的Index操作方法之后,请求被转发到 Home 控制器的 About 操作方法,而且所有请求相关的数据(Forms,QueryStrings)都被保留了下来。不过,在转发请求的Action中对ViewData和ViewBag做的修改都不能被保留,因为执行的是一个新的控制器实例。

  • 相关阅读:
    linux tcp GSO和TSO实现
    CentOS 6.8 源码安装mysql 5.6
    MySQL主从复制配置
    PHP超级全局变量、魔术变量和魔术函数
    工作中常用的正则表达式
    CentOS下编译安装LNMP环境
    解决<IE9版本不支持getElementsByClassName方法
    js 回调函数
    Firefox下table单元格td设计relative定位失效解决方案
    jQuery的.live()和.die()
  • 原文地址:https://www.cnblogs.com/shalves/p/Achieve_ServerTransfer_Method_In_ASPNET_MVC.html
Copyright © 2011-2022 走看看