2015-05-22 92 views
15

我有一個portlet。當portlet加載時,在呈現第一個視圖之前,在某些情況下,需要調用一個更改數據庫中數據的存儲庫。我不想詳細說明爲什麼這是必要的,關於這個設計缺陷的答案是沒有用的。我知道這是一個設計缺陷,但我仍然想找出一個替代解決方案來解決以下問題:更改GET頁面請求上的數據(處理預加載請求)

這個設置的問題是,瀏覽器發送預加載請求。例如,Portlet所在頁面的URL是/ test-portlet。現在,當你在地址欄中輸入它時,如果你的瀏覽器歷史記錄中包含它,那麼當瀏覽器向你顯示它時,瀏覽器就會發送GET請求到頁面。如果在解決第一個GET請求之前按Enter,則瀏覽器會發送一個新的GET請求。這意味着Portlet接收到兩個並行開始處理的請求。第一個數據庫過程可能正常工作,但考慮到數據庫過程的性質,第二個調用通常會發生異常。

從Java應用程序中處理上述問題將會是一個不錯的乾淨方式嗎?

旁註:我使用的是Spring MVC。

一個可能的控制器的一個簡單的例子:

@RequestMapping 
public String index(Model model, RenderRequest request){ 
    String username = dummyRepository.changeSomeData(request.getAttribute("userId")); 
    model.add("userName", username); 
    return "view"; 
} 

我有興趣在溶液中完全禁止第一執行。例如,一些重定向到控制器的POST,瀏覽器不會觸發。不知道它是否可以實現。

+0

你檢查請求的頭部?有沒有任何表示您處於「預加載」場景的標題?在這種情況下,您可以跳過您需要跳過的任何內容。 – geert3

+0

可以ü簡要描述你的數據庫程序是什麼? –

+0

@MSIbrahim我剛剛簡化了場景。實際上有一個REST API調用可以處理2個數據庫。我不認爲這是相關的,因爲我確實不想在較低的層次上進行更改,因爲它嚴格地說是web層的問題。 – Reins

回答

1

使用鎖我認爲你可以解決它,使secound請求等待第一個完成,然後處理它。我沒有在Java鎖經驗,但我發現有關文件鎖定另一組交換後在安裝插件: How can I lock a file using java (if possible)

+0

一個可能的想法,但第二個請求仍然會被處理。 我會更感興趣的解決方案,完全阻止兩個執行。例如,某些來自控制器的重定向,瀏覽器不會在預加載請求上觸發。不知道它是否可以實現。不過謝謝。 – Reins

+0

這是一個關於http協議的問題,我認爲這是不可能的。瀏覽器進行了很多「scetcy」優化,使得它們執行了很多請求,這些請求對於他們所請求的服務器來說都很難,而不需要智能地處理需要傳輸的數據量。使瀏覽器像Chrome一樣適用於您的網站的唯一方法是確保它可以處理它。 但給連接類型一個外觀http協議,如果你可以做你所要求的,就是你可以做到這一點。 – jpeg

1

請參考this answer,它可以幫助您檢測,而忽略了一些預加載的請求。不過,你也應該確保'最糟糕的情況'有效,或許使用@jpeg建議的鎖定方式,但它可能與在某處使用synchronize塊一樣容易。

+0

不幸的是,有些情況下請求是相同的。我認爲問題可能主要是Chrome,如此處所述:[link](http://stackoverflow.com/a/29097039/1826152)。 _it可以幫助您檢測,而忽略了一些預壓requests._ - 我會覺得很好,如果有將是一個單一的解決方案,以避免一堆不同的檢查一樣的'如果(thisheader);如果(thatheader);'等,並且仍然不能涵蓋所有可能性。 – Reins

+1

一個靈魂可能是JS Visibility API,但我仍然會等待後端解決問題的可能方案。我覺得從客戶端/瀏覽器端處理這件事有點令人費解。 – Reins

1

因爲我沒有看到chrome添加了一些特定的頭文件(或者無論如何通知服務器prerendering狀態)可能無法在服務器端檢測到它...至少不是直接。但是,您可以模擬客戶端的檢測,然後將其與服務器調用結合使用。

注意,您可以在客戶端檢測預渲染:

if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') { 
    // prerendering takes place 
} 

現在,你可以打破在客戶端處於預壓狀態下預加載通過顯示的情況下,瀏覽器的警告框(或你大概可以做同樣的在JavaScript中只是一些錯誤,而不是使用警報,()):

