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

发布于 2024-07-19 02:28:20 字数 1138 浏览 11 评论 0原文

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

 return RedirectToAction("Index");

 or

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

这实际上会给出 HTTP 重定向,这通常很好。 然而,当使用 Google Analytics 时,这会导致大问题,因为原始引荐来源网址丢失了,因此 Google 不知道您来自哪里。 这会丢失有用的信息,例如任何搜索引擎术语。

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

不管怎样,我正在为所有对该站点的传入访问编写一个“网关”控制器,我可以将其重定向到不同的位置或替代版本。

现在我更关心Google(而不是意外的书签),并且我希望能够将访问/的人发送到他们访问/home时会得到的页面/7,这是主页的版本 7。

就像我之前说过的,如果我这样做,我就会失去谷歌分析引荐来源网址的能力:

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

我真正想要的是一个

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

无需客户端重定向即可获得该视图的方法。 但我认为这样的事情不存在。

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

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

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.

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.

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

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.Index Action. This means I had to move 'Views/Home' into 'Shared' so it was accessible. There must be a better way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

三生路 2024-07-26 02:28:20

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

更新:现在可与 MVC3 配合使用(使用 西蒙的帖子)。 通过查看它是否在 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);
    }
}

如果您使用 T4MVC (如果没有......做!)这个扩展可能派上用场。

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

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

// in an action method
TransferToAction(MVC.Error.Index());

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());
甜妞爱困 2024-07-26 02:28:20

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

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

编辑 2011 年 4 月 11 日:从 MVC 3 RTM 开始,TempData 与 Server.TransferRequest 发生冲突

修改了下面的代码以引发异常 - 但目前没有其他解决方案。


这是我根据马库斯对斯坦原始帖子的修改版本所做的修改。 我添加了一个额外的构造函数来获取 Route Value 字典 - 并将其重命名为 MVCTransferResult 以避免混淆,因为它可能只是一个重定向。

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

return new MVCTransferResult(new {controller = "home", action = "something" });

我修改的类:

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

Edit: Updated to be compatible with 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.

Edit 4/11/2011: TempData breaks with Server.TransferRequest as of MVC 3 RTM

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.

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);
    }
}
尾戒 2024-07-26 02:28:20

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

You can use Server.TransferRequest on IIS7+ instead.

榆西 2024-07-26 02:28:20

我最近发现 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);
    }

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

    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);
    }
郁金香雨 2024-07-26 02:28:20

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

 HomeController controller = new HomeController();
 return controller.Index();

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();
╭⌒浅淡时光〆 2024-07-26 02:28:20

MVC 仍然能够实际执行 Server.TransferRequest

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

Rather than simulate a server transfer, MVC is still capable of actually doing a Server.TransferRequest:

public ActionResult Whatever()
{
    string url = //...
    Request.RequestContext.HttpContext.Server.TransferRequest(url);
    return Content("success");//Doesn't actually get returned
}
久隐师 2024-07-26 02:28:20

我想将当前请求重新路由到另一个控制器/操作,同时保持执行路径与请求第二个控制器/操作完全相同。 就我而言,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);

您的猜测是正确的:我放入此代码

public class RedirectOnErrorAttribute : ActionFilterAttribute, IExceptionFilter

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

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.

爱人如己 2024-07-26 02:28:20

只需实例化另一个控制器并执行它的操作方法即可。

Just instance the other controller and execute it's action method.

清醇 2024-07-26 02:28:20

您可以新建另一个控制器并调用返回结果的操作方法。 但是,这将要求您将视图放入共享文件夹中。

我不确定这是否是您所说的重复的意思,但是:

return new HomeController().Index();

编辑

另一种选择可能是创建您自己的 ControllerFactory,这样您就可以确定要创建哪个控制器。

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.

匿名。 2024-07-26 02:28:20

Server.TransferRequest 在 MVC 中完全没有必要。 这是一个过时的功能,仅在 ASP.NET 中才需要,因为请求直接到达页面,并且需要有一种方法将请求传输到另一个页面。 现代版本的 ASP.NET(包括 MVC)具有可自定义的路由基础结构,以直接路由到所需的资源。 当您可以简单地将请求直接发送到您想要的控制器和操作时,让请求到达控制器然后将其传输到另一个控制器是没有意义的。

更重要的是,由于您正在响应原始请求,因此无需仅仅为了将请求路由到右侧而将任何内容放入TempData或其他存储中地方。 相反,您到达控制器操作时原始请求完好无损。 您也可以放心,Google 会批准这种方法,因为它完全发生在服务器端。

