本文介绍了如何在 ASP.NET MVC 中模拟 Server.Transfer?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 ASP.NET MVC 中,您可以很容易地返回重定向 ActionResult:

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

 return RedirectToAction("Index");

 or

 return RedirectToRoute(new { controller = "home", version = Math.Random() * 10 });

这实际上会提供 HTTP 重定向,这通常没问题.但是,在使用 Google Analytics 时,这会导致大问题,因为原始引荐来源网址丢失了,因此 Google 不知道您来自哪里.这会丢失有用的信息,例如任何搜索引擎术语.

This will actually give an HTTP redirect, which is normally fine. However, when using Google Analytics this causes big issues because the original referrer is lost, so Google doesn't know where you came from. This loses useful information such as any search engine terms.

顺便说一下,这种方法的优点是可以删除可能来自活动的任何参数,但仍然允许我在服务器端捕获它们.将它们留在查询字符串中会导致人们在书签、推特或博客中添加他们不应该添加的链接.我已经多次看到这种情况,人们在 Twitter 上发布了指向我们网站的包含广告系列 ID 的链接.

As a side note, this method has the advantage of removing any parameters that may have come from campaigns but still allows me to capture them server side. Leaving them in the query string leads to people bookmarking or twitter or blog a link that they shouldn't. I've seen this several times where people have twittered links to our site containing campaign IDs.

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

Anyway, I am writing a 'gateway' controller for all incoming visits to the site which I may redirect to different places or alternative versions.

就目前而言,我现在更关心 Google(而不是意外添加书签),我希望能够将访问 / 的人发送到他们访问 /home/7,这是主页的第 7 版.

For now I care more about Google for now (than accidental bookmarking), and I want to be able to send someone who visits / to the page that they would get if they went to /home/7, which is version 7 of a homepage.

就像我之前说的,如果我这样做,我将失去谷歌分析推荐人的能力:

Like I said before if I do this I lose the ability for google to analyse the referrer:

 return RedirectToAction(new { controller = "home", version = 7 });

我真正想要的是一个

 return ServerTransferAction(new { controller = "home", version = 7 });

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

which will get me that view without a client side redirect.I don't think such a thing exists, though.

目前我能想到的最好办法是在我的 GatewayController.Index 操作中复制 HomeController.Index(..) 的整个控制器逻辑.这意味着我必须将 'Views/Home' 移动到 'Shared' 以便它可以访问.一定有更好的方法.

Currently the best thing I can come up with is to duplicate the whole controller logic for HomeController.Index(..) in my GatewayController.Index Action. This means I had to move 'Views/Home' into 'Shared' so it was accessible. There must be a better way.

推荐答案

TransferResult 类怎么样?(基于 斯坦斯回答)

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

更新:现在适用于 MVC3(使用来自 西蒙的帖子).通过查看它是否在 IIS7+ 的集成管道中运行,它应该(无法对其进行测试)也可以在 MVC2 中运行.

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+.

为了完全透明;在我们的生产环境中,我们从未直接使用 TransferResult.我们使用 TransferToRouteResult,它依次调用执行 TransferResult.以下是我的生产服务器上实际运行的内容.

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

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

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

这篇关于如何在 ASP.NET MVC 中模拟 Server.Transfer?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-25 15:09