【问题标题】:Spring get link to an entitySpring获取到实体的链接
【发布时间】:2017-06-16 18:34:55
【问题描述】:

我正在使用 Spring-boot-starter-data-jpa,在我的 RestController 中我想返回新创建对象的位置。有没有办法逆转@RequestMapping,而不是硬编码如何构建 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;
    }
}

我总是看到人们在这个函数中硬编码他们的 URL 结构,这让我无话可说....

当然我在同一个类中也有一个 GET 函数(否则就没有什么可以反转了)

@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);
    }

【问题讨论】:

    标签: spring spring-boot spring-data spring-data-rest spring-hateoas


    【解决方案1】:

    我通过将它添加到我的班级来解决它:

    @Autowired EntityLinks entityLinks;
    

    并使用 Spring 的一些 HATEOAS 功能。

    Link link = entityLinks.linkToSingleResource(BookInstance.class, createdBookInstance.getId()).expand();
    return ResponseEntity.created(URI.create(link.getHref())).build();
    

    注意:在上面几行中 createdBookInstance 只不过是在 DB 中创建记录后返回的对象。

    【讨论】:

    • 我在从 entityLinks 创建链接时得到了执行,说没有可用于创建类型链接的插件。您是否为此添加了任何特定的依赖项/插件?
    【解决方案2】:

    使用资源组装器的替代解决方案:

    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");
            }
    }
    

    在你的控制器类中:

    @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);
    }
    

    【讨论】:

    • 第一个代码sn-p中你对TaskResource做了什么类型的声明。你能告诉我从哪里加载课程吗?
    • TaskResource 只是一个愚蠢的复制粘贴问题。固定。
    • 这是“手工构建”链接恕我直言。请参阅@EralpB 答案以获得更简单(通过不同)的方法。
    【解决方案3】:

    由于其他解决方案与我的源代码不兼容,我费了很大劲才找到返回新创建的实体访问 API 的 URL 的解决方案。我个人觉得@EralpB 解决方案很简单。但是,我在创建链接时遇到了内部使用的 SimpleEntityPlugin 的问题。

    最后,我找到了简单代码的解决方案,我不会使用任何这样的 Hateoas API。我不确定这个解决方案是否与 Spring 兼容,但我尝试了 SpringBoot-2.x 版本。对我来说效果很好。

    @PostMapping("/students")
    public ResponseEntity<Object> createStudent(@RequestBody Student student) {
        Student savedStudent = studentRepository.save(student);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
            .buildAndExpand(savedStudent.getId()).toUri();
        return ResponseEntity.created(location).build();
    }
    

    根据上面的示例,URL 将返回http://localhost:8080/students/1。这可能会因您的配置而异。

    如果您需要在 URL 中的学生旁边和 id 之前添加任何额外的路径,您可以直接在 {id} 声明之前进行硬编码,例如 /search/{id}。所以实际的 URL 看起来像 http://localhost:8080/students/search/1

    如果您没有将学生配置为按 id 搜索学生的一部分,并且您配置了一些其他路径以通过 id 访问学生,您可以使用以下代码仅加载上下文,例如路径 http://localhost:8080 和将属性中的硬编码/加载添加到 uri。

    URI location = ServletUriComponentsBuilder.fromCurrentContextPath().path("/search/{id}")
                    .buildAndExpand(newCase.getCaseId()).toUri();
    

    根据上面的代码 sn-p,返回的 URL 看起来像 http://localhost:8080/search/1。您可以在ServletUriComponentsBuilder 下探索不同的方法。我希望这很清楚。

    【讨论】:

      【解决方案4】:

      要轻松获得正确的资源链接,您应该使用此答案https://stackoverflow.com/a/41956064/11152683

      要返回不仅是位置,还要返回新创建的资源,您可以采用这种方法:

      import lombok.RequiredArgsConstructor;
      import org.springframework.data.rest.webmvc.PersistentEntityResource;
      import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;
      import org.springframework.data.rest.webmvc.RepositoryRestController;
      import org.springframework.hateoas.Link;
      import org.springframework.hateoas.server.EntityLinks;
      import org.springframework.hateoas.server.ExposesResourceFor;
      import org.springframework.http.ResponseEntity;
      import org.springframework.web.bind.annotation.RequestBody;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestMethod;
      import sk.qpp.qaron.user.repository.QaronUser;
      
      import java.net.URI;
      
      @RepositoryRestController
      @ExposesResourceFor(BookInstance.class)
      @RequiredArgsConstructor // lombok thing, autoviring all final attributes through ctor
      public class BookInstanceController {
          private final EntityLinks entityLinks;
      
          @RequestMapping(value="/bookInstances", method= RequestMethod.POST)
          ResponseEntity<Object> createBookInstance(@RequestBody BookInstance bookInstance, PersistentEntityResourceAssembler resourceAssembler){
              // save to database
              final BookInstance createdBookInstance = bookInstanceRepository.save(bookInstance);
      
              final PersistentEntityResource newEntityResource = resourceAssembler.toModel(createdBookInstance);
              Link link = entityLinks.linkToItemResource(QaronUser.class, createdBookInstance.getId()).expand();
              return ResponseEntity
                      .created(URI.create(link.getHref()))
                      .body(newEntityResource);
          }
      }
      

      请注意,此示例使用 @RepositoryRestController 而不是 @RestController。这对于连接PersistentEntityResourceAssembler 而不是获得java.lang.IllegalArgumentException: entities is marked non-null but is null 至关重要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-01
        相关资源
        最近更新 更多