How to add “book” to /shelves/1/books

空扰寡人 提交于 2019-12-10 20:53:52

问题


I can't figure out how to do something that should be incredibly simple.

I have two entities: shelf and book. A shelf can have one or more books. Each of these entities has a corresponding JpaRepository exposed as a rest repository using Spring Data Rest. When I run the application, all of the query endpoints work perfectly, but I can't figure out how I can add a book to a shelf.

First I add a shelf by POSTing { "name":"westerns"} to /shelves (works fine).

Method 1: I try to add a book by POSTing { "name":"mybook"} to /shelves/1/books, but I get a 405 "method not allowed" error. Can I only post a book to the /books endpoint (without writing my own controller, I mean)? And if so, would I have to embed the shelf entity that the book belongs to?

Method 2: If I try to add a book by doing a PUT to /shelves/1 using the content { "name":"westerns", "books":[ {"name":"mybook"} ] }, I get the following error message back:

Message: "Could not read JSON: Template must not be null or empty! (through reference chain: org.demo.Shelf["books"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Template must not be null or empty! (through reference chain: org.demo.Shelf["books"])

The statcktrace:

2014-11-06 06:55:18.864 ERROR 9888 --- [nio-8080-exec-5] s.d.r.w.AbstractRepositoryRestController : Could not read JSON: Template must not be null or empty! (through reference chai n: org.demo.Shelf["books"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Template must not be null or empty! (through reference chain: org.demo.Shelf[" books"])

org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Template must not be null or empty! (through reference chain: org.demo.Shelf["books"]); nes ted exception is com.fasterxml.jackson.databind.JsonMappingException: Template must not be null or empty! (through reference chain: org.demo.Shelf["books"]) at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:228) at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readInternal(MappingJackson2HttpMessageConverter.java:212) at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:159) at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.resolveArgument(PersistentEntityResourceHandlerMethodArgumentResolver.java:100 ) at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:79) at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:157) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:124) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961) at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:874) at javax.servlet.http.HttpServlet.service(HttpServlet.java:649) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1736) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1695) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) Caused by: com.fasterxml.jackson.databind.JsonMappingException: Template must not be null or empty! (through reference chain: org.demo.Shelf["books"]) at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:232) at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:197) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1420) at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:244) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2993) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2158) at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:225) ... 38 common frames omitted Caused by: java.lang.IllegalArgumentException: Template must not be null or empty! at org.springframework.util.Assert.hasText(Assert.java:162) at org.springframework.hateoas.UriTemplate.(UriTemplate.java:56) at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$UriStringDeserializer.deserialize(PersistentEntityJackson2Module.java:380) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:227) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:204) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:23) at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:525) at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:99) at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:242) ... 42 common frames omitted

Here is all the relevant code (each class is in its own file, of course).

@Entity
public class Shelf {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String name;

    @OneToMany
    private List<Book> books;

    public List<Book> getBooks() {
    return books;
    }

    public void setBooks(List<Book> books) {
    this.books = books;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

}

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


}

@RepositoryRestResource
public interface BookRepository extends JpaRepository<Book, Long>{

}

@RepositoryRestResource
public interface ShelfRepository extends JpaRepository<Shelf, Long>{

}

I'm using Spring Boot 1.1.8.RELEASE


回答1:


The Book entities have their own REST endpoint and to create one you have to POST to that endpoint.

To put a book on a shelf that you have to send the URI of that book to the association URI (/shelves/{id}/books). Surprisingly the documentation has it wrong and adding items is not done via POST but PATCH:

PATCH http://localhost:8080/shelves/1/books
Content-Type: text/uri-list 
http://localhost:8080//books/1

You can add more than one book at once, one URI per line. If you don't want to add books, but rather replace all books on the shelf with new ones, i.e. replace the whole collection, you can use PUT instead.




回答2:


I think I've found the most straightforward way to do this (I've made the Book-Shelf relationship bi-directional).

  1. Create the shelf that needs to hold the book by posting the shelf {"name:"westerns"} to /shelves
  2. Post the new book with the URI of the parent shelf by posting { "name":"mybook", "shelf":"localhost:8080/shelves/1" } to /books

This works perfectly. Well, sort of. The book is created with the correct link to the parent, but now I'm having an issue with the parent not showing the book under /shelves/1/books. I posted a new question for that here.



来源:https://stackoverflow.com/questions/26768897/how-to-add-book-to-shelves-1-books

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!