虽然您可以通过 IRouteConstraintIRouteHandler 做很多事情,但最强大的路由扩展点是 RouteBase 子类。 此类可以扩展以提供传入路由和传出 URL 生成,这使其成为与 URL 和 URL 执行的操作有关的所有内容的一站式商店。

因此,按照第二个示例,从 //home/7,您只需要一条添加适当路由值的路由。

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // Routes directy to `/home/7`
        routes.MapRoute(
            name: "Home7",
            url: "",
            defaults: new { controller = "Home", action = "Index", version = 7 }
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

但是回到原始示例,其中有一个随机页面,它更加复杂,因为路由参数无法在运行时更改。 因此,可以使用 RouteBase 子类来完成,如下所示。

public class RandomHomePageRoute : RouteBase
{
    private Random random = new Random();

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        RouteData result = null;

        // Only handle the home page route
        if (httpContext.Request.Path == "/")
        {
            result = new RouteData(this, new MvcRouteHandler());

            result.Values["controller"] = "Home";
            result.Values["action"] = "Index";
            result.Values["version"] = random.Next(10) + 1; // Picks a random number from 1 to 10
        }

        // If this isn't the home page route, this should return null
        // which instructs routing to try the next route in the route table.
        return result;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        var controller = Convert.ToString(values["controller"]);
        var action = Convert.ToString(values["action"]);

        if (controller.Equals("Home", StringComparison.OrdinalIgnoreCase) &&
            action.Equals("Index", StringComparison.OrdinalIgnoreCase))
        {
            // Route to the Home page URL
            return new VirtualPathData(this, "");
        }

        return null;
    }
}

可以在路由中注册,如下所示:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // Routes to /home/{version} where version is randomly from 1-10
        routes.Add(new RandomHomePageRoute());

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

请注意,在上面的示例中,还存储一个记录用户进入的主页版本的 cookie 可能是有意义的,这样当他们返回时,他们会收到相同的主页版本。

另请注意,使用此方法,您可以自定义路由以考虑查询字符串参数(默认情况下完全忽略它们)并相应地路由到适当的控制器操作。

其他示例

Server.TransferRequest is completely unnecessary in MVC. This is an antiquated feature that was only necessary in ASP.NET because the request came directly to a page and there needed to be a way to transfer a request to another page. Modern versions of ASP.NET (including MVC) have a routing infrastructure that can be customized to route directly to the resource that is desired. There is no point of letting the request reach a controller only to transfer it to another controller when you can simply make the request go directly to the controller and action you want.

What's more is that since you are responding to the original request, there is no need to tuck anything into TempData or other storage just for the sake of routing the request to the right place. Instead, you arrive at the controller action with the original request intact. You also can be rest assured that Google will approve of this approach as it happens entirely on the server side.

While you can do quite a bit from both IRouteConstraint and IRouteHandler, the most powerful extension point for routing is the RouteBase subclass. This class can be extended to provide both incoming routes and outgoing URL generation, which makes it a one stop shop for everything having to do with the URL and the action that URL executes.

So, to follow your second example, to get from / to /home/7, you simply need a route that adds the appropriate route values.

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // Routes directy to `/home/7`
        routes.MapRoute(
            name: "Home7",
            url: "",
            defaults: new { controller = "Home", action = "Index", version = 7 }
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

But going back to your original example where you have a random page, it is more complex because the route parameters cannot change at runtime. So, it could be done with a RouteBase subclass as follows.

public class RandomHomePageRoute : RouteBase
{
    private Random random = new Random();

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        RouteData result = null;

        // Only handle the home page route
        if (httpContext.Request.Path == "/")
        {
            result = new RouteData(this, new MvcRouteHandler());

            result.Values["controller"] = "Home";
            result.Values["action"] = "Index";
            result.Values["version"] = random.Next(10) + 1; // Picks a random number from 1 to 10
        }

        // If this isn't the home page route, this should return null
        // which instructs routing to try the next route in the route table.
        return result;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        var controller = Convert.ToString(values["controller"]);
        var action = Convert.ToString(values["action"]);

        if (controller.Equals("Home", StringComparison.OrdinalIgnoreCase) &&
            action.Equals("Index", StringComparison.OrdinalIgnoreCase))
        {
            // Route to the Home page URL
            return new VirtualPathData(this, "");
        }

        return null;
    }
}

