3

我在这里有两个问题,如果第一个问题得到回答,第二个问题是无关紧要的,但我认为在技术上仍然很有趣......我会尽量清楚:

  • 第一个问题:我的目标是在 MVC 中伪造一个 Server.Transfer,是否有任何下降的方法可以做到这一点,我发现了很多关于它的文章,但大多数都是关于重定向/重新路由的,这在我的情况下是不可能的(不是至少我能想到)。

这是上下文,我们有两个版本的网站,一个“桌面”版本和一个移动版本。我们的营销人员希望在同一个 url 上提供两个版本的主页(因为 SEO 专家是这么说的)。

这听起来微不足道和简单,在大多数情况下,除了...相同的应用程序)。

因为桌面版本代表了我们大约 95% 的流量,所以这应该是默认设置,并且我们希望仅当用户在移动设备上或确实需要时,才从后面的 ASPX 代码“传输”(因此相同的 url)到 MVC 视图看手机版。据我目前所见,没有简单的方法可以做到这一点(Server.Transfer 只执行一个新的处理程序 - 因此页面 - 如果有一个物理文件)。因此,到目前为止,有没有人以适当的方式做到这一点?

这让我想到:

  • 第二个问题:我确实建立了自己的转移到 MVC 机制,但后来发现 Response.End() 实际上不再结束正在运行的线程,有人知道为什么吗?

显然,我不希望有任何答案出乎意料,所以这就是我正在做的事情:

在需要转移到移动设备的页面中,我执行以下操作:

protected override void OnPreInit(EventArgs e) {
  base.OnPreInit(e);
  MobileUri = "/auto/intro/index"; // the MVC url to transfer to
  //Identifies correct flow based on certain conditions 1-Desktop 2-Mobile
  BrowserCheck.RedirectToMobileIfRequired(MobileUri);
}

而我由 RedirectToMobileIfRequired 调用的实际 TransferToMobile 方法(我跳过了检测部分,因为它非常不相关)看起来像:

/// <summary>
/// Does a transfer to the mobile (MVC) action. While keeping the same url.
/// </summary>
private static void TransferToMobile(string uri) {
  var cUrl = HttpContext.Current.Request.Url;

  // build an absolute url from relative uri passed as parameter
  string url = String.Format("{0}://{1}/{2}", cUrl.Scheme, cUrl.Authority, uri.TrimStart('/'));

  // fake a context for the mvc redirect (in order to read the routeData).
  var fakeContext = new HttpContextWrapper(new HttpContext(new HttpRequest("", url, ""), HttpContext.Current.Response));
  var routeData = RouteTable.Routes.GetRouteData(fakeContext);

  // get the proper controller
  IController ctrl = ControllerBuilder.Current.GetControllerFactory().CreateController(fakeContext.Request.RequestContext, (string)routeData.Values["controller"]);

  // We still need to set routeData in the request context, as execute does not seem to use the passed route data.
  HttpContext.Current.Request.RequestContext.RouteData.DataTokens["Area"] = routeData.DataTokens["Area"];
  HttpContext.Current.Request.RequestContext.RouteData.Values["controller"] = routeData.Values["controller"];
  HttpContext.Current.Request.RequestContext.RouteData.Values["action"] = routeData.Values["action"];

  // Execute the MVC controller action
  ctrl.Execute(new RequestContext(new HttpContextWrapper(HttpContext.Current), routeData));

  if (ctrl is IDisposable) {
    ((IDisposable)ctrl).Dispose(); // does not help
  }

  // end the request.
  HttpContext.Current.Response.End();
  // fakeContext.Response.End(); // does not add anything
  // HttpContext.Current.Response.Close(); // does not help
  // fakeContext.Response.Close(); // does not help
  // Thread.CurrentThread.Abort(); // causes infinite loading in FF
}

在这一点上,我希望 Response.End() 调用也能结束线程(如果我跳过整个伪造控制器执行位,它会结束),但事实并非如此。

因此,我怀疑要么是我伪造的上下文(这是我发现能够使用新 url 传递当前上下文的唯一方法),要么是控制器阻止了线程被杀死。

