HTTP(超文本传输协议)协议通常使用状态码来表示服务器的响应状态。其中,状态码为304表示 Not Modified,该状态码通常会在浏览器发出请求时被使用。这意味着浏览器已经具有请求的资源的最新版本,因此服务器可以选择不返回内容,而浏览器可以从本地缓存中读取数据,减少了服务器压力并提高了页面加载速度。
一、HTTP缓存机制中的Not Modified
在HTTP缓存机制中,当浏览器要求访问某个资源时,它首先检查缓存中是否存在该资源的拷贝,并检查该资源在缓存中是否仍然有效。如果缓存中的资源是最新的,则浏览器可以直接从缓存中读取该资源,而不会向服务器发送请求。
当浏览器发送条件请求时,服务器将返回状态码304,表示该资源未修改,浏览器可以直接从缓存中读取该资源。
下面是一个使用Python的Flask框架来演示的代码示例:
from flask import Flask, request, make_response app = Flask(__name__) @app.route('/image') def image(): image_file = open("image.png", "rb").read() response = make_response(image_file) response.headers.set('Cache-Control', 'public, max-age=3600') response.headers.set('Content-Type', 'image/png') return response if __name__ == "__main__": app.run()
该代码使用了Flask框架来启动一个简单的HTTP服务器,并向客户端返回一个示例图片。由于请求中包含了Cache-Control头部信息,浏览器将在1个小时内缓存该资源。在下一次请求中,客户端将使用条件请求,并根据服务器的响应决定是否使用缓存来加速页面加载。
二、使用Etag实现Not Modified
Etag是HTTP协议提供的一种机制,用于检测资源的内容是否已经被修改。服务器在返回资源时,可以通过设置Etag头来指定该资源的特征值。浏览器在向服务器发出条件请求时,将Etag头包含在请求中,服务器可以使用该值来判断是否有必要返回资源的内容或返回状态码304。
下面是一个使用Node.js的Express框架来使用Etag实现Not Modified的代码示例:
const express = require('express'); const fs = require('fs'); const app = express(); const PORT = 3000; const FILE_PATH = './resources/myfile.txt'; app.get('/file', (req, res) => { fs.readFile(FILE_PATH, (err, data) => { const etag = '"' + generateETag(data) + '"'; if (req.headers['if-none-match'] === etag) { res.status(304).end(); return; } res.set({ 'ETag': etag, 'Content-Type': 'text/plain', 'Cache-Control': 'public, max-age=3600', }); res.send(data); }); }); function generateETag(data) { const hash = require('crypto').createHash('md5'); hash.update(data); return hash.digest('hex'); } app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });
该代码使用了Node.js的Express框架来启动HTTP服务器,并根据请求返回一个文本文件的内容。通过使用Crypto模块来计算文件内容的哈希值,并为每个文件资源生成独立的Etag。在接收到条件请求时,服务器使用实体标签来检查资源是否已经过期,从而控制是否返回响应内容。
三、使用Last-Modified实现Not Modified
Last-Modified是HTTP协议另一种可用于检测资源是否已经被修改的机制。服务器在返回资源时,可以通过设置Last-Modified头来指定最新的修改日期和时间。浏览器在向服务器发出条件请求时,将If-Modified-Since头部信息包含在请求中,服务器可以使用该值来决定是否必须返回响应内容或仅返回状态码304。
下面是一个使用Java的Spring框架来使用Last-Modified实现Not Modified的代码示例:
@RestController public class ApiController { @GetMapping(value = "/api") public ResponseEntitygetResource( @RequestHeader(value = "If-Modified-Since", required = false) String modifiedSince) { HttpHeaders headers = new HttpHeaders(); headers.setCacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS).getHeaderValue()); Path file = Paths.get("/resources/myfile.txt"); FileTime lastModified = Files.getLastModifiedTime(file); headers.setLastModified(lastModified.toMillis()); if (modifiedSince != null) { ZonedDateTime modifiedSinceTime = ZonedDateTime.parse(modifiedSince).withZoneSameInstant(ZoneId.systemDefault()); if (Files.getLastModifiedTime(file).toInstant().toEpochMilli() <= modifiedSinceTime.toInstant().toEpochMilli()) { return ResponseEntity.status(HttpStatus.NOT_MODIFIED).headers(headers).build(); } } String contents = new String(Files.readAllBytes(file), StandardCharsets.UTF_8); return ResponseEntity.ok().headers(headers).body(contents); } }
该代码使用Spring框架来启动一个简单的HTTP服务器,并使用Last-Modified标头为资源添加最新修改时间戳。当客户端在随后的请求中发出条件请求时,服务器使用If-Modified-Since标头来确定资源是否已过期,从而仅在必要时返回实际数据,减轻了服务器负载。
总结
Not Modified状态码是HTTP协议中用于表示资源是否已更新的状态码。在HTTP缓存机制中,Not Modified状态码可以有效减轻服务器的负担,提高页面加载速度;在使用Etag或Last-Modified时,服务器可以检测资源的内容是否已经被修改,从而在必要时仅返回状态码而不是实际数据,减少无谓的网络传输。