Which can be registered in routing like:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // Routes to /home/{version} where version is randomly from 1-10
        routes.Add(new RandomHomePageRoute());

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Note in the above example, it might make sense to also store a cookie recording the home page version the user came in on so when they return they receive the same home page version.

Note also that using this approach you can customize routing to take query string parameters into consideration (it completely ignores them by default) and route to an appropriate controller action accordingly.

Additional Examples

战皆罪 2024-07-26 02:28:20

路由不就可以为您处理这种情况吗? 即对于上述场景,您可以创建一个实现此逻辑的路由处理程序。

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.

哀由 2024-07-26 02:28:20

对于使用基于表达式的路由的任何人,仅使用上面的 TransferResult 类,这里有一个控制器扩展方法,可以实现这一点并保留 TempData。 不需要 TransferToRouteResult。

public static ActionResult TransferRequest<T>(this Controller controller, Expression<Action<T>> action)
    where T : Controller
{
     controller.TempData.Keep();
     controller.TempData.Save(controller.ControllerContext, controller.TempDataProvider);
     var url = LinkBuilder.BuildUrlFromExpression(controller.Request.RequestContext, RouteTable.Routes, action);
     return new TransferResult(url);
}

For anyone using expression-based routing, using only the TransferResult class above, here's a controller extension method that does the trick and preserves TempData. No need for TransferToRouteResult.

public static ActionResult TransferRequest<T>(this Controller controller, Expression<Action<T>> action)
    where T : Controller
{
     controller.TempData.Keep();
     controller.TempData.Save(controller.ControllerContext, controller.TempDataProvider);
     var url = LinkBuilder.BuildUrlFromExpression(controller.Request.RequestContext, RouteTable.Routes, action);
     return new TransferResult(url);
}
青衫儰鉨ミ守葔 2024-07-26 02:28:20

本身不是答案,但显然,要求不仅是实际导航“执行”Webforms Server.Transfer() 的等效功能,而且还要求所有这些在单元测试中得到完全支持。

因此,ServerTransferResult 应“看起来”像 RedirectToRouteResult,并且在类层次结构方面尽可能相似。

我正在考虑通过查看 Reflector 来做到这一点,并执行 RedirectToRouteResult 类以及各种 Controller 基类方法所做的任何操作,然后通过扩展方法将后者“添加”到控制器中。 也许这些可以是同一类中的静态方法,以便于下载?

如果我抽出时间来做这件事,我会把它发布出来,否则也许其他人可能会抢先一步!

Not an answer per se, but clearly the requirement would be not only for the actual navigation to "do" the equivalent functionality of Webforms Server.Transfer(), but also for all of this to be fully supported within unit testing.

Therefore the ServerTransferResult should "look" like a RedirectToRouteResult, and be as similar as possible in terms of the class hierarchy.

I'm thinking of doing this by looking at Reflector, and doing whatever RedirectToRouteResult class and also the various Controller base class methods do, and then "adding" the latter to the Controller via extension methods. Maybe these could be static methods within the same class, for ease/laziness of downloading?

If I get round to doing this I'll post it up, otherwise maybe somebody else might beat me to it!

梦晓ヶ微光ヅ倾城 2024-07-26 02:28:20

我通过在视图中利用 Html.RenderAction 帮助器实现了这一点:

@{
    string action = ViewBag.ActionName;
    string controller = ViewBag.ControllerName;
    object routeValues = ViewBag.RouteValues;
    Html.RenderAction(action, controller, routeValues);
}

在我的控制器中:

public ActionResult MyAction(....)
{
    var routeValues = HttpContext.Request.RequestContext.RouteData.Values;    
    ViewBag.ActionName = "myaction";
    ViewBag.ControllerName = "mycontroller";
    ViewBag.RouteValues = routeValues;    
    return PartialView("_AjaxRedirect");
}

I achieved this by harnessing the Html.RenderAction helper in a View:

@{
    string action = ViewBag.ActionName;
    string controller = ViewBag.ControllerName;
    object routeValues = ViewBag.RouteValues;
    Html.RenderAction(action, controller, routeValues);
}

And in my controller:

public ActionResult MyAction(....)
{
    var routeValues = HttpContext.Request.RequestContext.RouteData.Values;    
    ViewBag.ActionName = "myaction";
    ViewBag.ControllerName = "mycontroller";
    ViewBag.RouteValues = routeValues;    
    return PartialView("_AjaxRedirect");
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文