Spring RequestHeader

In this article, we will take a look at reading request headers in a spring controller for REST Apis or RestController.
This annotation is applicable both for Spring MVC and Spring boot.

Spring @RequestHeader
This annotation is applied before a parameter or argument in a controller method. Example,

@GetMapping("headerdemo")
public void headers(@RequestHeader("host") String host) {
 // code
}

When a request arrives, value of its header names host will be automatically injected into the method parameter.
This means that the string given after @RequestHeader matches with the name of the header.

Above code can also be written as

@GetMapping("headerdemo") 
public void headers(@RequestHeader(value = "host") String host) { 
  // code 
}

where value is an attribute of @RequestHeader.

If the name of method parameter is the same as header name, then the string after @RequestHeader or that given in value attribute can be omitted.
Thus, below code will also work

@GetMapping("headerdemo") 
public void headers(@RequestHeader String host) { 
  // code 
}

If the request does not contain header matching with the headers expected by the controller methods, then it will throw an HTTP 400 Bad Request error.
@RequestHeader optional header
As we saw above, if a parameter has @RequestHeader annotation, then a corresponding header must be present, else the request will fail.
This is not required in many cases, since a header may or may not be sent by client.
To prevent the request from failing, we can make the header as optional using required attribute of @RequestHeader and setting it to false. Example,

@GetMapping("headerdemo") 
public void headers(@RequestHeader(required=false) String host) { 
  // code 
}

required is true by default.

Remember that if we set the header optional this way, then the value of parameter will be set to null, in case the header is not present in the request.
We need to handle it or else there is chance of NullPointerException as per the use of this parameter.
There is another way to avoid this by setting a default value as explained further.

@RequestHeader default value
If we set a request header optional and it is not present in the incoming request, the parameter will be null, which may create problems later.
To avoid this, it is possible to set the default value of a method parameter using defaultValue attribute of @RequestHeader, if the corresponding header does not arrive in the request. Example,

@GetMapping("headerdemo") 
public void headers(@RequestHeader(required=false, 
             defaultValue="127.0.0.1") String host) { 
  // code 
}

Default value will only be set, if the header is not present in the request.
Reading multiple headers
It is possible to get more than one header values by applying @RequestHeader annotation to multiple parameters. Example,

@GetMapping("headerdemo") 
public void headers(@RequestHeader(value = "host") String host,
@RequestHeader("User-Agent") String userAgent) { 
  // code 
}

Above code will read two headers from the incoming request.

Remember that header names given in @RequestHeader are not case sensitive.
That is,

@RequestHeader("Host")
@RequestHeader("host")
@RequestHeader("hoSt")
@RequestHeader("HosT")

all these will work, if there is a header with the given name present in the request, irrespective of its case.
Reading all request headers
It might happen that you do not know the name of headers coming in the request, such as in the case of custom headers.
In such a case, above approach will not work.

To read all the headers, provide a parameter, which is of java Map type and annotate it with @RequestHeader.
When the request arrives, it will automatically be populated with entries, where the key of the map will be the name of header and value will be header value. Example,

@GetMapping("headerdemo")
public void headers(@RequestHeader Map<String, String> headers) {
  headers.keySet().forEach(h -> {
    System.out.println("Name: " + h + 
                       ", Value=: " + headers.get(h));
  });
}

Output is

Name: host, Value=: localhost:8080
Name: connection, Value=: keep-alive
Name: sec-ch-ua, Value=: ” Not A;Brand”;v=”99″, “Chromium”;v=”101″, “Google Chrome”;v=”101″
Name: sec-ch-ua-mobile, Value=: ?0
Name: sec-ch-ua-platform, Value=: “Windows”
Name: upgrade-insecure-requests, Value=: 1

Some values have been truncated for clarity.

In this example, we have used Java 8 forEach() method to iterate over header map.
Read all headers with HttpHeaders
There is another method of reading all request headers using HttpHeaders provided by Spring framework.
Instead of using a map type, change it to HttpHeaders. Example,

import org.springframework.http.HttpHeaders;

@GetMapping("headers")
public void headers(@RequestHeader HttpHeaders headers) {
  headers.keySet().forEach(h -> {
    System.out.println("Name: " + h + 
                       ", Value=: " + headers.get(h));	
  });
}

HttpHeaders is also based on a map with key and value pairs.

Mutli value headers
Till now, we have seen a single value associated with each request header.
There may be a header having multiple values.
To read such header values, use MultiValueMap, another Spring class, which is built over java Map.
It is used to hold multiple values as a java list against each key. Example,

@GetMapping("multiheaders")
public void headers(
  @RequestHeader MultiValueMap<String, String> headers) {
    headers.keySet().forEach(h -> {
    // get value of each header
    List<String> headerValues = headers.get(h);
    headerValues.forEach(v -> {
      System.out.println(v);
    });
  });
}

In all of the methods that read all request headers using Map, HttpHeaders or MultiValueMap, if a header is accessed by its name and it is not present, its value will be null.
Summary
In this article, we saw how to read request headers in a Spring controller with @RequestHeader annotation, in the following ways:

1. Specific header using its name

// header name after annotation
public void read(@RequestHeader("connection") String connect) {}

// header with name attribute
public void read(@RequestHeader(name = "connection") String connect) {}

// header name with value attribute
public void read(@RequestHeader(value="connection") String connect) {}

// header name same as parameter
public void read(@RequestHeader String connection) {}

2. Read all headers

// with java map
public void read(@RequestHeader Map<String, String> headers) { }

// with Spring HttpHeaders
public void read(@RequestHeader HttpHeaders<String, String> headers) { }

// with Spring MultiValueMap
public void read(
            @RequestHeader MultiValueMap<String, String> headers) { }

3. When reading a specific header with its name, it should be present in the request, else there will be 400 error.
4. Headers can be made optional by setting required attribute of @RequestHeader to false.
5. A default value for a header can be set using defaultValue attribute.

That is all for this article. Hope it was useful.