0

I am trying to follow best practices imagine a basic API with /books endpoint.

I've the following classes: Book BookRepository (PagingAndSortingRepository but not exported!!) BookController (this is the serving class) BookResource ("representation" of the book in "REpresentational State Transfer") BookResourceAssembler

My BookController looks like this:

@RequestMapping(path="/{id}", method = RequestMethod.GET)
    public ResponseEntity<?> getBook(@PathVariable Long id) {
        Book book = bookRepository.findOne(id);
        BookResource bookResource = this.bookResourceAssembler.toResource(book);
        return ResponseEntity.ok(bookResource);
    }

my BookResource looks like this:

public class BookResource extends ResourceSupport{
    public String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

}

BookResourceAssembler:

@Component
public class BookResourceAssembler extends ResourceAssemblerSupport<Book, BookResource> {

      public BookResourceAssembler() {
        super(BookController.class, BookResource.class);
      }


    @Override
    public BookResource toResource(Book entity) {
        BookResource resource = createResourceWithId(entity.getDbid(), entity);
        return resource;
    }
}

My first problem is easy one to solve I guess, title is not initialized so when I call /books/1 I get title: null.

Second and more important question is how do I do content-negotiation and versioning, which is a really important aspect of RESTful API. How should I introduce BookResourceV2 and where should I negotiate it? Let's say I want

"Content-Type: application/vnd.company.book+json.v2"

where do I state this? Which new classes/functions should I add to handle this v2 of the same resource?

I couldn't find a good tutorial that covers all aspects.

EralpB
  • 1,621
  • 4
  • 23
  • 36

1 Answers1

0

My first problem is easy one to solve I guess, title is not initialized so when I call /books/1 I get title: null.

You're using BookResource the wrong way. I assume your domain object (Book) looks something like:

public class Book {
    public String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

}

BookResource is a wrapper that adds links to the domain object and present it in a RESTful way...

public class BookResource extends Resource<Book> {

    public BookResource(Book content, Link... links) {
        super(content, links);
    }

}

For the record, in this case you don't even need custom Resource and ResourceAssembler classes:

@RequestMapping(path="/{id}", method = RequestMethod.GET)
public ResponseEntity getBook(@PathVariable Long id,
    PersistentEntityResourceAssembler assembler) {
    Book book = bookRepository.findOne(id);
    if (null == book) {
        throw new ResourceNotFoundException();
    }

    return ResponseEntity.ok(assembler.toResource(book));
}

In this very simple case (no special processing of the Book instance), you wouldn't even need this endpoint, since Spring generates it for you.

Second and more important question is how do I do content-negotiation and versioning, which is a really important aspect of RESTful API. How should I introduce BookResourceV2 and where should I negotiate it?

You need to define a RepositoryRestController and use the produces parameter of your RequestMapping-annotated controller endpoints, similarly to what I explained in this post.

Community
  • 1
  • 1
Marc Tarin
  • 3,109
  • 17
  • 49