在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框架下也未改变。this

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

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

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

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

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

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方法get

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

完整的实现代码:string

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

 第三步:在控制器的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作的修改都不能被保留,由于执行的是一个新的控制器实例。

相关文章
相关标签/搜索