if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') { 
    alert('this is alert during prerendering..') 
} 

現在,當鉻預呈現它會失敗,因爲JavaScript警告將阻止瀏覽器繼續執行JavaScript的網頁。

如果您輸入chrome:chrome:// net-internals /#prerender您可以跟蹤chrome何時執行預渲染以及哪些頁面執行預渲染。在上面的例子中的情況下(與預渲染期間警告框),你可以看到有:

的link rel預渲染(跨 域)http://some.url.which.is.preloaded的Javascript 警報 2015年6月7日19:26:18.758

最終狀態 - Javascript Alret證明chrome未能預加載頁面(我測試過了)。

現在如何解決您的問題?那麼,你可以結合這與異步調用(AJAX)並加載一些內容(來自另一個網址),取決於頁面實際上是否預渲染。

考慮下面的代碼(這可能是由您portlet下面的網址/測試的portlet被渲染):

<html> 
    <body> 


    <div id="content"></div> 

<script> 

if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') { 
    // when chrome uses prerendering we block the request with alert 
    alert('this is alert during prerendering..'); 
} else { 
    // in case no prerendering takes place we load the actual content asynchronously 

    var xhr = new XMLHttpRequest(); 
    xhr.onreadystatechange = function() { 
     if (xhr.readyState == 4) { 
      // when the content is loaded we place the html inside "content" div    
      document.getElementById('content').innerHTML = xhr.responseText; 

     } 
    } 
    xhr.open('GET', '/hidden-portlet', true); // we call the actual portlet 
    xhr.send(null); 

} 

</script> 

    </body> 
</html> 

正如你所看到的/隱藏的portlet只加載的情況下,瀏覽器加載頁面正常(沒有預加載)。 url/hidden-portlet(可以是另一個portlet/servlet)下的服務器端處理程序包含在預渲染期間不應執行的實際代碼。因此,它是/隱藏的portlet,其執行

dummyRepository.changeSomeData(request.getAttribute("userId")); 

此portlet還可以返回普通視圖(渲染HTML)將被異步置於頁面上的URL /測試的portlet感謝下的把戲/測試 - portlet:document.getElementById('content').innerHTML = xhr.responseText;

因此,在地址/ test-portlet下sumarize portlet只會返回帶有JavaScript代碼的HTML,這會觸發實際的portlet。

如果你有很多脆弱的portlet,你可以更進一步,所以你可以通過請求參數/test-portlet?actualUrl=hidden-portlet來參數化你/ test-portlet,這樣實際portlet的地址就可以從url中讀取請求參數在服務器端)。服務器會在這種情況下,動態呈現其應加載的網址:

因此,而不是硬編碼:

xhr.open('GET', '/hidden-portlet', true); 

,你將有

xhr.open('GET', '/THIS_IS_DYNAMICALLY_REPLACED_EITHER_ON_SERVER_OR_CLIENT_SIDE_WITH_THE_ADDRES_FROM_URL', true); 
+0

感謝您的輸入。正如我在另一個答案的評論中所說的,我知道可以從客戶端處理。由於看起來這可能是一種方式,因爲有報酬的問題在一週內沒有找到明確的答案(我仍然認爲這是駭人聽聞的)。我真的很抱歉,但從這個問題的角度來看,我不能接受答案,因爲問題是期待從服務器端獲得解決方案 - 可能與否。你已經非常徹底(雖然異步portlet加載從來不是問題:)),但是upvote是我能做的最多的。 – Reins

+0

只要你願意:)在頂部我只說明,據我所知,你不能僅僅使用服務器端解決它,因爲魔法在客戶端。客戶端需要通知服務器關於「預加載階段」。由於chrome不這樣做,你需要自己發送通知。或者,您可以考慮阻止第二個請求(真正的請求)的解決方案,但不能阻止第一個請求。 – walkeros

+0

阻塞第二個請求的問題是第二個請求實際上是構造返回給用戶的視圖的請求。它使用存儲庫中的數據。服務器端的一個理論解決方案是在第一次請求執行期間添加所需的所有內容,然後在第二次請求期間檢查會話。雖然這也沒有任何防錯的功能,因爲這些請求可能會在毫秒的時間範圍內出現,並且在執行第二個請求時,會話中可能仍然沒有任何內容。 – Reins