The response header 'ETag' and the request header 'If-None-Match' are used to cache resources on the clients. Comparing to Last-Modified-Header, using ETag is a more generic and efficient way to cache resources. Also 'Last-Modified' can be understood by the clients, whereas, 'ETag' is entirely understood and used on the server-side logic.
Please check out definitions and general step-by-step usage of ETag/If-None-Match headers if not already familiar.
In this tutorial we will show how to use the two headers in Java Servlet.
Example
The Servlet
@WebServlet(urlPatterns = "/test")
public class MyServlet extends HttpServlet {
@Override
protected void doGet (HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
String eTagFromBrowser = req.getHeader("If-None-Match");
String eTagFromServer = getETag();
if (eTagFromServer.equals(eTagFromBrowser)) {
//setting 304 and returning with empty body
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
resp.addHeader("ETag", getETag());
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.write("<h4>My Servlet</h4>");
writer.write(LocalDateTime.now().toString());
writer.write("<br/><a href='test'>test</a>");
}
private String getETag () {
//Using hard coded value, in real scenario this value might be auto-generated
//from the resource content, for example, by using a hash function.
return "\"version1\"";
}
}
The logger filter
Like our last example we are going to use same HeaderLogFilter to log the request/response headers.
@WebFilter(urlPatterns = {"/*"})
public class HeaderLogFilter implements Filter {
@Override
public void init (FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter (ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse rep = (HttpServletResponse) response;
System.out.println("----- Request ---------");
Collections.list(req.getHeaderNames())
.forEach(n -> System.out.println(n + ": " + req.getHeader(n)));
chain.doFilter(request, response);
System.out.println("----- response ---------");
rep.getHeaderNames()
.forEach(n -> System.out.println(n + ": " + rep.getHeader(n)));
System.out.println("response status: " + rep.getStatus());
}
@Override
public void destroy () {
}
}
Running the web application
Run the embedded Jetty (the plugin is included in the pom.xml):
mvn jetty:run
Output at /test
The console output, showing the ETag header in the response:
----- Request ---------
Cookie: JSESSIONID=424B4AF1976CBE891F2B470AA97C4575
Cache-Control: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Host: localhost:8080
Pragma: no-cache
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8
----- response ---------
ETag: "version1"
Date: Sat, 04 Feb 2017 03:00:09 GMT
Content-Type: text/html;charset=utf-8
response status: 200
Subsequent refresh/reload will not reload the resource instead that will return 304 code with empty body. The browser will use cached copy of the resource:
----- Request ---------
Cookie: JSESSIONID=424B4AF1976CBE891F2B470AA97C4575
If-None-Match: "version1"
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Host: localhost:8080
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8
----- response ---------
Date: Sat, 04 Feb 2017 03:01:58 GMT
response status: 304
Note that this time (comparing to the last tutorial Last-Modified/If-Modified-Since example) clicking on the link 'test' will also do server side validation. That's the reason we don't need response header like 'Cache-Control:no-cache' to force validation.
Now change the ETag value to 'version2' in the method MyServlet#getETag() and restart the server. On reloading the page or clicking on 'test' link:
----- Request ---------
Cookie: JSESSIONID=424B4AF1976CBE891F2B470AA97C4575
If-None-Match: "version1"
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Referer: http://localhost:8080/test
Host: localhost:8080
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8
----- response ---------
ETag: "version2"
Date: Sat, 04 Feb 2017 03:02:42 GMT
Content-Type: text/html;charset=utf-8
response status: 200
Example ProjectDependencies and Technologies Used: - javax.servlet-api 3.1.0 Java Servlet API
- jetty-maven-plugin 9.4.1.v20170120: Jetty maven plugins.
- JDK 1.8
- Maven 3.3.9
|