What is the preferred way to specify an HTTP "Location" Response Header in Spring MVC 3?

java, rest, spring-mvc

Solution

The key point is to use `UriComponentsBuilder`. There are several ways how you can get the instance of it

- preconfigured `UriComponentsBuilder` from `MvcUriComponentsBuilder`

- `UriComponentsBuilder` injected as parameter to method

Preconfigured `UriComponentsBuilder` from `MvcUriComponentsBuilder`

This way you can get `UriComponentsBuilder` that is configured to produce `URI` that points to some controller methods with predefined parameters.

Here is example from the javadoc for `MvcUriComponentsBuilder`:

For example, given this controller:

 @RequestMapping("/people/{id}/addresses")
 class AddressController {

   @RequestMapping("/{country}")
   public HttpEntity<Void> getAddressesForCountry(@PathVariable String country) { ... }

   @RequestMapping(value="/", method=RequestMethod.POST)
   public void addAddress(Address address) { ... }
 }

A UriComponentsBuilder can be created:

 // Inline style with static import of "MvcUriComponentsBuilder.on"

 MvcUriComponentsBuilder.fromMethodCall(
    on(AddressController.class).getAddressesForCountry("US")).buildAndExpand(1);

Another options which sometimes may be preferable is to specify controller method by name:

UriComponents uriComponents = MvcUriComponentsBuilder.fromMethodName(
    AddressController.class, "getAddressesForCountry", "US").buildAndExpand(1);
URI nextUri = uriComponents.toUri();

`UriComponentsBuilder` injected as parameter to method

As of spring 3.1 `Location` can be crafted using `UriComponentBuilder` parameter and set it to the returned `ResponseEntity`. `UriComponentBuilder` is aware of the context and manipulates with relative paths:

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createCustomer(UriComponentsBuilder b) {

    UriComponents uriComponents = 
        b.path("/customers/{id}").buildAndExpand(id);

    HttpHeaders headers = new HttpHeaders();
    headers.setLocation(uriComponents.toUri());
    return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}

Since version 4.1 you can make it even shorter

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createCustomer(UriComponentsBuilder b) {

    UriComponents uriComponents = 
        b.path("/customers/{id}").buildAndExpand(id);

    return ResponseEntity.created(uriComponents.toUri()).build();
}

Thanks to Dieter Hubau to pointing this out.

Problem

What is the preferred way to specify an HTTP "Location" Response Header in Spring MVC 3? As far as I can tell, Spring will only provide a "Location" in response to a redirect ("redirect:xyz" or RedirectView), however there are scenarios where a Location should be sent along with the entity body (ex, as a result of a "201 Created"). I'm afraid my only option is manually specifying it: ``` httpServletResponse.setHeader("Location", "/x/y/z"); ``` Is this correct? Is there a better way to tackle this problem?

Original source