How do I return 304 Unmodified status with Express

2019-04-11 15:35发布

问题:

I'm using node and express for the back end of an iOS application. Data is stored in a SQL Server database, so iOS apps query the server, the server queries the database, server receives the db response, and then forwards the response to the iOS application. I'm trying to figure out how caching works though. I'm serving a lot of static content - blog articles for example. So I planned to use etags, but I'm not sure how it's supposed to work. I make a request, get content, and cache the response on the client side. Ok. Then I make the same request later with the etag of the previous response stored in the 'If-None-Match' header. Then what?

Does Express.js handle this automatically? It doesn't seem to - I couldn't get it to generate a 304 response. If I try to check the response headers before I send the response, I get null, so I can't get the etag of a response before I send it. So how am I supposed to compare the request etag with the etag of the content that the server would send back? Am I supposed to use custom generated etags and cache these on the server, then compare request etags with this cache?

Below is a very simple route I set up to test this, no database involved. I just send a number to the server, and it returns the square. If I send a request with an etag to the same url, I will get the same response. I can check the 'If-None-Match' header of the request, but to what do I compare it in order to determine if I should send a 304 instead of a 200 status?

router.use("/square/:testId", function(req, res) {

  var obj = {};
  obj["testId"] = req.params.testId;
  obj["result"] = req.params.testId * req.params.testId;

  res.setHeader('Cache-Control', 'public, max-age=5');

  var h2 = JSON.stringify(res.headers,null,2);
  console.log("The response headers: " + h2);
  //Prints null

  res.status(200).send(obj);
});

回答1:

Am I supposed to use custom generated etags and cache these on the server, then compare request etags with this cache?

Yes, you can use something like the etag npm module which can take a Buffer or string and provide you with a strong etag value: res.setHeader('ETag', etag(body)). If you are storing modification dates in your database then you could also simply send those down to the client in the form of a Last-Modified header. In that case the client would end up sending an If-Modified-Since in subsequent requests which you could then use to compare to the modification column of the resource being requested.

In either case you will be responsible for responding with the appropriate status code, headers, and body.

If you're serving static files from the actual file system then you could just use the serve-static express middleware which will automatically provide the correct caching behavior.



回答2:

There is no need to use any third-party module like etag. Express handles the logic behind etag and no need to write code.

  1. Enable etag in Express. In the example below, we are using strong etags.

    // Use string ETag  
    app.set('etag', 'strong');  
    
  2. Build your response in the GET method:

    app.get('/my-data', (req, res) => {   
       ...
       res.append('Last-Modified', (new Date(lastModifiedStringDate)).toUTCString());
       return res.send(response);   
    }
    

Voilá, Express will send either the content of your response 200 OK or an empty body with response 304 Not Modified.

Clients submitting requests with header If-None-Match will receive HTTP 304 Not Modified, if the etag has not changed.

Make sure your requests do not send Cache-Control: no-cache. Tools like Postman per default do send the header Cache-Control: no-cache, which invalidates the Express ETag (no cache is no cache after all). You can change this behaviour by toggling it in Postman's settings.

We are adding the Last-Modified as a plus in our response. You do not need to use it as we do. But Express will also respect the If-Modified-Since: header using similar logic (temporal), instead of content (Etags).