将@RequestBody中的HAL URI映射到Spring Data Rest管理实体

时间:2018-03-23 21:33:24

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

我使用Spring Data JPA来管理JPA实体,Spring Data Rest将存储库导出到REST API中。

我还在构建一个自定义控制器,我想获取实体的URI(HAL链接)并通过CrudRepository自动将其映射到实体对象。

CustomController

@RequestMapping(path = MeLinks.ELEVATE, method = RequestMethod.PUT, consumes = RestMediaTypes.TEXT_URI_LIST_VALUE)
HttpEntity<?> elevate(@RequestBody @Valid Resources<Contact> contact) {
    ...
}

尝试使用内容类型PUT http://localhost:8080/contacts/1 text/uri-list之类的联系人链接时,我可以使用contact.getLinks()访问该链接,但不能访问Contact对象本身。

Spring Data Rest是否可以从URI自动推断实体?我知道内置的UriToEntityConverter,但我该如何使用它呢?

修改

这是一种有效的尝试,但并不能优雅地解决问题。

下面的代码初始化UriToEntityConverter并将传入的URI转换为域对象。

@Autowired
private ApplicationContext applicationContext;

@Autowired
private MappingContext<?, ?> mappingContext;

@RequestMapping(path = MeLinks.ELEVATE, method = RequestMethod.PUT, consumes = RestMediaTypes.TEXT_URI_LIST_VALUE)
HttpEntity<?> elevate(@RequestBody @Valid Resources<Contact> uris) {

    URI uri = URI.create(uris.getLinks().get(0).getHref());

    Repositories repositories = new Repositories(applicationContext);

    PersistentEntity<?,?> contactPersistentEntity = repositories.getPersistentEntity(Contact.class);

    UriToEntityConverter uriToEntityConverter = new UriToEntityConverter(
        new PersistentEntities(Collections.singleton(mappingContext)), 
        new DefaultRepositoryInvokerFactory(repositories), 
        repositories);

    Contact t = (Contact) uriToEntityConverter.convert(uri, TypeDescriptor.valueOf(URI.class), TypeDescriptor.valueOf(Contact.class));
}

可以想象,从Repository中提取域对象比上面容易得多。此外,这也可以假设URI使用ID作为链接的唯一部分。在我的情况下,我已经将其定制为使用UUID。因此UriToEntityConverter的默认行为无效。

1 个答案:

答案 0 :(得分:0)

如果要继续使用UriToEntityConverter,建议您使用EntityLookupSupport<T>类检出this part of the Spring Data REST documentation来将资源URI自定义为其他属性。 UriToEntityConverter然后应正确返回实体。

如果您希望直接按照说明使用存储库,请考虑以下事项:

查看UriToEntityConverter的源代码,它使用URI的最后部分作为对象的标识符。您也可以这样做。 (在此代码段中,假设您正确检索了URI对象,我刚刚从上面修改了您的代码)

private ContactRepository contactRepository;

// You should use constructor injection, as it is preferred over field injection.
@Autowired
public CustomController(ContactRepository contactRepository){
    this.contactRepository = contactRepository;
}


@RequestMapping(path = MeLinks.ELEVATE, method = RequestMethod.PUT, consumes = RestMediaTypes.TEXT_URI_LIST_VALUE)
HttpEntity<?> elevate(@RequestBody @Valid CollectionModel<Contact> uris) {
    URI uri = URI.create(uris.getLinks().get(0).getHref());

    Optional<Contact> optionalContact = contactRepository.findByName(getLastUriPart(uri));

    if (optionalContact.isPresent()){
        return optionalContact.get();
    } else {
        // Error handling
    }
}

private String getLastUriPart(URI uri) {
    String[] uriParts = uri.getPath().split("/");

    if (uriParts.length < 2) {
        // Error handling
    }

    return uriParts[uriParts.length - 1];
}