ASP.NET 核心中 Server.Transfer 的替代方案

Alternative to Server.Transfer in ASP.NET Core

我正在将一个 ASP.NET 应用程序迁移到 ASP.NET 核心,他们对 HttpServerUtility.Transfer(string path) 有一些调用。但是,HttpServerUtility 不存在于 ASP.NET 核心中。

有没有我可以使用的替代方案?或者 Response.Redirect 是我唯一的选择吗?

我想尽可能保持与旧应用程序相同的行为,因为 Server.Transfer and Response.Redirect.

之间存在差异

我相信您正在寻找 MVC 中的 "named view" return。像这样,

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

以上将 return 该特定视图。如果您有控制视图详细信息的条件,您也可以这样做。

你是对的。 Server.Transfer 和 Server.Redirect 是完全不同的。 Server.Transfer 执行一个新页面,returns 它向浏览器发送结果,但不通知浏览器它返回了一个不同的页面。所以在这种情况下,浏览器 url 将显示请求的原始 url 但内容将来自其他页面。这与执行将指示浏览器请求新页面的 Server.Redirect 完全不同。在这种情况下,浏览器中显示的 url 将更改为显示新的 url.

要在 Asp.Net 核心中执行 Server.Transfer 的等效操作,您需要更新 Request.PathRequest.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();
            }

        }
    }

将原始 url 放在 HttpContext.Items["OriginalUrl"] 中并不是绝对必要的,但这样做可以让结束页面很容易知道它是否正在响应传输,如果是,原始 url 是。

根据你的情况,我看到了一些适合你的选项:

  • 返回另一个视图:所以只有 HTML。查看 Muqeet Khan 的回答
  • 返回同一控制器的另一个方法:这也允许执行其他操作的业务逻辑。只需写类似 return MyOtherAction("foo", "bar").
  • 的内容
  • 返回另一个控制器的动作:参见 Ron C 的回答。我对这个解决方案有点麻烦,因为它省略了整个中间件,其中包含大约 90% 的ASP.NET 核心的逻辑(如安全、cookie、压缩……)。
  • 路由样式中间件:添加一个类似于路由的中间件。在这种情况下,您的决策逻辑需要在那里进行评估。
  • 延迟重新运行中间件堆栈:您基本上需要重新运行堆栈的一大部分。我相信这是可能的,但还没有看到解决方案。我看过 Damian Edwards(ASP.NET Core 的 PM)的演示文稿,他主持了 ASP.NET Core,但没有 Kestrel/TCPIP 使用,只是为了在浏览器中本地渲染 HTML。你能做到的。但这是很多过载。

忠告:转移已死;)。这样的差异是 ASP.NET 核心存在和性能改进的原因。这对迁移不利,但对整个平台有利。

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

在此示例中,我的视图模型有一个名为“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,因此您可能需要寻找一种替代方法,以便在母版页的正确位置插入脚本和样式。

我可以看到这是一个相当古老的线程。不知道.Net Core什么时候加入了URL Rewriting 但答案是重写中间件中的URL,不是重定向,不会return到服务器,不会更改浏览器地址栏中的 url,但会更改路由。

资源:

https://weblog.west-wind.com/posts/2020/Mar/13/Back-to-Basics-Rewriting-a-URL-in-ASPNET-Core

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-5.0