2015-05-22 6 views
15

Ich habe ein Portlet. Wenn das Portlet geladen wird, muss vor dem Rendern der ersten Ansicht in einigen Fällen ein Repository aufgerufen werden, das die Daten in der Datenbank ändert. Ich würde nicht näher darauf eingehen, warum dies notwendig ist, und die Antworten darauf, dass es sich um einen Konstruktionsfehler handelt, sind nicht hilfreich. Ich bin mir bewusst, dass es ein Designfehler ist, aber ich möchte immer noch eine alternative Lösung für das folgende Problem finden:Ändern Daten auf GET Seite Anfrage (Umgang mit Vorbelastung Anfragen)

Das Problem mit dieser Einrichtung ist, dass Browser Preloading-Anforderungen senden. Die URL der Seite, auf der sich das Portlet befindet, ist beispielsweise/test-portlet. Jetzt, wenn Sie es in Ihre Adresszeile eingeben, dann wenn Sie es in Ihrem Browser-Verlauf haben, dann sendet der Browser eine GET-Anfrage an die Seite bereits, wenn es Ihnen vorschlägt. Wenn Sie die Eingabetaste drücken, bevor die erste GET-Anfrage aufgelöst wurde, sendet der Browser eine neue GET-Anfrage. Dies bedeutet, dass das Portlet zwei separate Anforderungen empfängt, die es parallel verarbeitet. Die erste Datenbankprozedur funktioniert möglicherweise korrekt, aber unter Berücksichtigung der Art der Datenbankprozedur gibt der zweite Aufruf normalerweise eine Ausnahme.

Was wäre eine schöne saubere Art und Weise sein, mit dem oben erwähnten Problem aus der Java-Anwendung zu tun?

Nebenbei bemerkt: Ich bin mit Spring MVC.

Ein einfaches Beispiel einer möglichen Steuerung:

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

Ich würde in einer Lösung interessiert sein zusammen die erste Ausführung zu blockieren. Zum Beispiel eine Art von Umleitung zum POST vom Controller, die der Browser nicht auslösen würde. Nicht sicher, ob es aber erreichbar ist.

+0

haben Sie untersucht die Request-Header? Gibt es Header, die anzeigen, dass Sie sich im "Vorladen" -Szenario befinden? In diesem Fall können Sie alles überspringen, was Sie überspringen müssen. – geert3

+0

können Sie kurz beschreiben, was Ihre Datenbankprozeduren sind? –

+0

@MSIbrahim Ich habe gerade das Szenario vereinfacht. Tatsächlich gibt es einen REST-API-Aufruf, der 2 Datenbanken manipuliert. Ich denke nicht, dass dies relevant ist, da ich auf keinen Fall Änderungen auf einer niedrigeren Ebene vornehmen möchte, da es sich ausschließlich um das Problem der Web-Ebene handelt. – Reins

Antwort

1

Mit Sperren Ich denke, man es lösen könnte, die secound Anfrage Warten auf den ersten machen zu beenden und die Verarbeitung es dann. Ich habe Erfahrung nicht mit Sperren in Java, aber ich fand einen anderen Stapel Austausch Post über Dateisperren in jave: How can I lock a file using java (if possible)

+0

Eine mögliche Idee, aber die zweite Anfrage würde dann noch bearbeitet werden. Ich wäre mehr an einer Lösung interessiert, um beide Ausführungen insgesamt zu blockieren. Zum Beispiel eine Art Umleitung vom Controller, die der Browser bei einer Preloading-Anfrage nicht auslösen würde. Nicht sicher, ob es erreichbar ist. Trotzdem danke. – Reins

+0

Das ist eine Frage aus HTTP-Protokoll, die ich glaube nicht möglich ist. Browser haben eine Menge "Scetcy" -Optimierungen vorgenommen, die sie dazu bringen, eine Menge von Anfragen zu machen, die sowohl für den Server, den sie anfordern, schwierig sind, als auch für die Smart-Datenmenge, die übertragen werden muss. Der einzige Weg, um Browser wie Chrome mit Ihrer Website arbeiten zu lassen, besteht darin, sicherzustellen, dass es damit umgehen kann. Aber geben Sie HTTP-Protokoll mit Verbindungstyp ein Blick, wenn Sie tun können, was Sie fragen, ist die Art, wie Sie es tun können. – jpeg

1

verweisen auf this answer, könnte es Ihnen helfen, einige Vorbelastung Anfragen zu erkennen und zu ignorieren. Sie sollten jedoch auch sicherstellen, dass der "schlimmste Fall" funktioniert, vielleicht mit dem Sperren, wie von @jpeg vorgeschlagen, aber es könnte so einfach sein wie mit einem synchronize Block irgendwo.

+0

