How to simulate Server.Transfer in ASP.NET MVC?

后端 未结 14 1402
死守一世寂寞
死守一世寂寞 2020-11-22 12:00

In ASP.NET MVC you can return a redirect ActionResult quite easily :

 return RedirectToAction(\"Index\");

 or

 return RedirectToRoute(new { controller = \"         


        
相关标签:
14条回答
  • 2020-11-22 12:44

    I wanted to re-route the current request to another controller/action, while keeping the execution path exactly the same as if that second controller/action was requested. In my case, Server.Request wouldn't work because I wanted to add more data. This is actually equivalent the current handler executing another HTTP GET/POST, then streaming the results to the client. I'm sure there will be better ways to achieve this, but here's what works for me:

    RouteData routeData = new RouteData();
    routeData.Values.Add("controller", "Public");
    routeData.Values.Add("action", "ErrorInternal");
    routeData.Values.Add("Exception", filterContext.Exception);
    
    var context = new HttpContextWrapper(System.Web.HttpContext.Current);
    var request = new RequestContext(context, routeData);
    
    IController controller = ControllerBuilder.Current.GetControllerFactory().CreateController(filterContext.RequestContext, "Public");
    controller.Execute(request);
    

    Your guess is right: I put this code in

    public class RedirectOnErrorAttribute : ActionFilterAttribute, IExceptionFilter
    

    and I'm using it to display errors to developers, while it'll be using a regular redirect in production. Note that I didn't want to use ASP.NET session, database, or some other ways to pass exception data between requests.

    0 讨论(0)
  • 2020-11-22 12:46

    How about a TransferResult class? (based on Stans answer)

    /// <summary>
    /// Transfers execution to the supplied url.
    /// </summary>
    public class TransferResult : ActionResult
    {
        public string Url { get; private set; }
    
        public TransferResult(string url)
        {
            this.Url = url;
        }
    
        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
    
            var httpContext = HttpContext.Current;
    
            // MVC 3 running on IIS 7+
            if (HttpRuntime.UsingIntegratedPipeline)
            {
                httpContext.Server.TransferRequest(this.Url, true);
            }
            else
            {
                // Pre MVC 3
                httpContext.RewritePath(this.Url, false);
    
                IHttpHandler httpHandler = new MvcHttpHandler();
                httpHandler.ProcessRequest(httpContext);
            }
        }
    }
    

    Updated: Now works with MVC3 (using code from Simon's post). It should (haven't been able to test it) also work in MVC2 by looking at whether or not it's running within the integrated pipeline of IIS7+.

    For full transparency; In our production environment we've never use the TransferResult directly. We use a TransferToRouteResult which in turn calls executes the TransferResult. Here's what's actually running on my production servers.

    public class TransferToRouteResult : ActionResult
    {
        public string RouteName { get;set; }
        public RouteValueDictionary RouteValues { get; set; }
    
        public TransferToRouteResult(RouteValueDictionary routeValues)
            : this(null, routeValues)
        {
        }
    
        public TransferToRouteResult(string routeName, RouteValueDictionary routeValues)
        {
            this.RouteName = routeName ?? string.Empty;
            this.RouteValues = routeValues ?? new RouteValueDictionary();
        }
    
        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
    
            var urlHelper = new UrlHelper(context.RequestContext);
            var url = urlHelper.RouteUrl(this.RouteName, this.RouteValues);
    
            var actualResult = new TransferResult(url);
            actualResult.ExecuteResult(context);
        }
    }
    

    And if you're using T4MVC (if not... do!) this extension might come in handy.

    public static class ControllerExtensions
    {
        public static TransferToRouteResult TransferToAction(this Controller controller, ActionResult result)
        {
            return new TransferToRouteResult(result.GetRouteValueDictionary());
        }
    }
    

    Using this little gem you can do

    // in an action method
    TransferToAction(MVC.Error.Index());
    
    0 讨论(0)
提交回复
热议问题