ASP.NET Core中的Server.Transfer替代方案

11

可能是ASP.NET 5中的重定向重复问题 - Gerardo Grignoli
我会收集所有参数,然后像这个答案所示一样执行“Redirect”或“RedirectToAction”。(http://stackoverflow.com/a/30430848/97471) - Gerardo Grignoli
2
@GerardoGrignoli 我想要保持同样的行为。也就是说,在服务器端执行更改,这样客户端就不需要做任何工作,并且知道它们已被重定向到另一个页面。Server.Transfer vs Response.Redirect - Connie Yau
我很震惊,你认为@Thomas的答案比我的更有用。他没有提供任何可行的代码和理论解决方案,无法比得上我的代码在管道运行过程中是唯一可以处理不同控制器上视图所需的操作方法的,而且为此我花了大约2.5小时的时间来给你提供这些代码。为什么你会把+50奖励给他而不是我呢? - RonC
5个回答

6

根据您的情况,我为您提供几个选项:

  • 返回另一个视图:只返回HTML。请参考Muqeet Khan的答案。
  • 返回同一控制器的另一个方法:这也允许执行其他操作的业务逻辑。只需编写类似return MyOtherAction("foo", "bar")的内容即可。
  • 返回另一个控制器的操作:请参考Ron C的答案。我对此解决方案有些困惑,因为它省略了包含ASP.NET Core 90%逻辑(如安全性、cookie、压缩等)的整个中间件。
  • 路由样式中间件:添加类似于路由的中间件。在这种情况下,需要在那里评估您的决策逻辑。
  • 延迟重新运行中间件堆栈:实际上需要重新运行堆栈的大部分。我相信这是可能的,但还没有看到解决方案。我看过Damian Edwards(ASP.NET Core的PM)的演示,他在本地浏览器中托管ASP.NET Core而不使用Kestrel/ TCPIP,仅用于呈现HTML。您可以这样做。但是这会带来很大的负担。

建议:转移已死;)。像这样的差异是ASP.NET Core存在和性能改进的原因。这对于迁移来说可能不太好,但对于整个平台来说是好的。


我的解决方案与你提出的前两个解决方案在中间件方面没有任何区别,而且几乎肯定比你提出的后两个解决方案运行更多的中间件,因为如果你想要实现这样的解决方案,就必须退出中间件。此外,我的解决方案演示了如何运行由不同控制器处理的不同视图的操作逻辑。 - RonC
那是真的。我列出的清单主要展示了 Transfer 可能看起来有很多不同方式。考虑到 ASP.NET Core 的灵活性,我相信还有更多的选项。你的解决方案看起来有点 hacky,不过我还没有阅读 MVC middleware 实际如何做到这一点的细节,所以它可能是100%合法的。我认为我答案最重要的部分是最后两段;)。 - Thomas
1
在完整框架中,Server.Transfer 执行被转移页面的后台代码并提供该页面。在 .net core 中可能有其他选项来执行类似的操作,但我还没有看到有人为这些选项提供代码。 :-) - RonC
通过您的评论,我重新阅读了文档。考虑到IHttpHandler重载,完全替换将是运行最终的UseMvc添加的完整中间件堆栈(代表处理程序),其中包括路由(用于路径解析),然后是控制器/操作/过滤器/...如此多的选项。喜欢这个新世界。 - Thomas
1
我同意那将是一个很酷的解决方案,比我的更好。理论上来说,这应该是可能的,我希望有人能发布如何实现它的代码。 - RonC
2
我对“中间件堆栈的延迟重新运行”路径很感兴趣,有人有这方面的工作示例吗? - Daniel Little

5
你说得对。Server.Transfer和Server.Redirect是非常不同的。Server.Transfer执行一个新页面并将其结果返回给浏览器,但不告诉浏览器它返回了不同的页面。因此,在这种情况下,浏览器URL将显示原始请求的URL,但内容将来自其他页面。这与使用Server.Redirect完全不同,后者会指示浏览器请求新页面。在这种情况下,浏览器中显示的URL将更改以显示新URL。
要在Asp.Net Core中执行类似于Server.Transfer的操作,您需要更新Request.Path和Request.QueryString属性,指向您想要转移到的URL,并实例化处理该URL的控制器并调用其操作方法。我提供了下面的完整代码以说明这一点。 page1.html
 <html>
    <body>
        <h1>Page 1</h1>
    </body>
