2012-09-27 100 views
3

我有兩個問題在這裏,第二個是無關緊要,如果第一個得到答案,但仍然技術上有趣的我認爲...我會嘗試要儘可能明確:虛假MVC Server.Transfer:Response.End()不會結束我的線程

  • 1問題:我的目標就是假一Server.Transfer的MVC中,沒有任何血統的方式來做到這一點,我發現了好幾篇文章吧,但多數地方約重定向/重新路由,這在我的情況下是不可能的(至少不是我能想到的)。

這裏是上下文,我們有兩個版本的我們的網站,一個「桌面」和一個移動的。我們的營銷人員希望兩個版本的主頁在同一個網址上投放(因爲SEO專家這麼說)。

這聽起來微不足道,很簡單,除了...我們的桌面網站是一個.NET 4.0 ASPX網站,我們的移動網站是MVC,它們都運行在同一個網站(同一個項目,相同的應用程序,相同的應用程序)。

因爲桌面版本代表我們流量的95%左右,所以這應該是默認設置,並且我們希望只有當用戶在移動設備上時,才能從後面的ASPX代碼「轉移」(因此具有相同的url)設備或真的想看到移動版本。據我目前所見,沒有簡單的方法來做到這一點(Server.Transfer只執行一個新的處理程序 - 因此,如果有一個物理文件的頁面)。因此,迄今爲止,問題是否有人以適當的方式做到了這一點?

而且這是我帶來:

  • 第二個問題:我沒有建立自己轉移到MVC機制,但後來想通了,一到Response.End()實際上並沒有結束正在運行的線程了,不是任何人都知道爲什麼?

很顯然,我不希望任何回答出藍色的,所以這裏是我在做什麼:

在網頁這就需要transfering移動

,我做這樣的事情:

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頁面,編寫頭文件等時也會產生大量錯誤)。

因此,任何新的領導將超過歡迎!

總結一下: - 有沒有人有一個更簡單的方法來實現共享一個ASPX頁面和MVC視圖在同一個網址? - 如果沒有,是否有人知道我如何確保我的迴應真的結束?

非常感謝提前!

+0

的「搜索引擎優化專家」之稱的最常見和globaly接受partern運行一個網站的移動版本不一件好事?真棒! – Pluc

+0

+1;內容;諷刺+1 + – tsemer

+1

@Pluc:的確,在這裏給了我意見,SEO專家是比較貴的,他贏了:)。但無論如何,不​​幸的是,在我的情況下並不是每一個都有幫助(除非你作爲SEO專家的服務收取更多費用)。 Thx說明明顯不過:)。 –

回答

2

好,

對於任何有興趣,我至少有回答問題1 :)。 當我第一次在該功能的工作,我看着下面(和非常接近)問題:

How to simulate Server.Transfer in ASP.NET MVC?

並試圖通過二者創建斯坦的傳輸方法(使用httpHandler.ProcessRequest)和服務器.TransferRequest方法。兩者都有我的缺點:

  • 第一個不能在IIS中工作(因爲我需要在頁面中調用它,而且已經太遲了)。
  • 第二個對於所有需要在IIS中運行他們的站點的開發者來說都是非常煩人的(沒有比較大的,但仍然...)。

看到我的解決方案顯然不是最佳的,我不得不回到IIS解決方案,這似乎是生產環境的最佳選擇。

該解決方案爲一個網頁,並引發了一個又一個無限循環......

這就是當我指着我所懶洋洋地丟棄不是原因:我們的網址重定向模塊。它使用Request.RawUrl來匹配規則,並且哦,令人驚訝,Server.TransferRequest保留原始的Request.RawUrl,而app.Request.Url.AbsolutePath將包含被轉移的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); 
} 
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反編譯器來追蹤它。