Leider gibt es Fälle, in denen die Anfragen identisch sind. Ich denke, das Problem könnte hauptsächlich Chrome sein, wie hier beschrieben: [link] (http://stackoverflow.com/a/29097039/1826152). _Es könnte Ihnen helfen, einige Preload-Anfragen zu erkennen und zu ignorieren._ - Ich würde es nett finden, wenn es eine einzige Lösung geben würde, um zu vermeiden, eine Reihe von verschiedenen Prüfungen wie 'if (thisheader); if (thatheader); etc, und die immer noch nicht alle Möglichkeiten abdecken könnte. – Reins

+1

Eine Lösung könnte JS Visibility API sein, aber ich würde immer noch auf eine mögliche Lösung des Problems von der Back-End-Seite warten. Ich finde es ein bisschen hackisch, dies vom Client/Browser aus zu tun. – Reins

1

Da ich nicht sehe, dass Chrome fügt einige spezifische Header (oder sowieso benachrichtigt den Server über Prerendering-Status) ist es wahrscheinlich nicht möglich, es auf der Serverseite zu erkennen ... zumindest nicht direkt. Sie können jedoch die Erkennung auf der Clientseite simulieren und später mit dem Serveraufruf kombinieren.

Beachten Sie, dass Sie auf der Client-Seite detect prerendering:

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

Jetzt können Sie auf Client-Seite brechen Vorbelastung durch Alert-Box im Falle Browser zeigt ist der Zustand in Vorbelastung (oder Sie können tun, wahrscheinlich das gleiche mit nur einem gewissen Fehler in JavaScript aktivieren, anstatt Alarm zu verwenden()):

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

Nun, wenn Chrom prerenders die Seite es wird scheitern, weil die Javascript-alert den Browser Javascript, um fortzufahren verhindert die Ausführung.

Wenn Sie in Chrom Typ: chrome: // net-internals/# Prerender Sie verfolgen können, wann und für welche Seiten Chrom Pre-Rendering ausführt.Im Falle des obigen Beispiels (mit Warnfeld während prerendering) kann man dort sehen:

link rel Prerender (Kreuz Domäne) http://some.url.which.is.preloadedJavascript Alarm 2015.06.07 19: 26: 18,758

Der Endzustand - Javascript Alret beweist, dass Chrom die Seite vorzuladen fehlgeschlagen (ich diese getestet haben).

Nun, wie kann dies Ihr Problem lösen? Nun können Sie diese kombinieren mit asynchronem Aufruf (AJAX) und einige Inhalte laden (von einer anderen URL), je nach wheater der Seite tatsächlich prerendering oder nicht.

Betrachten Sie folgenden Code (die von Ihrem Portlet unter url/Test-Portlet gemacht werden könnten):

<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> 

Wie Sie sehen das/hidden-Portlet nur bei Browser geladen wird, wird die Seite normalerweise geladen (ohne Vorladung). Der serverseitige Handler unter URL/verstecktes Portlet (der ein anderes Portlet/Servlet sein kann) enthält tatsächlichen Code, der während des Vorrenderns nicht ausgeführt werden sollte. So ist es die/hidden-Portlets, die

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

Dieses Portlet auch normale Ansicht zurückkehren kann (gerendert html) ausführt, die asynchron auf der Seite unter url/Test-Portlet dank den Trick platziert wird auf/test- Portlet: document.getElementById('content').innerHTML = xhr.responseText;.

So das Portlet unter der Adresse/Test-Portlet sumarize nur html mit einem Javascript-Code zurückgibt, die tatsächliche Portlet auslöst.

Wenn Sie viele zerbrechliche Portlets haben, können Sie damit noch weiter gehen, so dass Sie/test-portlet mit Anfrageparameter wie /test-portlet?actualUrl=hidden-portlet parametrisieren können, so dass die Adresse des tatsächlichen Portlets von url genommen wird (was als gelesen werden kann) Parameter auf Serverseite anfordern). Server wird in diesem Fall dynamisch die URL übertragen, die geladen werden soll:

Anstatt also fest einprogrammiert:

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

Sie haben

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

Danke für Ihre Eingabe. Wie ich in den Kommentaren einer anderen Antwort festgestellt habe, weiß ich, dass es möglich ist, mit der Client-Seite umzugehen. Wie es scheint, könnte dies der richtige Weg sein, da eine belobigte Frage innerhalb einer Woche keine definitiven Antworten gefunden hat (ich würde immer noch sagen, dass es hackisch ist). Es tut mir wirklich leid, aber aus der Perspektive dieser Frage kann ich die Antwort nicht akzeptieren, da die Frage eine Lösung von der Serverseite erwartet - möglich oder nicht. Sie waren sehr gründlich (obwohl das asynchrone Laden von Portlets nie ein Problem war :)), aber eine Verbesserung ist das, was ich am meisten kann. – Reins

+0

Wie du es wünschst :) An der Spitze habe ich nur gesagt, dass, soweit ich weiß, du es nicht nur mit der Server-Seite lösen kannst, weil die Magie auf dem Client liegt. Es ist ein Client, der den Server über die "Vorladephase" benachrichtigen muss. Da Chrome dies nicht tut, müssen Sie selbst eine Benachrichtigung senden. Alternativ können Sie sich eine Lösung vorstellen, die die zweite Anfrage (die echte Anfrage) blockiert, aber Sie können die erste nicht blockieren. – walkeros

+0

Das Problem beim Blockieren der zweiten Anforderung besteht darin, dass die zweite Anforderung tatsächlich diejenige ist, die die Ansicht erstellt, die an den Benutzer zurückgegeben wird. Und es verwendet die Daten aus dem Repository. Eine theoretische Lösung von der Serverseite wäre es, alles, was während der Ausführung der ersten Anforderungen zu der Sitzung benötigt wird, hinzuzufügen und dann die Sitzung während der zweiten Anfrage zu überprüfen. Obwohl dies in keiner Weise fehlerbehaftet ist, weil die Anforderungen innerhalb eines Millisekundenzeitrahmens kommen und zu dem Zeitpunkt, zu dem die zweite Anforderung ausgeführt wird, ist in der Sitzung möglicherweise nichts vorhanden. – Reins