Jim Kennedy
Jim Kennedy

Reputation: 822

Add custom header to REST PUT request

I have an application that uses Spring Boot 2.1.8 with an Angular front end. I have defined my repositories using @RepositoryRestResource. The Spring Boot app basically provides a REST API for the Angular piece. The application defines several business entities and the REST respo provides CRUD functionality for each entity. Here is a typical repo interface for one of those entities:

// REST resouce /api/privgroups
@RepositoryRestResource(collectionResourceRel = "privgroups", path = "privgroups")
public interface PrivGroupRepository extends CrudRepository<PrivGroup, Long>
{
   List<PrivGroup> findAll();
}

GETing and single entity, POSTing (creating an entity) and PUTing (updating an entity) are all working fine, but I would like to return a custom header when updating (HTTP PUT) an entity. The header would be consumed by the Angular side to display a custom toastr message specific to that entity. Since the repositories also implement the REST interface I am unsure how to add a specific header that would change based on the target entity.

I have developed applications that include a REST controller that calls against a service which in turn calls against a repository. In this case, I have more control and can easily return custom headers like so:

@PutMapping("/{id}")
public ResponseEntity<MyEntity> updateMyEntity(@PathVariable("id") Long id, @RequestBody MyEntity myEntity)
{
    MyEntity updatedEntity = this.MyEntityService.updateMyEntity(MyEntity);
    return ResponseEntity.ok()
        .header("X-Save", "MyEntity")
        .body(updatedEntity);
}

Is there a newer "built-in" technique I can use to achieve this? I know I can add headers using a filter and I've read a couple posts on the subject. I think it would be difficult to identify the entity being updated and I'm unsure this is the best approach.

NOTE that this post: Rest API - how add custom headers? is really old. The spring data rest docs https://docs.spring.io/spring-data/rest/docs/current/reference/html/ don't have anything specific on the subject.

Upvotes: 3

Views: 2209

Answers (2)

Alan Hay
Alan Hay

Reputation: 23246

The answer by @pepevalbe looks promising.

An alternative might be - as you suggested - using a standard Servlet Filter or Spring HandlerInterceptor.

To address the issue of getting a reference to the modified entity in that case you could register a Spring Data Rest event listener that would simply store a reference to the modified entity in ThreadLocal storage from where it could be retrieved in the Filter or HandlerInterceptor.

https://www.baeldung.com/java-threadlocal

https://docs.spring.io/spring-data/rest/docs/current/reference/html/#events.application-listener

Upvotes: 0

pepevalbe
pepevalbe

Reputation: 1380

I have used Spring Data Rest recently and I didn't find any "built-in" technique for that. However you can achieve that by implementing the ResponseBodyAdvice interface in a class annotated with @ControllerAdvice. This is how I got it:

@ControllerAdvice
public class PutMyEntityBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {

        // Write the condition to check if beforyBodyWrite should be called
        return returnType.getParameterType().equals(ResponseEntity.class);
    }

    @Override
    public Object beforeBodyWrite(Object object, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {

        // Check if it is a PUT request for MyEntity object
        if (HttpMethod.PUT.equals(request.getMethod()) && object instanceof PersistentEntityResource) {
            PersistentEntityResource persistentEntityResource = (PersistentEntityResource) object;
            if (persistentEntityResource.getContent().getClass().equals(MyEntity.class)) {
                // Add custom header or manipulate response object
                response.getHeaders().add("X-Save", "MyEntity");
            }
        }
        return object;
    }
}

Upvotes: 2

Related Questions