The current (Spring 3) version of WebRequest.checkNotModified(long lastModifiedDate) combines both the "check" and the "set" steps when deciding whether or not a request is cached/modified. I think the method mistakenly assumes that the lastModifiedDate can always be computed in a persistent manner, which is typically not the case.
An example is helpful:
Code:
@Controller
public class ExampleController {
private long lastModified = -1; //not persistent, gets reset on server restart...
@RequestMapping(value = "/example", method = RequestMethod.GET)
public String example(WebRequest webRequest) {
if (webRequest.checkNotModified(lastModified)) {
// shortcut exit - no further processing necessary
return null;
}
// further request processing, actually building content
lastModified = System.currentTimeMillis();
return "movie";
}
}
This code will rarely result in a HTTP 304 response.
- On the first request, checkNotModified checks the header (no value) and passed in last modified date (-1) and sets nothing in the response header since the last modified date is -1.
- On the second request, checkNotModified checks the header (no value) and passed in last modified date (now at time_1) and sets the response header to a date of time_1. It still returns false though, since the response 'was modified' based on the request's timestamp (no value < time_1). As a result, the "further processing" occurs, and lastModified is locally moved forward to time_2.
- On the third request, checkNotModified checks the header (now at time_1) and passed in last modified date (now at time_2) and sets the response header to a date of time_2. It still returns false though, since the response 'was modified' based on the request's timestamp (time_1 < time_2). As a result, the "further processing" occurs, and lastModified is moved forward to time_3.
- This last pattern repeats for each request, incrementing the time on each request but the response time is always behind the local last modified time.
(Note, last modified dates are rounded down to the nearest second. As a result it is possible to get a time_1 and time_2 that are == after dropping the milliseconds. Thus, in rare cases, it will appear to work.)
I think (looking for advice here) that the two steps need to be broken apart so that controller code would look more like the following:
Code:
@RequestMapping(value = "/example", method = RequestMethod.GET)
public String example(WebRequest webRequest) {
if (webRequest.isNotModified(lastModified)) {
// shortcut exit - no further processing necessary
return null;
}
// further request processing, actually building content
lastModified = System.currentTimeMillis();
webRequest.setLastModified(lastModified);
return "movie";
}
Thoughts?