doGet和doHead方法的区别

5

我知道doHead方法不会通过http发送body,但是doGet方法也不会。 这两者之间有明确的区别吗? 谢谢。


请参阅HTTP方法定义 - Edwin Dalorzo
2个回答

8

简短回答

HEAD方法只会发送与GET请求相同的头部信息,但不包含实体主体。GET方法会发送这些头部信息和实体主体。当然,在极少数或有争议的情况下,如果GET方法在其主体中什么都不发送,那么GET方法和HEAD方法行为将相似。

书籍《HTTP 权威指南》补充道:

这让客户端能够检查资源的标题,而无需实际获取资源。使用HEAD方法,您可以:

  • 了解资源(例如,确定其类型)而无需获取它。
  • 通过查看响应的状态代码来查看对象是否存在。
  • 通过查看标头测试资源是否已修改。

服务器开发人员必须确保返回的头部正是GET请求会返回的头部。 HEAD方法也是符合HTTP / 1.1要求的。

因此,doHead是否调用doGet可能只是一个实现细节,但如果是这样,doHead需要在发送响应之前删除doGet的主体。但这种实现是低效的,因为理想情况下,doHead不应该承担执行整个doGet请求以确定要发送哪些头部的困难。

某些头部的计算可能会带来一些困难。例如,“Content-Length”头需要我们知道实际资源的大小,因此HEAD方法可能需要“获取”资源以确定相应GET请求的字节数大小,但它不会使网络负担过重,也不会将资源发送回来,尽管它可能会使服务器负担加重,以便确定要发送哪些头部(即Content-Length、Content-Type、Content-Language等)

详细回答

HTTP规范中提到:

9.4 HEAD

HEAD方法与GET方法相同,但服务器不得在响应中返回消息正文。响应头中包含的元信息应与响应GET请求时发送的信息相同。此方法可用于获取有关请求所涉及的实体的元信息,而无需传输实体正文本身。此方法经常用于测试超文本链接的有效性、可访问性和最近修改。

对HEAD请求的响应可以被缓存,因为响应中包含的信息可以用于从该资源更新先前缓存的实体。如果新字段值表明缓存的实体与当前实体不同(如Content-Length、Content-MD5、ETag或Last-Modified的更改所示),则缓存必须将缓存条目视为过期。

而对于GET,则说:

9.3 GET

GET方法表示检索由请求URI标识的任何信息(以实体形式)。如果请求URI引用数据生成过程,则应该将生成的数据作为响应中的实体返回,而不是该过程的源文本,除非该文本恰好是该过程的输出。

如果请求消息包括If-Modified-Since、If-Unmodified-Since、If-Match、If-None-Match或If-Range头字段,则GET方法的语义将更改为“条件GET”。条件GET方法请求仅在由条件头字段描述的情况下传输实体。条件GET方法旨在通过允许刷新缓存的实体而无需多次请求或传输客户端已持有的数据来减少不必要的网络使用。

如果请求消息包括Range头字段,则GET方法的语义将更改为“部分GET”。部分GET请求仅传输实体的一部分,如第14.35节所述。部分GET方法旨在通过允许完成部分检索的实体而无需传输客户端已持有的数据来减少不必要的网络使用。

只有满足第13节中描述的HTTP缓存要求的响应才可以被缓存。

有关表单使用时的安全性,请参见第15.1.3节。

如果您查看javax.servlet.http.HttpServlet的源代码,您会发现其文档说:

从受保护的服务方法接收 HTTP HEAD 请求并处理请求。客户端在只想查看响应头部(例如 Content-Type 或 Content-Length)时发送 HEAD 请求。HTTP HEAD 方法会统计响应中的输出字节数,以准确设置 Content-Length 头。如果您重写此方法,则可以避免计算响应体,直接设置响应头部以提高性能。请确保编写的 doHead 方法既安全又幂等(即保护自己不被多次调用一次 HTTP HEAD 请求)。如果 HTTP HEAD 请求格式不正确,doHead 将返回一个 HTTP "Bad Request" 消息。
默认的方法实现如下所示:
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
   NoBodyResponse response = new NoBodyResponse(resp);
   doGet(req, response);
   response.setContentLength();
}

如果你阅读NoBodyResponse的代码,你会发现它只是一个响应,其输出流会丢弃所有数据,并仅计算字节数以确定相应GET响应的正确Content-Length。因此,这是Servlet规范建议如何生成有效的HEAD响应的方式。只要遵循HTTP规范指南,你可以覆盖此行为以使其更加高效。

我在某个地方读到,doHead 调用了 doGet,这可能让我感到困惑。不过现在我明白了,谢谢。 - Rollerball
@Rollerball:- 是的,你说得对。HttpServlet类的doHead方法调用doGet方法,并仅将响应头返回给客户端。 - Rahul Tripathi
@Rollerball:如果你想的话,你可以通过使用java.net.Socket或java.net.URL类,向一个servlet发送自定义的doHead请求来进行测试。 - Rahul Tripathi
1
@RahulTripathi 谢谢澄清。 - Rollerball
@Edwin Dalorzo,非常感谢您的回答,解释得非常清楚。但是,“如果是这样,doHead需要在发送响应之前剥离doGet的正文。” - Rollerball
@Rollerball 这正是默认的 Java Servlet 实现所做的。但您可以覆盖它以提高效率。例如,如果您知道 GET 请求将发送的所有标头,则可以避免 doHead 实际调用 doGet,并通过此方式避免使服务器负担整个 doGet 的工作。但是,您可能需要在每种情况下进行确定,或者仅使用默认实现。我已经添加了更多详细信息来回答您的问题。 - Edwin Dalorzo

1

doGet()用于拦截HTTP GET请求。而

根据DoHead的文档:

接收受保护服务方法的HTTP HEAD请求并处理该请求。当客户端只想查看响应头,如Content-Type或Content-Length时,它会发送HEAD请求。 HTTP HEAD方法计算响应中的输出字节数以准确设置Content-Length标头。

如果您重写此方法,则可以避免计算响应正文并直接设置响应标头以提高性能。确保编写的doHead方法既安全又幂等(即,保护自己不被调用多次进行一次HTTP HEAD请求)。

如果HTTP HEAD请求格式不正确,doHead将返回HTTP“ Bad Request”消息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接