asp.net-mvc 如何在 ASP.NET MVC 中模拟 Server.Transfer?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/799511/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to simulate Server.Transfer in ASP.NET MVC?
提问by Simon_Weaver
In ASP.NET MVC you can return a redirect ActionResult quite easily :
在 ASP.NET MVC 中,您可以很容易地返回重定向 ActionResult:
return RedirectToAction("Index");
or
return RedirectToRoute(new { controller = "home", version = Math.Random() * 10 });
This will actually give an HTTP redirect, which is normally fine. However, when using google analytics this causes big issues because the original referer is lost so google doesnt know where you came from. This loses useful information such as any search engine terms.
这实际上会给出一个 HTTP 重定向,这通常很好。然而,当使用谷歌分析时,这会导致大问题,因为原始引用丢失了,所以谷歌不知道你来自哪里。这会丢失有用的信息,例如任何搜索引擎术语。
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.
附带说明一下,此方法的优点是可以删除可能来自活动的任何参数,但仍允许我在服务器端捕获它们。将它们留在查询字符串中会导致人们在书签或 Twitter 或博客中添加他们不应该添加的链接。我已经多次看到这种情况,人们在 Twitter 上发布了指向我们网站的包含活动 ID 的链接。
Anyway, I am writing a 'gateway' controller for all incoming visits to the site which i may redirect to different places or alternative versions.
无论如何,我正在为网站的所有传入访问编写一个“网关”控制器,我可能会将其重定向到不同的地方或替代版本。
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.
现在,我现在更关心 Google(而不是意外添加书签),并且我希望能够将访问/该页面的人发送到他们访问/home/77 版的主页。
Like I said before If I do this I lose the ability for google to analyse the referer :
就像我之前说的,如果我这样做,我将失去谷歌分析引用者的能力:
return RedirectToAction(new { controller = "home", version = 7 });
What i really want is a
我真正想要的是
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.
这将使我在没有客户端重定向的情况下获得该视图。我不认为这样的事情存在。
Currently the best thing I can come up with is to duplicate the whole controller logic for HomeController.Index(..)in my GatewayController.IndexAction. This means I had to move 'Views/Home'into 'Shared'so it was accessible. There must be a better way??..
目前我能想到的最好的办法是HomeController.Index(..)在我的GatewayController.IndexAction 中复制整个控制器逻辑。这意味着我必须搬进'Views/Home'去'Shared'才能进入。一定会有更好的办法??..
回答by Markus Olsson
How about a TransferResult class? (based on Stans answer)
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);
}
}
}
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+.
更新:现在适用于 MVC3(使用来自Simon 帖子的代码)。通过查看它是否在 IIS7+ 的集成管道中运行,它应该(无法对其进行测试)也可以在 MVC2 中工作。
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.
完全透明;在我们的生产环境中,我们从未直接使用 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);
}
}
And if you're using T4MVC(if not... do!) this extension might come in handy.
如果您正在使用T4MVC(如果不是...做!)这个扩展可能会派上用场。
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());
回答by Simon_Weaver
Edit: Updated to be compatible with ASP.NET MVC 3
编辑:更新为与 ASP.NET MVC 3 兼容
Provided you are using IIS7 the following modification seems to work for ASP.NET MVC 3. Thanks to @nitin and @andy for pointing out the original code didn't work.
如果您使用的是 IIS7,以下修改似乎适用于 ASP.NET MVC 3。感谢 @nitin 和 @andy 指出原始代码不起作用。
Edit 4/11/2011: TempData breaks with Server.TransferRequest as of MVC 3 RTM
2011 年 4 月 11 日编辑:从 MVC 3 RTM 开始,TempData 与 Server.TransferRequest 中断
Modified the code below to throw an exception - but no other solution at this time.
修改了下面的代码以抛出异常 - 但此时没有其他解决方案。
Here's my modification based upon Markus's modifed version of Stan's original post. I added an additional constructor to take a Route Value dictionary - and renamed it MVCTransferResult to avoid confusion that it might just be a redirect.
这是我根据 Markus 对 Stan 原始帖子的修改版本所做的修改。我添加了一个额外的构造函数来获取 Route Value 字典 - 并将其重命名为 MVCTransferResult 以避免混淆它可能只是一个重定向。
I can now do the following for a redirect:
我现在可以为重定向执行以下操作:
return new MVCTransferResult(new {controller = "home", action = "something" });
My modified class :
我修改后的课程:
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);
}
}
回答by Nitin Agarwal
You can use Server.TransferRequeston IIS7+ instead.
您可以改为在 IIS7+ 上使用Server.TransferRequest。
回答by Nitin Agarwal
I found out recently that ASP.NET MVC doesn't support Server.Transfer() so I've created a stub method (inspired by Default.aspx.cs).
我最近发现 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);
}
回答by Brian Sullivan
Couldn't you just create an instance of the controller you would like to redirect to, invoke the action method you want, then return the result of that? Something like:
难道你不能只创建一个你想重定向到的控制器实例,调用你想要的操作方法,然后返回结果吗?就像是:
HomeController controller = new HomeController();
return controller.Index();
回答by Brian Sullivan
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:
我想将当前请求重新路由到另一个控制器/动作,同时保持执行路径与请求第二个控制器/动作完全相同。就我而言, 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);
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.
我正在使用它向开发人员显示错误,而它将在生产中使用常规重定向。请注意,我不想使用 ASP.NET 会话、数据库或其他一些方法在请求之间传递异常数据。
回答by AaronLS
Rather than simulate a server transfer, MVC is still capable of actually doing a Server.TransferRequest:
MVC 仍然能够实际执行Server.TransferRequest,而不是模拟服务器传输:
public ActionResult Whatever()
{
string url = //...
Request.RequestContext.HttpContext.Server.TransferRequest(url);
return Content("success");//Doesn't actually get returned
}
回答by Richard Szalay
Just instance the other controller and execute it's action method.
只需实例化另一个控制器并执行它的操作方法。
回答by JoshBerke
You could new up the other controller and invoke the action method returning the result. This will require you to place your view into the shared folder however.
您可以新建另一个控制器并调用返回结果的操作方法。但是,这将要求您将视图放入共享文件夹中。
I'm not sure if this is what you meant by duplicate but:
我不确定这是否是您所说的重复的意思,但是:
return new HomeController().Index();
Edit
编辑
Another option might be to create your own ControllerFactory, this way you can determine which controller to create.
另一种选择可能是创建您自己的 ControllerFactory,这样您就可以确定要创建哪个控制器。
回答by Richard
Doesn't routing just take care of this scenario for you? i.e. for the scenario described above, you could just create a route handler that implemented this logic.
路由不只是为您处理这种情况吗?即对于上述场景,您可以创建一个实现此逻辑的路由处理程序。

