ここには 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 で共有するためのハックの少ない方法はありますか? - そうでない場合、応答が本当に終了していることを確認する方法を知っている人はいますか?
よろしくお願いします!