我遇到了发布/重定向/获取模式的下一个问题。
重定向后执行GET
时,Chrome会从缓存中获取该页面。
因此用户可以看到过时的数据。
我已尝试过强制/支持重新验证
if (request.checkNotModified(sinceLastTweet)) return null;
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Last-Modified", String.valueOf(sinceLastTweet));
但只有no-store
会导致服务器请求。
为什么Chrome在执行重定向时从缓存中获取页面?
@RequestMapping(method = GET)
public String home(ModelMap model, @PathVariable String username, HttpServletResponse response, WebRequest request) {
// response.setHeader("Cache-Control", "no-store");
List<Tweet> tweets = tweetRepository.findAll();
// long sinceLastTweet = tweets.get(0).getTimestamp().toEpochMilli();
// if (request.checkNotModified(sinceLastTweet)) return null;
// response.setHeader("Cache-Control", "no-cache");
// response.setHeader("Last-Modified", String.valueOf(sinceLastTweet));
model.addAttribute("tweets", tweets);
model.addAttribute("tweet", new Tweet());
model.addAttribute("username", username);
return "home";
}
答案 0 :(得分:9)
1)您必须尝试强制Chrome不使用缓存
How to control web page caching, across all browsers?
就像那个问题的答案一样,你必须把这个标题放在http响应中:
response.header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.header("Pragma: no-cache"); // HTTP 1.0.
response.header("Expires: 0"); // Proxies.
2)你可以做一个小技巧
您可以使用相同的控制器对以下格式的任何请求进行编程:
http://localhost:8080/spitter/username-{timestamp}
然后在发送请求之前更改页面中的链接以获得最后一个时间戳。
在这种情况下,如果chrome缓存页面无关紧要的事件。
您可以使用任何其他方法,例如“autoincrement”或“uuid”,而不是时间戳。
答案 1 :(得分:7)
如果一个Chrome实例已缓存了网页/username
,则该Chrome页面未被重定向影响,因为Chrome会读取Location
标题并提供缓存页面。
也许有一次您在尝试缓存标题时访问了该页面,现在您的Chrome认为它必须缓存它,甚至可能永远存在。
您需要使/username
页面的缓存(使用该Chrome)无效:
/username
页面设置为始终,其中包含禁用缓存的标头,例如rev_dihazum建议的标头或Spring WebContentGenerator中的完成方式。/username
并在页面中点击CTRL + R:页面将被实际请求,将读取缓存禁用标题,从那时起每次再次请求页面服务器,包括请求何时作为重定向的效果。如果页面是公开的,而您无法控制其浏览器的用户已经缓存了该页面,则问题会更严重,您可以选择以下选项之一:
Location
网址,以使其唯一,变体为2)请注意,Last-Modified
时间格式为Tue, 15 Nov 1994 08:12:31 GMT
,您可以使用setDateHeader
方法将其设置为long
您可以查看WebContentGenerator以查看Spring中的其他缓存代码。
答案 2 :(得分:1)
也许您可以尝试在您的网页上使用ETag。
我不确定我是否正确理解了您的重定向方案。
我们假设您有一些包含username
的显示页面,并且您有第二页为您的数据提供编辑,并且您希望能够更改username
然后按一个按钮保存,然后返回显示页面。
一种选择是在网址中加入username
(例如...&username=Jack
)。由于用户名已更改,因此URL也会更改(即...&username=Jim
)。在这种情况下,Chrome将没有Jack的显示页面的缓存版本,并将从服务器获得它的新版本。 (如果您有任何您不想在网址中明确提及的字段,则可以计算所有字段的校验和,并将其添加为...&check=12345678
等参数。
另一个选择是让服务器生成一个ETag - 所以让我们详细研究一下。我们想要的是服务器应用程序负责告诉浏览器数据是否需要刷新或者可以从缓存中获取,对吗?
{请原谅我在这里没有使用你的例子,因为我目前正在另一个堆栈上开发 - 但是,我认为你的问题可以用你的Java / Spring Stack以同样的方式解决}
查看响应标题部分中的屏幕截图,并在中间看到 ETag 。此ID由服务器生成 - 因此服务器应用程序在此处受控制。 (如果你还没有实施ETag,也许这个article会帮助你。)
那会发生什么?一旦服务器上的内容发生变化,ETag也会发生变化 - 浏览器在响应中看到ETag,尊重服务器负责并询问服务器内容是否已更改,如果是,则将带有过期内容的缓存页面踢出,并从服务器请求新的内容。
当然,您必须确保一旦服务器得知用户已从 Jack 更改为 Jim (或相应的任何其他字段),服务器应用程序为该用户的显示页面生成新的ETag。
希望它有所帮助!
答案 3 :(得分:0)
在网址末尾添加一些随机数(以毫秒为单位的当前时间)。像服务网址
// start time
$open_time = strtotime("9:00:00");
// end time
$close_time = strtotime("23:59:00");
// selected time
$item['starttime'] = '09:30:00am';
// increment by seconds 15 mins = 900secs
for( $i=$open_time; $i<$close_time; $i+=900) {
$time = date("H:i:sa",$i);
echo "<option value='$time'" .(($item['starttime'] == $time) ? ' selected' : '') .">$time</option>";
}
因此,在进行上述服务调用时,不要拨打上述网址,而是在末尾添加一些随机数
http://localhost:7001/rest/getBook/5
在javascript中几乎不需要调整服务调用。
/rest/getBook/5?_time-in-milisec
由于每次URL不同,因此不会引用浏览器缓存。
答案 4 :(得分:0)
您可以使用POST而不是GET。 POST没有缓存。