服务器禁止head 请求
HEAD是一个经常被遗忘的HTTP方法(动词),其行为类似于GET,但不返回正文。 您使用HEAD来检查资源的存在(如果不存在,它应该返回404),并确保您的缓存中没有陈旧的版本。 在这种情况下,您期望304 Not Modified ,而200则意味着服务器具有最新版本。 例如,您可以使用HEAD来高效地实施软件更新。 在这种情况下, ETag是您的应用程序版本(生成,标记,提交哈希),并且您具有固定的/most_recent端点。 您的软件会在ETag发送当前版本的HEAD请求。 如果没有更新,则服务器将以304答复。如果为200,则可以询问用户是否要升级而不下载软件。 最终请求GET /most_recent将始终下载您软件的最新版本。 HTTP的力量!
在servlet中,默认情况下, HEAD是在您应该重写的doHead()实现的。 默认实现只是委派给GET但丢弃主体。 这效率不高,尤其是当您从外部(例如Amazon S3)加载资源时。 幸运的是(?)Spring MVC默认情况下不实现HEAD,因此您必须手动执行。 让我们从HEAD的一些集成测试开始:
def 'should return 200 OK on HEAD request, but without body'() {
expect:
mockMvc
.perform(
head('/download/' + FileExamples.TXT_FILE_UUID))
.andExpect(
status().isOk())
.andExpect(
content().bytes(new byte[0]))
}
def 'should return 304 on HEAD request if we have cached version'() {
expect:
mockMvc
.perform(
head('/download/' + FileExamples.TXT_FILE_UUID)
.header(IF_NONE_MATCH, FileExamples.TXT_FILE.getEtag()))
.andExpect(
status().isNotModified())
.andExpect(
header().string(ETAG, FileExamples.TXT_FILE.getEtag()))
}
def 'should return Content-length header'() {
expect:
mockMvc
.perform(
head('/download/' + FileExamples.TXT_FILE_UUID))
.andExpect(
status().isOk())
.andExpect(
header().longValue(CONTENT_LENGTH, FileExamples.TXT_FILE.size))
}
实际的实现非常简单,但是为了避免重复需要一些重构。 下载端点现在接受GET和HEAD:
@RequestMapping(method = {GET, HEAD}, value = "/{uuid}")
public ResponseEntity<Resource> download(
HttpMethod method,
@PathVariable UUID uuid,
@RequestHeader(IF_NONE_MATCH) Optional<String> requestEtagOpt,
@RequestHeader(IF_MODIFIED_SINCE) Optional<Date> ifModifiedSinceOpt
) {
return storage
.findFile(uuid)
.map(pointer -> new ExistingFile(method, pointer))
.map(file -> file.handle(requestEtagOpt, ifModifiedSinceOpt))
.orElseGet(() -> new ResponseEntity<>(NOT_FOUND));
}
我创建了一个新的抽象ExistingFile ,它封装了我们在其上调用的找到的FilePointer和HTTP动词。 ExistingFile.handle()具有通过HEAD提供文件或仅提供元数据所需的一切:
public class ExistingFile {
private static final Logger log = LoggerFactory.getLogger(ExistingFile.class);
private final HttpMethod method;
private final FilePointer filePointer;
public ExistingFile(HttpMethod method, FilePointer filePointer) {
this.method = method;
this.filePointer = filePointer;
}
public ResponseEntity<Resource> handle(Optional<String> requestEtagOpt, Optional<Date> ifModifiedSinceOpt) {
if (requestEtagOpt.isPresent()) {
final String requestEtag = requestEtagOpt.get();
if (filePointer.matchesEtag(requestEtag)) {
return notModified(filePointer);
}
}
if (ifModifiedSinceOpt.isPresent()) {
final Instant isModifiedSince = ifModifiedSinceOpt.get().toInstant();
if (filePointer.modifiedAfter(isModifiedSince)) {
return notModified(filePointer);
}
}
return serveDownload(filePointer);
}
private ResponseEntity<Resource> serveDownload(FilePointer filePointer) {
log.debug("Serving {} '{}'", method, filePointer);
final InputStreamResource resource = resourceToReturn(filePointer);
return response(filePointer, OK, resource);
}
private InputStreamResource resourceToReturn(FilePointer filePointer) {
if (method == HttpMethod.GET)
return buildResource(filePointer);
else
return null;
}
private InputStreamResource buildResource(FilePointer filePointer) {
final InputStream inputStream = filePointer.open();
return new InputStreamResource(inputStream);
}
private ResponseEntity<Resource> notModified(FilePointer filePointer) {
log.trace("Cached on client side {}, returning 304", filePointer);
return response(filePointer, NOT_MODIFIED, null);
}
private ResponseEntity<Resource> response(FilePointer filePointer, HttpStatus status, Resource body) {
return ResponseEntity
.status(status)
.eTag(filePointer.getEtag())
.lastModified(filePointer.getLastModified().toEpochMilli())
.body(body);
}
}
resourceToReturn()至关重要。 如果返回null ,Spring MVC将不包含任何主体作为响应。 其他所有内容均保持不变(响应标头等)
编写下载服务器
- 第一部分:始终流式传输,永远不要完全保留在内存中
- 第二部分:标头:Last-Modified,ETag和If-None-Match
- 第三部分:标头:内容长度和范围
- 第四部分:有效地执行
HEAD操作 - 第五部分:油门下载速度
- 第六部分:描述您发送的内容(内容类型等)
- 这些文章中开发的示例应用程序可在GitHub上找到。
服务器禁止head 请求
本文探讨了HTTP中的HEAD请求,用于检查资源存在和验证缓存,特别是在软件更新场景中的应用。服务器默认可能不高效处理HEAD请求,但在Spring MVC中需要手动实现。示例介绍了如何编写高效的HEAD请求处理,确保响应中不包含主体,同时维持正确的响应头信息。此外,还提到了一个系列博客的其他部分,涵盖了下载服务器的各种优化技术。

239

被折叠的 条评论
为什么被折叠?