fakeContext.Response 与 CurrentContext.Response 相同,并且结束虚假上下文响应或终止线程的几次尝试并没有真正帮助我。

在 Response.End() 之后运行的任何代码实际上都不会呈现给客户端(这是一个小小的胜利),因为正在关闭响应流(以及连接,客户端中没有“无限加载”)。但是代码仍在运行,这并不好(在尝试编写 ASPX 页面、编写标题等时,显然也会产生大量错误)。

因此,任何新的领导都将受到欢迎!

总结一下: - 有没有人有一种不那么老套的方法来实现在同一个 url 上共享 ASPX 页面和 MVC 视图?- 如果没有,有没有人知道我如何确保我的回复真的被结束了?

提前谢谢了!

4

2 回答 2

2

好,

对于有兴趣的人,我至少可以回答问题 1 :)。当我第一次使用该功能时,我查看了以下(非常接近的)问题:

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

并尝试了 Stan 创建的 Transfer Method (使用httpHandler.ProcessRequest)和Server.TransferRequest方法。两者都对我不利:

  • 第一个在 IIS 中不起作用,(因为我需要在页面中调用它,而这似乎已经太晚了)。
  • 对于需要在 IIS 中运行网站的开发人员来说,第二个问题非常烦人(没什么大不了的,但仍然......)。

看到我的解决方案显然不是最佳的,我不得不回到 IIS 解决方案,这似乎是最适合生产环境的解决方案。

该解决方案适用于一个页面并在另一个页面上触发了无限循环......

就在那时我被指出了我懒惰地丢弃的原因:我们的 url 重定向模块。它使用 Request.RawUrl 来匹配一个规则,而且令人惊讶的是,Server.TransferRequest 保留了原始的 Request.RawUrl,而 app.Request.Url.AbsolutePath 将包含转移到的 url。所以基本上我们的 url 重写模块总是重定向到原来的请求,它试图转移到新的请求,等等。

在 url 重写模块中改变了这一点,并希望一切仍然像魅力一样工作(显然很多测试将跟随这样的改变)......

为了解决开发人员的问题,我选择将这两种解决方案结合起来,这可能会增加开发和生产之间不同行为的风险,但这就是我们有测试服务器的目的......

所以这是我的传输方法最后的样子:

再一次,这意味着从 ASPX 页面转移到 MVC 操作,从 MVC 到 MVC,您可能不需要任何复杂的东西,因为您可以使用 TransferResult 或只是返回不同的视图、调用另一个操作等。

private static void Transfer(string url) {
  if (HttpRuntime.UsingIntegratedPipeline) {
    // IIS 7 integrated pipeline, does not work in VS dev server.
    HttpContext.Current.Server.TransferRequest(url, true);
  }

  // for VS dev server, does not work in IIS
  var cUrl = HttpContext.Current.Request.Url;
  // Create URI builder
  var uriBuilder = new UriBuilder(cUrl.Scheme, cUrl.Host, cUrl.Port, HttpContext.Current.Request.ApplicationPath);
  // Add destination URI
  uriBuilder.Path += url;
  // Because UriBuilder escapes URI decode before passing as an argument
  string path = HttpContext.Current.Server.UrlDecode(uriBuilder.Uri.PathAndQuery);
  // Rewrite path
  HttpContext.Current.RewritePath(path, true);
  IHttpHandler httpHandler = new MvcHttpHandler();
  // Process request
  httpHandler.ProcessRequest(HttpContext.Current);
}
于 2012-09-28T09:48:27.800 回答
0

我没有做太多研究,但似乎发生了以下事情Response.End()

public void End()
{
    if (this._context.IsInCancellablePeriod)
    {
        InternalSecurityPermissions.ControlThread.Assert();
        Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
    }
    else if (!this._flushing)
    {
        this.Flush();
        this._ended = true;
        if (this._context.ApplicationInstance != null)
        {
            this._context.ApplicationInstance.CompleteRequest();
        }
    }
}

这至少可以提供“为什么”(_context.IsInCancellablePeriod)。您可以尝试使用您最喜欢的 CLR 反编译器来跟踪它。

于 2012-09-27T11:16:21.260 回答