Изменение данных на запрос страницы GET (обработка запросов на предварительную загрузку)

У меня есть портлет. Когда портлет загружается, то до отображения первого представления в некоторых случаях возникает необходимость вызвать репозиторий, который изменяет данные в базе данных. Я не стал бы более подробно рассказывать о том, почему это необходимо, и ответы об этом, являющиеся недостатком дизайна, не помогают. Я знаю, что это дефект дизайна, но мне все же хотелось бы найти альтернативное решение следующей проблемы:

Проблема с этой настройкой заключается в том, что браузеры отправляют запросы предварительной загрузки. Например, URL-адрес страницы, в которой находится портлет, - это /test -portlet. Теперь, когда вы вводите его в свою адресную строку, тогда, если у вас есть его в истории браузера, браузер отправляет запрос GET на страницу уже, когда это вам предложит. Если вы нажмете клавишу ввода, прежде чем первый запрос GET будет разрешен, браузер отправит новый запрос GET. Это означает, что портлет получает 2 отдельных запроса, которые он начинает обрабатывать параллельно. Первая процедура базы данных может работать правильно, но с учетом характера процедуры базы данных второй вызов обычно дает исключение.

Каким будет хороший чистый способ справиться с вышеупомянутой проблемой из приложения Java?

Sidenote: Я использую Spring MVC.

Простой пример возможного контроллера:

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

Меня бы заинтересовало решение полностью заблокировать первое выполнение. Например, somekind перенаправления на POST с контроллера, который браузер не будет запускать. Не уверен, что это возможно.

Ответы

Ответ 1

Используя блокировки, я думаю, что вы могли бы решить эту проблему, чтобы запрос на отрыв ожидал завершения первого и последующей обработки. У меня нет опыта работы с блокировками в java, но я нашел еще один столбец обмена файлами о заблокированных файлах в jave: Как заблокировать файл с помощью java (если возможно)

Ответ 2

Обратитесь к этому ответу, это может помочь вам обнаружить и игнорировать некоторые запросы предварительной загрузки. Однако вы также должны убедиться, что работает "худший случай", возможно, используя блокировку, как это было предложено в @jpeg, но это может быть так же просто, как использовать блок synchronize где-то.

Ответ 3

Так как я не вижу, что chrome добавляет некоторый определенный заголовок (или как-либо уведомляет сервер о состоянии предварительной записи), вероятно, его невозможно обнаружить на стороне сервера... по крайней мере, не напрямую. Однако вы можете имитировать обнаружение на стороне клиента, а затем объединить его с вызовом сервера.

Обратите внимание, что вы можете обнаружить prerendering на стороне клиента:

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

Теперь вы можете разбить предварительную загрузку на стороне клиента, показывая окно предупреждения в случае, если браузер находится в состоянии предварительной загрузки (или вы, вероятно, можете сделать то же самое с некоторой ошибкой в ​​javascript, вместо использования alert()):

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

Теперь, когда chrome выполняет предварительную настройку страницы, она не будет работать, потому что предупреждение javascript не позволит браузеру продолжить выполнение javascript.

Если вы наберете chrome: chrome://net-internals/# prerender, вы можете отслеживать, когда и для каких страниц хром выполняет предварительную передачу. В случае вышеприведенного примера (с предупреждающим полем во время предварительной записи) вы можете видеть:

Ссылка Rel Prerender (крест домен) http://some.url.which.is.preloaded Javascript Предупреждение 2015-06-07 19: 26: 18.758

Последнее состояние - Javascript Alret доказывает, что хром не смог предварительно загрузить страницу (я протестировал это).

Теперь, как это решить вашу проблему? Ну, вы можете объединить это с асинхронным вызовом (AJAX) и загрузить некоторый контент (из другого URL-адреса) в зависимости от того, какая страница на самом деле является prerendering или нет.

Рассмотрим следующий код (который может отображаться вашим портлетом в url/test-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>  

Как вы видите, порт /hidden -portlet загружен только в том случае, если браузер загружает страницу обычно (без предварительной загрузки). Обработчик на стороне сервера под url/hidden-portlet (который может быть другим портлетом/сервлетом) содержит фактический код, который не должен выполняться во время предварительной записи. Таким образом, это /hidden -portlet, который выполняет

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

Этот портлет также может возвращать нормальный вид (визуализированный html), который будет асинхронно размещен на странице под URL-адресом/тестовым портлетом благодаря трюку/тестовый портлет: document.getElementById('content').innerHTML = xhr.responseText;.

Итак, чтобы суммировать портлет в файле address/test-portlet, возвращается html с кодом javascript, который запускает фактический портлет.

Если у вас много хрупких портлетов, вы можете пойти еще дальше, чтобы вы могли параметризовать вас/тестовый портлет с параметром запроса, например /test-portlet?actualUrl=hidden-portlet, чтобы адрес фактического портлета был взят из URL-адреса (который можно прочитать как параметр запроса на стороне сервера). В этом случае сервер будет динамически отображать URL-адрес, который должен быть загружен:

Итак, вместо hardcoded:

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