Spring 获得 link 实体

Spring get link to an entity

我正在使用 Spring-boot-starter-data-jpa,在我的 RestController 中我想 return 新创建对象的位置。有没有办法反转@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函数class(否则就没有什么可以反推的了)

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

我通过将此添加到我的 class:

解决了这个问题
@Autowired EntityLinks entityLinks;

并使用 Spring 的一些 HATEOAS 功能。

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

注意:在上面几行中,createdBookInstance只是在数据库中创建记录后返回对象。

使用资源汇编程序的替代解决方案:

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

在你的控制器中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);
}

由于其他解决方案与我的源代码不兼容,我费了好大劲才找到 return 新创建的实体访问 URL 的解决方案 API。我个人觉得@EralpB 解决方案很简单。但是,我在创建 Link 时遇到了内部使用的 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 将 return 类似于 http://localhost:8080/students/1。这可能因您的配置而异。

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

如果以防万一,您没有将学生配置为按 ID 搜索学生的一部分,并且配置了一些其他路径以按 ID 访问学生,则可以使用以下代码仅加载上下文,例如路径 http://localhost:8080 并将 hardcode/load 从属性添加到 uri。

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

根据上面的代码片段,returning URL 看起来像 http://localhost:8080/search/1. You can explore different methods available under the ServletUriComponentsBuilder。我希望这会很清楚。

要轻松获得正确的资源 link,您应该使用此答案

到return 不仅位置,而且新创建的资源,你可以采用这种方法:

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.

至关重要