Search code examples
springspring-bootspring-dataspring-data-restspring-hateoas

Spring get link to an entity


I'm using Spring-boot-starter-data-jpa, in my RestController I want to return the Location of newly created object. Is there a way to reverse the @RequestMapping's and not hardcode how to build the URL?

@RestController
@ExposesResourceFor(BookInstance.class)
public class BookInstanceController {
    @RequestMapping(value="/bookInstances", method=RequestMethod.POST)
    ResponseEntity<BookInstance> createBookInstance(@RequestBody BookInstance bookInstance){
    BookInstance createdBookInstance = bookInstanceRepository.save(bookInstance);

    return ResponseEntity.created(**reverseURL(createdBookInstance)**);
//      return new ResponseEntity<BookInstance>(createdBookInstance, HttpStatus.CREATED);
//      return createdBookInstance;
    }
}

I always see people hard-coding their URL construction in this function, which just leaves me without words....

Of course I also have a GET function in the same class (otherwise there wouldn't be anything to reverse)

@RequestMapping(value="/bookInstances/{id}", method=RequestMethod.GET)
ResponseEntity<?> findOne(@PathVariable("id") Long id){
        BookInstance bookInstance = bookInstanceRepository.findOne(id);
        if(bookInstance == null){
            return ResponseEntity.notFound().build();
        }
        return new ResponseEntity<BookInstance>(bookInstance, HttpStatus.OK);
    }

Solution

  • An alternative solution that uses a Resource assembler:

    public class BookInstanceResource extends Resource<BookInstance> {
    
        public BookInstanceResource(Book content, Link... links) {
            super(content, links);
        }
    
    }
    
    public class BookInstanceResourceAssembler extends ResourceAssemblerSupport<BookInstance, BookInstanceResource> {
    
        public BookInstanceResourceAssembler() {
            super(BookInstanceController.class, BookInstanceResource.class)
        }
    
        @Override
        public BookInstanceResource toResource(BookInstance bookInstance) {
            // linkTo requires the following static import:
            // import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
            ControllerLinkBuilder builder = linkTo(BookInstance.class).slash("bookInstances").slash(bookInstance);
    
            return new BookInstanceResource(bookInstance,
                                            builder.withSelfRel(),
                                            builder.withRel("bookInstance");
            }
    }
    

    In your controller class:

    @Autowired
    private BookInstanceResourceAssembler resourceAssembler;
    
    @GetMapping(value = "/bookInstances/{id}")
    ResponseEntity findOne(@PathVariable("id") Long id) {
        BookInstance bookInstance = bookInstanceRepository.findOne(id);
        if(bookInstance == null){
            return ResponseEntity.notFound().build();
        }
    
        BookInstanceResource resource = resourceAssembler.toResource(bookInstance);
    
        return ResponseEntity.created(URI.create(resource.getLink("self").getHref()))
                             .body(resource);
    }