如何在ASP.NET MVC中模拟Server.Transfer?

Sim*_*ver 123 asp.net-mvc server.transfer

在ASP.NET MVC中,您可以非常轻松地返回重定向ActionResult:

 return RedirectToAction("Index");

 or

 return RedirectToRoute(new { controller = "home", version = Math.Random() * 10 });
Run Code Online (Sandbox Code Playgroud)

这实际上会提供HTTP重定向,这通常很好.但是,当使用谷歌分析时,这会导致很大的问题,因为最初的引用会丢失,所以谷歌不知道你来自哪里.这会丢失有用的信息,例如任何搜索引擎术语.

作为旁注,此方法的优点是可以删除任何可能来自广告系列但仍允许我捕获服务器端的参数.将它们留在查询字符串中会导致人们加入书签或推特或博客链接,而这些链接不应该存在.我已经多次看到这样的情况,人们在我们的网站上链接了包含广告系列ID的链接.

无论如何,我正在为网站的所有传入访问写一个"网关"控制器,我可能会重定向到不同的地方或替代版本.

现在我更关心谷歌(比意外的书签),我希望能够发送访问/该页面的人,如果他们去了/home/7,这是主页的第7版.

就像我之前说的如果我这样做,我失去了谷歌分析引用者的能力:

 return RedirectToAction(new { controller = "home", version = 7 });
Run Code Online (Sandbox Code Playgroud)

我真正想要的是一个

 return ServerTransferAction(new { controller = "home", version = 7 });
Run Code Online (Sandbox Code Playgroud)

这将使我看到没有客户端重定向的视图.我不认为这样的事情存在.

目前我能想到的最好的事情是HomeController.Index(..)在我的GatewayController.IndexAction中复制整个控制器逻辑.这意味着我必须'Views/Home'进入,'Shared'因此它是可访问的.肯定有更好的办法??..

Mar*_*son 130

TransferResult类怎么样?(根据斯坦斯回答)

/// <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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:现在可以使用MVC3(使用Simon的帖子中的代码).它应该(无法测试)也可以通过查看它是否在IIS7 +的集成管道中运行来在MVC2中工作.

为了完全透明; 在我们的生产环境中,我们从不直接使用TransferResult.我们使用TransferToRouteResult,然后调用执行TransferResult.这是我的生产服务器上实际运行的内容.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您正在使用T4MVC(如果不是......那么!),这个扩展可能会派上用场.

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

使用这个小宝石,你可以做到

// in an action method
TransferToAction(MVC.Error.Index());
Run Code Online (Sandbox Code Playgroud)

  • @BradLaney:您可以删除'var urlHelper ...'和'var url ...'行,并将'url'替换为'this.Url',其余部分也可以.:) (2认同)

Sim*_*ver 47

编辑:已更新以与ASP.NET MVC 3兼容

如果您使用的是IIS7,则以下修改似乎适用于ASP.NET MVC 3.感谢@nitin和@andy指出原始代码不起作用.

编辑4/11/2011:从MVC 3 RTM开始,TempData在Server.TransferRequest中断

修改了下面的代码以引发异常 - 但此时没有其他解决方案.


这是我根据Markus修改后的Stan的原始帖子进行的修改.我添加了一个额外的构造函数来获取Route Value字典 - 并将其重命名为MVCTransferResult,以避免混淆它可能只是一个重定向.

我现在可以执行以下重定向:

return new MVCTransferResult(new {controller = "home", action = "something" });
Run Code Online (Sandbox Code Playgroud)

我修改过的课程:

public class MVCTransferResult : RedirectResult
{
    public MVCTransferResult(string url)
        : base(url)
    {
    }

    public MVCTransferResult(object routeValues):base(GetRouteURL(routeValues))
    {
    }

    private static string GetRouteURL(object routeValues)
    {
        UrlHelper url = new UrlHelper(new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()), RouteTable.Routes);
        return url.RouteUrl(routeValues);
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var httpContext = HttpContext.Current;

        // ASP.NET MVC 3.0
        if (context.Controller.TempData != null && 
            context.Controller.TempData.Count() > 0)
        {
            throw new ApplicationException("TempData won't work with Server.TransferRequest!");
        }

        httpContext.Server.TransferRequest(Url, true); // change to false to pass query string parameters if you have already processed them

        // ASP.NET MVC 2.0
        //httpContext.RewritePath(Url, false);
        //IHttpHandler httpHandler = new MvcHttpHandler();
        //httpHandler.ProcessRequest(HttpContext.Current);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 有用!上帝保佑你和StackOverflow! (2认同)

Nit*_*wal 14

您可以在IIS7 +上使用Server.TransferRequest.


小智 12

我最近发现ASP.NET MVC不支持Server.Transfer()所以我创建了一个存根方法(受Default.aspx.cs启发).

    private void Transfer(string url)
    {
        // Create URI builder
        var uriBuilder = new UriBuilder(Request.Url.Scheme, Request.Url.Host, Request.Url.Port, Request.ApplicationPath);
        // Add destination URI
        uriBuilder.Path += url;
        // Because UriBuilder escapes URI decode before passing as an argument
        string path = Server.UrlDecode(uriBuilder.Uri.PathAndQuery);
        // Rewrite path
        HttpContext.Current.RewritePath(path, false);
        IHttpHandler httpHandler = new MvcHttpHandler();
        // Process request
        httpHandler.ProcessRequest(HttpContext.Current);
    }
Run Code Online (Sandbox Code Playgroud)


Bri*_*van 9

难道你不能只创建一个你想要重定向到的控制器实例,调用你想要的动作方法,然后返回结果吗?就像是:

 HomeController controller = new HomeController();
 return controller.Index();
Run Code Online (Sandbox Code Playgroud)

  • 不,您创建的控制器不会正确设置请求和响应设置.这可能会导致问题. (3认同)

小智 7

我想将当前请求重新路由到另一个控制器/操作,同时保持执行路径与请求第二个控制器/操作完全相同.就我而言,Server.Request不起作用,因为我想添加更多数据.这实际上等同于执行另一个HTTP GET/POST的当前处理程序,然后将结果流式传输到客户端.我相信会有更好的方法来实现这一目标,但这对我有用:

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);
Run Code Online (Sandbox Code Playgroud)

你的猜测是对的:我把这段代码放进去了

public class RedirectOnErrorAttribute : ActionFilterAttribute, IExceptionFilter
Run Code Online (Sandbox Code Playgroud)

我正在使用它向开发人员显示错误,而它将在生产中使用常规重定向.请注意,我不想使用ASP.NET会话,数据库或其他一些方法在请求之间传递异常数据.


Aar*_*nLS 6

而不是模拟服务器传输,MVC仍然能够实际执行Server.TransferRequest:

public ActionResult Whatever()
{
    string url = //...
    Request.RequestContext.HttpContext.Server.TransferRequest(url);
    return Content("success");//Doesn't actually get returned
}
Run Code Online (Sandbox Code Playgroud)


Ric*_*lay 5

只是实例化另一个控制器并执行它的动作方法.