Watch the video about this article at YouTube or use link https://www.youtube.com/watch?v=sh2rvIZ7vx0
1. Introduction
In this article, I will discuss how to inject the request headers in the Spring Framework. We will use @RequestHeader annotation provided in the Spring.
2. Content
We will create an interface and use its implementation for checking the headers’ injection. The interface name is IHello, and the implementation is HelloImpl. We will look at all the methods one by one. At the end, I will provide the entire codebase for interface and implementation. I will use POSTMAN to run GET requests.
3. Define interface
Request path is /v1/hello.
@RequestMapping(path = "/v1/hello") public interface IHello { }
4. Define class
HelloImpl class implements the IHello interface. I annotated HelloImpl class with @RestController.
@RestController public class HelloImpl implements IHello { }
5. Read all headers
Let us get right into it. We will read all the headers that came as part of the request. For this we will use org.springframework.web.bind.annotation.RequestHeader annotation.
But we want to do it in a responsible way. Never annotate this annotation in the implementation class. Put it in the interface. Why? Because restful web services are a contract. So the contract of accessing the required headers must be in interface, not in implementation class.
Now, there are 3 different ways you can read all the headers.
- Using java.util.Map<String, String>
- Using org.springframework.util.MultiValueMap<String, String>
- Using org.springframework.http.HttpHeaders
Let us put methods in the IHello interface to access headers in all the 3 ways.
@RequestMapping( method = RequestMethod.GET, path = "/all-headers-map") public ResponseEntity<String> sayHelloAllHeaders( @RequestHeader Map<String, String> headers); @RequestMapping( method = RequestMethod.GET, path = "/all-headers-multi-map") public ResponseEntity<String> sayHelloAllHeaders( @RequestHeader MultiValueMap<String, String> headers); @RequestMapping( method = RequestMethod.GET, path = "/all-headers-httpheaders") public ResponseEntity<String> sayHelloAllHeaders( @RequestHeader HttpHeaders headers);
Let us now implement these methods in HelloImpl class.
@Override public ResponseEntity<String> sayHelloAllHeaders( Map<String, String> headers) { headers.forEach((k, v) -> System.out.println(k + " : " + v)); return new ResponseEntity<String>("request-id : " + headers.get("request-id"), HttpStatus.OK); } @Override public ResponseEntity<String> sayHelloAllHeaders( MultiValueMap<String, String> headers) { headers.forEach((k, v) -> System.out.println(k+" : "+v)); return new ResponseEntity<String>("request-id : " + headers.get("request-id").get(0), HttpStatus.OK); } @Override public ResponseEntity<String> sayHelloAllHeaders( HttpHeaders headers) { headers.forEach((k, v) -> System.out.println(k+" : "+v)); return new ResponseEntity<String>("request-id : " + headers.get("request-id").get(0), HttpStatus.OK); }
If the headers are present below, get printed on the console for all the above 3 methods.
request-id : c4e6c422-4553-4e4c-ab35-fe93f3cedef5 user-agent : PostmanRuntime/7.25.0 accept : */* cache-control : no-cache postman-token : c887c01e-93ff-415f-9308-9522b132a074 host : localhost:8080 accept-encoding : gzip, deflate, br connection : keep-alive
In POSTMAN, we will get below string as a response
request-id : c4e6c422-4553-4e4c-ab35-fe93f3cedef5
What if the headers are not present? We will get 400 BAD REQUEST error.
6. Read one header at a time using the name
We could read all the headers in the previous section. What if we want to read just one header? We can do so by passing a name field in @RequestHeader annotation. You can also use a value field instead of name. It will work just fine. We can add either of the below methods in the IHello interface. Both of the below methods are identical.
@RequestMapping( method = RequestMethod.GET, path = "/one-header") public ResponseEntity<String> sayHelloSpecificHeader( @RequestHeader(name = "request-id") String requestId); @RequestMapping( method = RequestMethod.GET, path = "/one-header") public ResponseEntity<String> sayHelloSpecificHeader( @RequestHeader(value = "request-id") String requestId);
The implementation in class HelloImpl looks like this.
@Override public ResponseEntity<String> sayHelloSpecificHeader( String requestId) { return new ResponseEntity<String>(requestId, HttpStatus.OK); }
If the request id exists, then it will be populated in ResponseEntity and returned as a response.Â
Output in POSTMAN when we provide request-id in header in request :
request-id : d2c32a91-776c-4c79-b572-80d0aef6eb93
What if the header is not present? Then Spring returns with 400 BAD REQUEST error.
{ "timestamp": 1594171400166, "status": 400, "error": "Bad Request", "message": "Missing request header 'request-id' for method parameter of type String", "path": "/v1/hello/one-header" }
You will see this error in your log.
Resolved [org.springframework.web.bind.MissingRequestHeaderException: Missing request header ‘request-id’ for method parameter of type String]
7. Reading optional header using name and required
In the previous section, we saw that if the header we are expecting is not present, 400 error is thrown. What if you still want to process the request with the header? You can do so by using an attribute called required of @RequestHeader annotation.
@RequestMapping( method = RequestMethod.GET, path = "/optional-header") public ResponseEntity<String> sayHelloOptionalHeaderNotRequired( @RequestHeader(name = "request-id", required = false) String requestId);
Implementation in class HelloImpl is done as below :
@Override public ResponseEntity<String> sayHelloOptionalHeaderNotRequired( String requestId) { final ResponseEntity<String> responseEntity; if (requestId != null) { responseEntity = new ResponseEntity<String>( "request-id : " + requestId, HttpStatus.OK); } else { responseEntity = new ResponseEntity<String>( "request-id : " + requestId, HttpStatus.OK); } return responseEntity; }
If the request-id is present, then it is displayed as below in POSTMAN.
request-id : 62b834b2-206b-4ce1-824f-7a1d4e09810f
If the request-id is not present, then it is displayed as below in POSTMAN.
request-id : null
8. Providing default header value using defaultValue
If the header is not present, then we want to provide a default value for that header. We can do so using defaultValue parameter in @RequestHeader parameter.Â
Below is the method that goes in the IHello interface.
@RequestMapping( method = RequestMethod.GET, path = "/optional-header-default-value") public ResponseEntity<String> sayHelloOptionalHeaderDefaultValue( @RequestHeader( name = "request-id", defaultValue = "default-value") String requestId);
HelloImpl class’s implementation of this method is as below:Â
@Override public ResponseEntity<String> sayHelloOptionalHeaderDefaultValue( String someHeader) { return new ResponseEntity<String>( "request-id : " + requestId, HttpStatus.OK); }
If the request-id parameter is passed in header POSTMAN prints this :
request-id : cd5e454e-302b-43f2-888e-24b9556647a3
If the request-id parameter is not passed in header POSTMAN prints this :
request-id : default-value
9. Entire Implementation
IHello Interface
import java.util.Map; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @RequestMapping(path = "/v1/hello") public interface IHello { /** * Prints all the headers and returns {@link HttpStatus#OK}. * * Returns {@link HttpStatus#OK} if headers are present else * returns {@link HttpStatus#BAD_REQUEST}. */ @RequestMapping( method = RequestMethod.GET, path = "/all-headers-map") public ResponseEntity<String> sayHelloAllHeaders( @RequestHeader Map<String, String> headers); /** * @see #sayHelloAllHeaders(Map) */ @RequestMapping( method = RequestMethod.GET, path = "/all-headers-multi-map") public ResponseEntity<String> sayHelloAllHeaders( @RequestHeader MultiValueMap<String, String> headers); /** * @see #sayHelloAllHeaders(Map) */ @RequestMapping( method = RequestMethod.GET, path = "/all-headers-httpheaders") public ResponseEntity<String> sayHelloAllHeaders( @RequestHeader HttpHeaders headers); /** * Returns {@link HttpStatus#OK} if Request-ID header is present * else returns {@link HttpStatus#BAD_REQUEST}. */ @RequestMapping( method = RequestMethod.GET, path = "/one-header") public ResponseEntity<String> sayHelloSpecificHeader( @RequestHeader(name = "request-id") String requestId); /** * Returns {@link HttpStatus#OK} along with request-id if * request-id header is present else returns * {@link HttpStatus#OK} and null request-id. */ @RequestMapping( method = RequestMethod.GET, path = "/optional-header") public ResponseEntity<String> sayHelloOptionalHeaderNotRequired( @RequestHeader( name = "request-id", required = false) String requestId); /** * Returns {@link HttpStatus#OK} with the value of header in * request. If the value doesn't exists then it will return * default value. */ @RequestMapping( method = RequestMethod.GET, path = "/optional-header-default-value") public ResponseEntity<String> sayHelloOptionalHeaderDefaultValue( @RequestHeader( name = "some-header", defaultValue = "some-header-default-value") String someHeader); }
HelloImpl class
import java.util.Map; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloImpl implements IHello { /** * {@inheritDoc} */ @Override public ResponseEntity<String> sayHelloAllHeaders( Map<String, String> headers) { headers.forEach((k, v) -> System.out.println(k+" : "+v)); return new ResponseEntity<String>("request-id : " + headers.get("request-id"), HttpStatus.OK); } /** * {@inheritDoc} */ @Override public ResponseEntity<String> sayHelloAllHeaders( MultiValueMap<String, String> headers) { headers.forEach((k, v) -> System.out.println(k+" : "+v)); return new ResponseEntity<String>("request-id : " + headers.get("request-id").get(0), HttpStatus.OK); } /** * {@inheritDoc} */ @Override public ResponseEntity<String> sayHelloAllHeaders( HttpHeaders headers) { headers.forEach((k, v) -> System.out.println(k+" : "+v)); return new ResponseEntity<String>("request-id : " + headers.get("request-id").get(0), HttpStatus.OK); } /** * {@inheritDoc} */ @Override public ResponseEntity<String> sayHelloSpecificHeader( String requestId) { return new ResponseEntity<String>( "request-id : " + requestId, HttpStatus.OK); } /** * {@inheritDoc} */ @Override public ResponseEntity<String> sayHelloOptionalHeaderNotRequired( String requestId) { final ResponseEntity<String> responseEntity; if (requestId != null) { responseEntity = new ResponseEntity<String>( "request-id : " + requestId, HttpStatus.OK); } else { responseEntity = new ResponseEntity<String>( "request-id : " + requestId, HttpStatus.OK); } return responseEntity; } /** * {@inheritDoc} */ @Override public ResponseEntity<String> sayHelloOptionalHeaderDefaultValue( String someHeader) { return new ResponseEntity<String>( someHeader, HttpStatus.OK); } }
10. Conclusion
That is all on accessing the HTTP headers in the RestController of Spring Framework.
Code can be found on Github.