</html>

page2.html

 <html>
    <body>
        <h1>Page 2</h1>
    </body>
</html>

ExampleTransferController.cs

    using Microsoft.AspNetCore.Diagnostics;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;

    namespace App.Web.Controllers {

        public class ExampleTransferController: Controller {

            public ExampleTransferController() {

            }

            [Route("/example-transfer/page1")]
            public IActionResult Page1() {
                bool condition = true;

                if(condition) {

                    //Store the original url in the HttpContext items
                    //so that it's available to the app.
                    string originalUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}{HttpContext.Request.QueryString}";
                    HttpContext.Items.Add("OriginalUrl", originalUrl);

                    //Modify the request to indicate the url we want to transfer to
                    string newPath = "/example-transfer/page2";
                    string newQueryString = "";
                    HttpContext.Request.Path = newPath;
                    HttpContext.Request.QueryString = new QueryString(newQueryString);

                    //Now call the action method for that new url
                    //Note that instantiating the controller for the new action method
                    //isn't necessary if the action method is on the same controller as 
                    //the action method for the original request but
                    //I do it here just for illustration since often the action method you
                    //may want to call will be on a different controller.
                    var controller = new ExampleTransferController();
                    controller.ControllerContext = new ControllerContext(this.ControllerContext);
                    return controller.Page2();
                }

                return View();
            }


            [Route("/example-transfer/page2")]
            public IActionResult Page2() {

                string originalUrl = HttpContext.Items["OriginalUrl"] as string;
                bool requestWasTransfered = (originalUrl != null);

                return View();
            }

        }
    }

HttpContext.Items["OriginalUrl"]中放置原始URL并不是必须的,但这样做可以方便最终页面知道是否在响应传输请求,如果是,则可以了解原始URL。

1
@J.Lennon Server.Transfer也不会重新执行完整的流程。我亲自检查过了,我的HttpModules从未被新页面调用过。 - RonC
1
@J.Lennon 同意。但到目前为止,这是任何人想出的最好方法(肯定值得点赞) :-) - RonC
@RonC你的例子返回了控制器中的另一个视图。如果我需要返回另一个网页(在本地主机上运行的不同站点),我该怎么做呢?如何实现? - Lingam
Server.TransferRequest 的美妙之处在于它执行操作过滤器、控制器的 "OnActionExecuting"、授权等等。而这个解决方案只是执行操作方法。 - Alex from Jitbit
我不太喜欢这个解决方案,因为你不应该手动创建控制器。通常情况下,ASPNETCore会自动创建控制器并填充许多内容。此外,您的依赖注入也必须手动运行等等。这不是我想要采取的方法。 - Kat Lim Ruiz
显示剩余7条评论

3

0

我知道这是一个非常古老的问题,但如果有人正在使用Razor Pages并正在寻找Server.Transfer替代方案(或根据业务规则返回不同视图的方法),那么您可以使用部分视图。

在这个例子中,我的视图模型有一个名为“UseAlternateView”的属性:

public class TestModel : PageModel
{
    public bool UseAlternateView { get; set; }

    public void OnGet()
    {
        // Here goes code that can set UseAlternateView=true in certain conditions
    }
}

在我的 Razor 视图中,根据 UseAlternateView 属性的值,我会呈现不同的部分视图。
@model MyProject.Pages.TestModel
@if (Model.UseAlternateView)
{
    await Html.RenderPartialAsync("_View1", Model);
}
else
{
    await Html.RenderPartialAsync("_View2", Model);
}

局部视图(文件“_View1.cshtml”和“_View2.cshtml”)包含以下代码:

@model MyProject.Pages.TestModel
<div>
    Here goes page content, including forms with binding to Model properties
    when necessary
</div>

注意:当使用这样的局部视图时,您不能使用@Region,因此您可能需要寻找替代方法,在主页面的正确位置插入脚本和样式。


0

我相信你正在寻找MVC中的“命名视图”返回。像这样:

[HttpPost]
public ActionResult Index(string Name)
{
 ViewBag.Message = "Some message";
 //Like Server.Transfer() in Asp.Net WebForm
 return View("MyIndex");
}

以上代码将返回特定的视图。如果您有一个控制视图细节的条件,也可以实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接