add location header to Spring MVC's POST response?
Asked Answered
C

4

21

My Spring Boot 1.4 application has this POST HTTP Method to create a resource. As a requirement, it should spit a location header specifying the URL of the newly created resource: RFC 9110 HTTP Semantics - Method Definitions. I am just wondering if there is any good way to do it than manually constructing the URL and adding it to the response.

Any helps/clues are deeply appreciated

Cudgel answered 2/3, 2017 at 3:11 Comment(1)
You can use ControllerLinkBuilder from Spring Data REST, but it has its shortcomings.Smoothbore
C
53

This exact scenario is demonstrated in the Building REST Services with Spring Guide.

Once you have persisted your new entity, in your controller you can use the below:

URI location = ServletUriComponentsBuilder
                    .fromCurrentRequest()
                    .path("/{id}")
                    .buildAndExpand(newEntity.getId())
                    .toUri();

You can then add this to your response using ResponseEntity as below:

ResponseEntity.created(location).build()

or

ResponseEntity.status(CREATED).header(HttpHeaders.LOCATION, location).build()

The latter expects a string so you can use toUriString() on the uri builder instead of toUri().

Carltoncarly answered 26/8, 2018 at 8:51 Comment(2)
Should'nt the URI be built on the service layer?Eason
@AlGrant I would say no because the URI is related the the HTTP endpoint and therefore I would encapsulate all work relating to it in the controller. Your service could be called from multiple places e.g. perhaps a terminal interface in which case the URI may not be relevant. It would depend on the architecture and use cases of your particular system though.Carltoncarly
S
3

Another way is to use UriComponentsBuilder. You can inject it right into a controller method:

@PostMapping(consumes = { MediaType.APPLICATION_JSON_UTF8_VALUE })
public ResponseEntity<Void> addCustomer(
          UriComponentsBuilder uriComponentsBuilder, 
          @RequestBody CustomerRequest customerRequest ){

        final long customerId = customerService.addCustomer(customerRequest)

        UriComponents uriComponents =
                uriComponentsBuilder.path("/{id}").buildAndExpand(customerId);

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

Take note that the following will not compute the current request path. You will need to add it manually (or I might be missing something).

ex:

UriComponents uriComponents =
       uriComponentsBuilder.path("CURRENT_REQUEST_PATH/{id}")
       .buildAndExpand(customerId);
Silken answered 13/9, 2019 at 11:29 Comment(0)
I
3
 @Autowired 
 private UserService service;

 @PostMapping("/users")
 public ResponseEntity<Object> createUser(@RequestBody User user)
  {
    User myUser = service.save(user);
    URI location = ServletUriComponentsBuilder
                .fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(myUser.getId())
                .toUri();

    ResponseEntity.created(location).build()
  }
Isolation answered 12/5, 2022 at 11:55 Comment(0)
U
1

This answer pointed me in the right direction, but my create entity endpoint path was slightly different from the path where the saved entity was stored.

I was able to use fromCurrentRequest() and replacePath("new path here") like so:

URI uri = ServletUriComponentsBuilder.fromCurrentRequest()
        .replacePath("/customer/{id}/fat")
        .buildAndExpand(savedCustomer.getCustomerId())
        .toUri();

return ResponseEntity.created(uri).build();
Unashamed answered 20/4, 2022 at 16:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.