Spring MVC 获取HTTP请求头的几种方法

作者 | 2020年4月29日

1. 前言

在本文中,我们将学习几种在Spring Rest Controller中读取HTTP请求头(HTT Headers)的方法。

首先,我们学习使用@RequestHeader读取一个或者全部请求头,然后我们再深入学习@RequestHeader注解的一些常用属性。

2. 读取HTTP请求头

2.1 读取单个请求头

如果我们需要读取一个特定的请求头,我们可以使用@RequestHeader注解并指定要读取的请求头名称:

@GetMapping("/greeting")
public ResponseEntity<String> greeting(@RequestHeader("accept-language") String language) {
    // code that uses the language variable
    return new ResponseEntity<String>(greeting, HttpStatus.OK);
}

然后我们就可以使用参数变量language访问请求头accept-language的内容了。如果HTTP请求中不存在该请求头,则会返回400 Bad Request错误。

此外我们的header也可以为字符串之外的其它类型。例如,如果我们知道请求头中存放的是数字,则可以将变量声明为int类型:

@GetMapping("/double")
public ResponseEntity<String> doubleNumber(@RequestHeader("my-number") int myNumber) {
    return new ResponseEntity<String>(String.format("%d * 2 = %d", 
      myNumber, (myNumber * 2)), HttpStatus.OK);
}

2.2 一次获取全部的请求头

如果我们希望获取全部的HTTP请求头,我们可以不给@RequestHeader注解指定请求头的名称。

此时,方法中与请求头绑定的参数类型可以是:Map、MultiValueMap 、HttpHeaders 。

首先,我们来看一下把所有HTTP请求头绑定为Map类型(Key是请求头的名称,键是对应的值):

@GetMapping("/listHeaders")
public ResponseEntity<String> listAllHeaders(
  @RequestHeader Map<String, String> headers) {
    headers.forEach((key, value) -> {
        LOG.info(String.format("Header '%s' = %s", key, value));
    });

    return new ResponseEntity<String>(
      String.format("Listed %d headers", headers.size()), HttpStatus.OK);
}

如果同样名称的请求头在请求中出现多次,此时使用Map类型绑定HTTP请求头则只能取到第一个值。如果我们要获取全部的值则可以使用MultiValueMap类型绑定HTTP请求头:

@GetMapping("/multiValue")
public ResponseEntity<String> multiValue(
  @RequestHeader MultiValueMap<String, String> headers) {
    headers.forEach((key, value) -> {
        LOG.info(String.format(
          "Header '%s' = %s", key, value.stream().collect(Collectors.joining("|"))));
    });

    return new ResponseEntity<String>(
      String.format("Listed %d headers", headers.size()), HttpStatus.OK);
}

我们也可以使用HttpHeaders对象来绑定请求头:

@GetMapping("/getBaseUrl")
public ResponseEntity<String> getBaseUrl(@RequestHeader HttpHeaders headers) {
    InetSocketAddress host = headers.getHost();
    String url = "http://" + host.getHostName() + ":" + host.getPort();
    return new ResponseEntity<String>(String.format("Base URL = %s", url), HttpStatus.OK);
}

3. @RequestHeader的属性

现在我们已经学习了@RequestHeader注解,下面我们研究一下它的一些属性。

属性valuename是等价的,这意味着下面的三段代码可以实现一样的功能:

public ResponseEntity<String> greeting(@RequestHeader("accept-language") String language) {}
public ResponseEntity<String> greeting(
  @RequestHeader(name = "accept-language") String language) {}
public ResponseEntity<String> greeting(
  @RequestHeader(value = "accept-language") String language) {}

默认情况下,如果给@RequestHeader注解指定了具体的请求头名称,但是HTTP请求中却没有找到该请求头,则会抛出400错误。

我们可以把required属性设为false,这样当HTTP请求中该请求头不存在时就不会抛出400错误(但是会把值设为null):

@GetMapping("/nonRequiredHeader")
public ResponseEntity<String> evaluateNonRequiredHeader(
  @RequestHeader(value = "optional-header", required = false) String optionalHeader) {
    // 检测optionalHeader是否为null,null即表示HTTP请求中不存在该请求头
    return new ResponseEntity<String>(String.format(
      "Was the optional header present? %s!",
        (optionalHeader == null ? "No" : "Yes")),HttpStatus.OK);
}

我们还可以使用defaultValue属性,给该请求头一个默认值,如果HTTP请求中没找到该请求头则使用默认值:

@GetMapping("/default")
public ResponseEntity<String> evaluateDefaultHeaderValue(
  @RequestHeader(value = "optional-header", defaultValue = "3600") int optionalHeader) {
    return new ResponseEntity<String>(
      String.format("Optional Header is %d", optionalHeader), HttpStatus.OK);
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注