深入了解HTTP HEAD请求

2023-04-13

HTTP HEAD请求

HEAD请求是一种HTTP请求方法,与GET请求类似,但不返回响应正文主体,只返回响应头部信息。它通常用于获取与资源相关的元数据,如响应状态码、响应头部、最后修改时间等,而不需要传输实际的资源内容。HEAD请求经常被用来检查服务器是否可用、文件是否存在以及检查资源的更新时间戳等操作,其请求及响应报文如下所示:

请求报文:


HEAD / HTTP/1.1

Host: www.devzhi.com

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36

Accept: */*

Accept-Encoding: gzip, deflate, br

Connection: keep-alive

响应报文:


HTTP/1.1 200 OK

Server: nginx/1.18.0 (Ubuntu)

Date: Thu, 13 Apr 2023 02:41:00 GMT

Content-Type: text/plain

Content-Length: 18

Connection: keep-alive

如何实现HEAD接口

Spring Boot

在Spring Boot中实现HEAD请求方法跟其他HTTP请求方法的实现方式基本相同,只需要在对应的Controller中定义一个使用@RequestMapping注解的方法,并设置其RequestMethod为HEAD即可。

举个例子,假设你要对某个请求路径”/example”实现HEAD请求方法,可以在Controller类中增加如下代码:


import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class ExampleController {

    @RequestMapping(value = "/example", method = RequestMethod.HEAD)

    public ResponseEntity<Void> headExample() {

        // 这里可以写一些业务逻辑

        // 返回一个空的ResponseEntity,表示当前请求资源存在

        return ResponseEntity.ok().build();

    }

}

在上述代码中,我们在方法headExample()上标注了@RequestMapping注解,并将其RequestMethod设置为HEAD。在方法体中,我们可以编写一些业务逻辑来处理该请求,然后通过返回一个空的ResponseEntity来表示当前请求资源存在。

需要注意的是,由于HEAD请求方法不会返回具体的内容,因此在返回ResponseEntity时,我们只需要返回状态码即可。如果当前请求的资源不存在,我们可以返回404状态码。

除此之外,在Spring Boot中实现GET接口后HEAD接口会被自动实现,Spring Boot会自动为对应的请求路径生成HEAD请求的处理逻辑。这是因为HTTP协议规定,在没有明确指定HEAD请求处理逻辑的情况下,服务器应该使用GET请求的处理逻辑来处理HEAD请求。

因此,如果你已经在Controller中实现了对应的GET请求方法,就不需要再显式地为同样的请求路径编写HEAD请求方法了。当客户端发送HEAD请求时,Spring Boot会自动调用对应的GET请求方法,并忽略其返回值,只返回请求头信息。

在 Spring Boot 中,处理 HTTP 请求的核心组件是 DispatcherServlet。当收到一个请求时,DispatcherServlet 会根据请求路径和请求方法查找对应的 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter,并将请求交给它们进行处理。

RequestMappingHandlerMapping 负责查找匹配当前请求的 Controller 方法,而 RequestMappingHandlerAdapter 则负责调用 Controller 方法并将其返回结果转换为响应内容。在默认情况下,Spring Boot 使用 DefaultRequestMappingHandlerAdapter 和 RequestMappingHandlerMapping 来处理 HTTP 请求。

对于 HEAD 请求,DispatcherServlet 会先调用 RequestMappingHandlerMapping 的getHandler() 方法来查找对应的处理器(handler)。在这个方法中,RequestMappingHandlerMapping 会通过遍历所有已注册的 HandlerMethod,找到对应的 GET 请求的处理器方法,并使用它来处理当前 HEAD 请求。

具体而言,就是首先查找是否有对应的 GET 请求方法,然后将其包装成一个空的 ResponseBody,并设置响应头信息,最后返回这个 ResponseBody 对象。这样,客户端就能够得到与 GET 请求相同的响应头信息,但是不会获取到响应正文内容。

如果在当前路径下没有对应的 GET 请求方法,则会返回一个 404 Not Found 响应。

下面是一个简化版的代码示例,展示了 Spring Boot 如何处理 HEAD 请求:


public class RequestMappingHandlerMapping {

    public Object getHandler(HttpServletRequest request) {

        // 查找当前请求的处理器方法

        HandlerMethod handlerMethod = findHandlerMethodForRequest(request);

        if (handlerMethod != null) {

            // 找到了对应的处理器方法,使用它来处理 HEAD 请求

            if ("HEAD".equals(request.getMethod())) {

                return new HandlerMethodReturnValueHandlerComposite().handleReturnValue(

                        new ResponseEntity<>(null, handlerMethod.getResponseHeaders(), HttpStatus.OK), 

                        handlerMethod,

                        new ServletWebRequest(request)

                );

            }

            // ...其他请求方法的处理逻辑

        }

        // 没有找到合适的处理器方法,返回 404 Not Found 响应

        return new ResponseEntity(HttpStatus.NOT_FOUND);

    }

    // ...其他辅助方法

}

在上述代码中,当 DispatcherServlet 收到一个 HEAD 请求时,会调用 RequestMappingHandlerMapping 的getHandler() 方法进行处理。

如果存在对应的 GET 请求方法,RequestMappingHandlerMapping 就会将其包装成一个空的 ResponseBody 对象,并设置响应头信息,最终返回这个对象作为响应结果。如果没有找到对应的 GET 方法,则会直接返回一个 404 Not Found 响应。

image-20230413105030946

Gin

与 Spring Boot 不同,Gin 框架不会自动为 GET 请求生成 HEAD 请求的处理方法。因此,如果要支持 HEAD 请求,你需要为对应的请求路径显式地编写一个处理 HEAD 请求的函数。

举个例子,假设你要对某个请求路径”/example”实现 HEAD 请求方法,可以在路由中增加如下代码:


import "github.com/gin-gonic/gin"

func main() {

    r := gin.Default()

    r.GET("/example", func(c *gin.Context) {

        // 这里是 GET 请求的业务逻辑

        c.String(http.StatusOK, "Hello, GET request!")

    })

    r.HEAD("/example", func(c *gin.Context) {

        // 这里是 HEAD 请求的业务逻辑

        c.Status(http.StatusOK)

    })

    r.Run(":8080")

}

如何使用curl发送HEAD请求

curl是一个非常强大的命令行工具,可以用来向服务器发送HTTP请求。要发送HEAD请求,只需使用”-I”选项即可。以下是一个使用curl发送HEAD请求的示例:


curl -I https://www.devzhi.com

上述命令将向http://www.devzhi.com发送一个HEAD请求,如果该资源存在,则服务器将返回响应头部信息。


$ curl -I https://www.devzhi.com

HTTP/1.1 404 Not Found

Server: nginx/1.18.0 (Ubuntu)

Date: Thu, 13 Apr 2023 02:52:52 GMT

Content-Type: text/plain

Content-Length: 18

Connection: keep-alive

注意到了吗,返回了404,因为目前我的博客是使用gin来实现的,而我没有去实现HEAD接口,这也是对前文关于Gin框架介绍部分的一个实际演示,什么?为什么文章一开头返回的是200 OK ?,那当然是为了把文章写下去啊。

HEAD请求的应用场景

  1. 检查资源是否存在:当你需要检查一个资源是否存在时,使用HEAD请求可以返回该资源的状态码和相关头信息,而无需下载整个资源。

  2. 获取资源的元数据:有时候,我们只需要获取资源的元数据,比如最后修改时间、大小等信息。HEAD请求可以帮助我们在不下载整个资源的情况下获取这些信息。

  3. 验证资源是否被修改:在某些情况下,我们需要在资源被修改后更新缓存。使用HEAD请求可以让我们检查资源的最后修改时间,从而判断是否需要更新缓存。

  4. 确认链接是否有效:当您需要在网站中创建链接时,使用HEAD请求可以验证链接是否指向有效的资源,本站对友情链接的检测便是采用的HEAD请求。