Java 更改GET页面请求上的数据(处理预加载请求)

Java 更改GET页面请求上的数据(处理预加载请求),java,spring,spring-mvc,portlet,Java,Spring,Spring Mvc,Portlet,我有一个portlet。加载portlet时,在呈现第一个视图之前,在某些情况下需要调用存储库来更改数据库中的数据。我不会详细说明为什么这是必要的,关于这是一个设计缺陷的答案也没有帮助。我知道这是一个设计缺陷,但我仍然希望找到以下问题的替代解决方案: 这种设置的问题是,浏览器发送预加载请求。例如,portlet所在页面的URL是/testportlet。现在,当您在地址栏中键入它时,如果您的浏览器历史记录中有它,那么浏览器会在向您建议该页面时向该页面发送GET请求。如果在解决第一个GET请求之前

我有一个portlet。加载portlet时,在呈现第一个视图之前,在某些情况下需要调用存储库来更改数据库中的数据。我不会详细说明为什么这是必要的,关于这是一个设计缺陷的答案也没有帮助。我知道这是一个设计缺陷,但我仍然希望找到以下问题的替代解决方案:

这种设置的问题是,浏览器发送预加载请求。例如,portlet所在页面的URL是/testportlet。现在,当您在地址栏中键入它时,如果您的浏览器历史记录中有它,那么浏览器会在向您建议该页面时向该页面发送GET请求。如果在解决第一个GET请求之前按enter键,则浏览器将发送一个新的GET请求。这意味着portlet接收2个单独的请求,并开始并行处理。第一个数据库过程可能正常工作,但考虑到数据库过程的性质,第二个调用通常会给出一个异常

从Java应用程序中处理上述问题的干净方法是什么

旁注:我使用的是SpringMVC

一个可能的控制器的简单示例:

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

我对完全阻止第一次执行的解决方案感兴趣。例如,从控制器重定向到POST,浏览器不会触发。但不确定是否可以实现。

使用锁,我认为您可以解决它,让secound请求等待第一个请求完成,然后再处理它。我对java中的锁没有经验,但我发现另一篇关于jave中文件锁的堆栈交换帖子:

请参阅,它可能有助于您检测和忽略某些预加载请求。不过,您还应该确保“最坏情况”可以正常工作,也许可以使用@jpeg建议的锁定,但也可以像在某处使用
同步
块一样简单。

因为我看不到chrome添加了一些特定的头(或者无论如何通知服务器预渲染状态)可能无法在服务器端检测到它。。。至少不是直接的。不过,您可以在客户端模拟检测,然后将其与服务器调用相结合

请注意,您可以在客户端检测预渲染:

if (document.webkitVisibilityState == 'prerender' ||  document.visibilityState ==    'prerender' || document.visibilityState[0] == 'prerender') {
    // prerendering takes place
}  
现在,如果浏览器处于预加载状态,您可以通过显示警报框来中断客户端的预加载(或者您也可以通过javascript中的一些错误而不是使用alert())来执行此操作:

现在,当chrome预呈现页面时,它将失败,因为javascript警报将阻止浏览器继续执行javascript

如果键入chrome:chrome://net-internals/#prerender 您可以跟踪chrome何时以及为哪些页面执行预渲染。在上述示例中(预渲染期间带有警报框),您可以看到:

链接Rel预渲染(交叉 域)Javascript 警报2015-06-07 19:26:18.758

最终状态——Javascript Alret证明chrome无法预加载页面(我已经测试过了)

这怎么解决你的问题呢?好的,您可以将其与异步调用(AJAX)相结合,并根据页面是否实际预呈现加载一些内容(从另一个url)

考虑以下代码(可能由您的portlet在url/testportlet下呈现):

此portlet还可以返回正常视图(呈现的html),由于trick-on/test portlet:
document.getElementById('content')。innerHTML=xhr.responseText

因此,要在address/testportlet下汇总portlet,portlet只返回带有触发实际portlet的javascript代码的html

如果您有许多脆弱的portlet,那么您可以进一步使用它,这样您就可以使用请求参数对您/测试portlet进行参数化,比如
/testportlet?actualur=hiddenportlet
,以便实际portlet的地址取自url(可以在服务器端作为请求参数读取)。在这种情况下,服务器将动态呈现应加载的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); 

一个可能的想法,但是第二个请求仍然会被处理。我更感兴趣的是一个完全阻止两次执行的解决方案。例如,来自控制器的某种重定向,浏览器不会在预加载请求时触发该重定向。不确定是否可以实现。不过谢谢。这是http协议的问题,我认为这是不可能的。浏览器进行了大量的“scetcy”优化,使它们能够执行大量请求,这些请求对于它们请求的服务器来说都是困难的,而对于需要传输的数据量来说则是不明智的。让chrome等浏览器与您的网站配合使用的唯一方法是确保它能够处理。但是看看连接类型为http的协议,如果你能按你的要求去做,这就是你可以做到的。你检查过请求头了吗?是否有任何标题表明您处于“预加载”场景中?在这种情况下,你可以跳过你需要跳过的任何内容。你能简要描述一下你的数据库过程是什么吗???@MSIbrahim我刚刚简化了这个场景。实际上,有一个RESTAPI调用,它处理2个数据库。我不认为这是相关的,因为我当然不想在较低的级别上进行更改,因为这完全是web层的问题that@MSIbrahim有点不清楚你的问题到底是什么。正如我在第一个问题中所说的,我已经考虑过一个可能的get/redirect/post(“例如,从控制器重定向到post,浏览器不会触发。但不确定是否可以实现”),但是
dummyRepository.changeSomeData(request.getAttribute("userId"));
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);