3

ここには 2 つの問題があります。最初の問題が解決された場合、2 番目の問題は無関係ですが、私の意見では技術的に興味深いものです...できるだけ明確にするように努めます。

  • 最初の質問: 私の目標は、MVC で Server.Transfer を偽造することです。それを行うための降下方法はありますか?少なくとも私が考えることができる)。

これがコンテキストです。私たちのウェブサイトには、「デスクトップ」バージョンとモバイル バージョンの 2 つのバージョンがあります。当社のマーケティング担当者は、ホームページの両方のバージョンを同じ URL で提供することを望んでいます (SEO の専門家がそう言ったからです)。

これは些細で単純に聞こえますが、ほとんどの場合はそうです... デスクトップ サイトは .NET 4.0 ASPX サイトで、モバイル サイトは MVC であり、どちらも同じサイト (同じプロジェクト、同じ apppool、同じアプリ)。

デスクトップ バージョンはトラフィックの約 95% を占めるため、これがデフォルトである必要があり、ユーザーがモバイル デバイスを使用している場合、または本当に必要な場合にのみ、ASPX コード ビハインドから MVC ビューに "転送" (したがって同じ URL) する必要があります。モバイル版を表示します。私がこれまでに見た限りでは、それを行う簡単な方法はありません (Server.Transfer は新しいハンドラーのみを実行するため、物理ファイルがある場合はページを実行します)。したがって、これまでに適切な方法でそれを行った人はいますか?

そして、それは私をもたらします:

  • 2 番目の質問: 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() の後に実行されているコードは、実際にはクライアントにレンダリングされません (これは小さな勝利です)。これは、Response ストリーム (および接続、クライアントでの「無限の読み込み」がない) が閉じられているためです。しかし、コードはまだ実行されており、それは良くありません (また、ASPX ページを書き込もうとしたり、ヘッダーを書き込もうとすると、明らかに大量のエラーが生成されます)。

したがって、新しいリードは大歓迎です。

要約すると: - ASPX ページと MVC ビューを同じ URL で共有するためのハックの少ない方法はありますか? - そうでない場合、応答が本当に終了していることを確認する方法を知っている人はいますか?

よろしくお願いします!

4

2 に答える 2

2

良い、

興味のある人は誰でも、少なくとも質問1への答えがあります:)。私が最初にその機能に取り組んだとき、次の (そして非常に近い) 質問を見ました。

ASP.NET MVC で Server.Transfer をシミュレートする方法は?

そして、Stan によって作成された Transfer Method ( httpHandler.ProcessRequestを使用) とServer.TransferRequestメソッドの両方を試しました。どちらも私にとって不利な点がありました:

  • 最初のものは IIS では機能しません (ページでそれを呼び出す必要があり、それはすでに遅すぎるように思われるため)。
  • 2 つ目は、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 に答える