【问题标题】:How to add "book" to /shelves/1/books如何将“书”添加到 /shelves/1/books
【发布时间】:2015-01-02 08:03:03
【问题描述】:

我不知道怎么做应该非常简单的事情。

我有两个实体:书架和书。一个书架可以有一本或多本书籍。这些实体中的每一个都有一个相应的 JpaRepository 暴露为使用 Spring Data Rest 的 rest 存储库。当我运行应用程序时,所有查询端点都可以正常工作,但我不知道如何将一本书添加到书架上。

首先我通过将 { "name":"westerns"} 发布到 /shelves 来添加一个架子(工作正常)。

方法 1: 我尝试通过将 { "name":"mybook"} 发布到 /shelves/1/books 来添加一本书,但我收到 405“方法不允许”错误。我可以只将一本书发布到 /books 端点(我的意思是不编写我自己的控制器)吗?如果是这样,我是否必须嵌入该书所属的书架实体?

方法 2: 如果我尝试通过使用内容 { "name":"westerns", "books":[ {"name" 对 /shelves/1 执行 PUT 来添加一本书:"mybook"} ] },我收到以下错误消息:

消息:“无法读取 JSON:模板不能为空或空!(通过引用链:org.demo.Shelf["books"]);嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException: Template不能为空或空!(通过引用链:org.demo.Shelf["books"])

statcktrace:

2014-11-06 06:55:18.864 错误 9888 --- [nio-8080-exec-5] s.d.r.w.AbstractRepositoryRestController:无法读取 JSON: 模板不能为空或为空! (通过参考链: org.demo.Shelf["books"]);嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException:模板不得 为空或为空! (通过引用链:org.demo.Shelf["books"])

org.springframework.http.converter.HttpMessageNotReadableException: 无法读取 JSON:模板不能为空或为空! (通过 参考链:org.demo.Shelf["books"]);嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException:模板不得 为空或为空! (通过参考链:org.demo.Shelf["books"]) 在 org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:228) 在 org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readInternal(MappingJackson2HttpMessageConverter.java:212) 在 org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:159) 在 org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.resolveArgument(PersistentEntityResourceHandlerMethodArgumentResolver.java:100 ) 在 org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:79) 在 org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:157) 在 org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:124) 在 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749) 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689) 在 org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) 在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938) 在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870) 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961) 在 org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:874) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:649) 在 org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:727) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 在 org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) 在 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 在 org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) 在 org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) 在 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) 在 org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) 在 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) 在 org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) 在 org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070) 在 org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611) 在 org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1736) 在 org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1695) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 在 org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 在 java.lang.Thread.run(Thread.java:745) 引起: com.fasterxml.jackson.databind.JsonMappingException:模板不得 为空或为空! (通过参考链:org.demo.Shelf["books"]) 在 com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:232) 在 com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:197) 在 com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1420) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:244) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118) 在 com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2993) 在 com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2158) 在 org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:225) ... 省略了 38 个常见帧 原因: java.lang.IllegalArgumentException:模板不能为空或 空的!在 org.springframework.util.Assert.hasText(Assert.java:162) 在 org.springframework.hateoas.UriTemplate.(UriTemplate.java:56) 在 org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$UriStringDeserializer.deserialize(PersistentEntityJackson2Module.java:380) 在 com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:227) 在 com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:204) 在 com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:23) 在 com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:525) 在 com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:99) 在 com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:242) ...省略了42个常用框架

这是所有相关代码(当然,每个类都在自己的文件中)。

@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>{

}

我正在使用 Spring Boot 1.1.8.RELEASE

【问题讨论】:

  • 您不发布 Book,但在这种情况下您应该 PUT Shelve。或者,如果您建立双向关系,您可以将 Book 发布到 books 端点。
  • @M.Deinum,好的,所以我试过了,我得到了 204,但实际上没有创建任何书(并且日志中没有错误)。我检查了货架,它是空的。此外,/books 下没有任何内容。
  • 嗯 wel 204 没有内容,所以看起来你没有把书放在正确的书架上/shelves/1
  • @M.Deinum,哦,我在 /shelves/1/books 上做 PUT。所以现在当我在 /shelves/1 上放置一个书架 { "name":"westerns", "books":[ {"name":"mybook"} ] } 时出现错误,“模板不能为空或为空!(通过引用链:org.demo.Shelf["books"])"。
  • @zeroflagL,你能给我一个示例 json,说明如何将 /books/1 uri 发布到 /shelves/1/books?

标签: spring-boot spring-data-rest


【解决方案1】:

Book 实体有自己的 REST 端点,要创建一个,您必须 POST 到该端点。

要将一本书放在书架上,您必须将该书的 URI 发送到关联 URI (/shelves/{id}/books)。令人惊讶的是,文档有错误,添加项目不是通过 POST 而是通过 PATCH 完成的:

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

您可以一次添加多本书,每行一个 URI。如果您不想添加书籍,而是将书架上的所有书籍替换为新书籍,即替换整个收藏,则可以使用 PUT。

【讨论】:

  • 你找到了什么文档,@zeroflagL?感谢您的回答。
  • @GalenYoder official documentation 告诉你一点,但它也说你可以使用 POST,这是错误的。在启动时的日志文件中可以看到只支持PATCH,所以我试了一下。
【解决方案2】:

我想我找到了最直接的方法来做到这一点(我已经将书架关系设为双向)。

  1. 通过将书架 {"name:"westerns"} 发布到 /shelves 来创建需要放置图书的书架
  2. 通过将 { "name":"mybook", "shelf":"localhost:8080/shelves/1" } 发布到 /books 来发布带有父书架 URI 的新书

这非常有效。嗯,有点。这本书是通过正确链接到父级的链接创建的,但现在我遇到了一个问题,即父级没有在 /shelves/1/books 下显示这本书。我针对 here 发布了一个新问题。

【讨论】:

  • 我在接受 POST 请求的“父”类上有一个控制器。我遇到了同样的 JSON 编组错误。
猜你喜欢
  • 1970-01-01
  • 2011-01-13
  • 2018-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多