In an ASP. NET MVC application, if you use the Server.Transfer () method to forward a request to a different path or an HTTP handler for processing, a "error occurred while performing a child request for XXX" will be thrown The HttpException exception. And inside the method that finally implements the Server.Transfer () operation, I see a few lines of code.
is Page) { new HttpException (string. Empty);}
Obviously, within a method, all IHttpHandler are treated as page types. Throws an exception if the incoming handler is not of the page type! Even if your incoming URL or IHttpHandler corresponds to a physical ashx file, it is no exception. And this approach has not changed under the framework of. NET 4.5.
We know that in the ASP. All HTTP requests are processed from the IHttpHandler ProcessRequest () method In addition to WebService, and in MVC mode This gives us the conditions that are similar to Server.Transfer () in our own implementation. We just have to get an instance of the IHttpHandler that is used to process the new request and make the appropriate changes to the context of the request so that we can call ProcessRequest to force the processing of the request to be executed in the new IHttpHandler.
In an ASP. NET MVC program, the IHttpHandler of processing the request is obtained from the Iroutehandler Gethttphandler () method, so in order to get a new handler from this method, we must create a new routing request context information " RequestContext ".
First step: Get the route instance where the target request handler is located and create a new routing data (Routedata):
There are two ways to get a routed instance, one from the routing table based on the route name, and one to create a new route instance based on the URL, and the route handler. In both of these ways, you must give explicit values for the route parameters that are used to determine the destination URL for the route.
First approach: Generate route data based on route name and value
Public voidToroute (stringRouteName,Objectvalues) { //Get the routing instance varRoute =Routetable.routes[routename]; if(Route = =NULL) Throw NewException (string. Format ("a route named \ ' {0}\ ' does not exist in the routing table", RouteName)); //Creating routing Data varRoutedata =NewRoutedata (Route,NewMvcroutehandler ()); //Add route parameter/value foreach(varPairinch NewRouteValueDictionary (values)) {Routedata.values[pair. Key]=pair. Value; } Route (Routedata); }
The second way: Generate route data based on the given URL and value. ( in an ASP. NET MVC application, the implementation of Iroutehandler is System.Web.Mvc.MvcRouteHandler)
Public voidTourl (stringUrlObjectvalues) { //To create a route handler instance varRoutehandler =NewMvcroutehandler (); //Creating routing Data varRoutedata =NewRoutedata (NewRoute (URL, routehandler), Routehandler); //Add route parameter/value foreach(varPairinch NewRouteValueDictionary (values)) {Routedata.values[pair. Key]=pair. Value; } Route (Routedata); }
Step Two: Create a new routing request context information (RequestContext), override the internal path, get the IHttpHandler, and call its ProcessRequest method
voidRoute (Routedata routedata) {varRequestContext =NewRequestContext (Context, routedata); //overriding the internal request path varNewPath = RouteData.Route.GetVirtualPath (RequestContext,NULL). virtualpath; RequestContext.HttpContext.RewritePath (NewPath); //get handlers, process requestsIHttpHandler handler =RouteData.RouteHandler.GetHttpHandler (RequestContext); if(Handler = =NULL) Throw NewException ("failed to get IHttpHandler from the specified route"); Handler. ProcessRequest (HttpContext.Current); }
The complete implementation code:
usingSYSTEM.WEB.MVC; usingSystem.Web.Routing; Public classRequestrouter {ReadOnlyhttpcontextbase _context; Publichttpcontextbase Context {Get{return This. _context;} } PublicRequestrouter (httpcontextbase context) { This. _context =context; } voidRoute (Routedata routedata) {varRequestContext =NewRequestContext (Context, routedata); //overriding the internal request path varNewPath = RouteData.Route.GetVirtualPath (RequestContext,NULL). virtualpath; RequestContext.HttpContext.RewritePath (NewPath); //get handlers, process requestsIHttpHandler handler =RouteData.RouteHandler.GetHttpHandler (RequestContext); if(Handler = =NULL) Throw NewException ("failed to get IHttpHandler from the specified route"); Handler. ProcessRequest (HttpContext.Current); } Public voidToroute (stringRouteName,Objectvalues) { //Get the routing instance varRoute =Routetable.routes[routename]; if(Route = =NULL) Throw NewException (string. Format ("a route named \ ' {0}\ ' does not exist in the routing table", RouteName)); //Creating routing Data varRoutedata =NewRoutedata (Route,NewMvcroutehandler ()); //Add route parameter/value foreach(varPairinch NewRouteValueDictionary (values)) {Routedata.values[pair. Key]=pair. Value; } Route (Routedata); } Public voidTourl (stringUrlObjectvalues) { //To create a route handler instance varRoutehandler =NewMvcroutehandler (); //Creating routing Data varRoutedata =NewRoutedata (NewRoute (URL, routehandler), Routehandler); //Add route parameter/value foreach(varPairinch NewRouteValueDictionary (values)) {Routedata.values[pair. Key]=pair. Value; } Route (Routedata); } }
Step three: Forward the request in the action of the Controller
public ActionResult Index () { var routerequest = Requestrouter ( HttpContext); Routerequest.toroute ( default , new {controller = " home " , action = " about " return new Emptyresult ();}
In this way, after requesting the index action method in the controller above, the request is forwarded to the Home controller's about operation method, and all request-related data (forms,querystrings) is preserved. However, changes made to ViewData and viewbag in the action that forwarded the request cannot be persisted because a new controller instance is being executed.
Implement Server.Transfer () similar functionality in an ASP. NET MVC Application