Spring-Data-Rest 能否处理与其他微服务资源的关联?
Can Spring-Data-Rest handle associations to Resources on other Microservices?
对于一个新项目,我正在构建一个 rest api,它引用第二个服务的资源。为了方便客户,我想添加此关联以序列化为 _embedded 条目。
这可能吗?我考虑过构建一个假的 CrudRepository(假客户端的门面)并使用资源处理器手动更改该假资源的所有 url。那行得通吗?
深入了解 spring-data-rest 的功能:
Data-Rest 将所有实体包装到 PersistentEntityResource
对象中,这些对象扩展了 spring HATEOAS 提供的 Resource<T>
接口。这个特定的实现有一个嵌入对象的列表,这些对象将被序列化为 _embedded
字段。
所以理论上我的问题的解决方案应该像实施 ResourceProcessor<Resource<MyType>>
并将我的参考对象添加到嵌入一样简单。
在实践中,这种方法有一些丑陋但可以解决的问题:
PersistentEntityResource
不是通用的,因此虽然您可以为其构建 ResourceProcessor,但该处理器默认情况下会捕获所有内容。我不确定当您开始使用投影时会发生什么。所以这不是解决方案。
PersistentEntityResource
实现了 Resource<Object>
,因此不能转换为 Resource<MyType>
,反之亦然。如果您想访问嵌入字段,则必须使用 PersistentEntityResource.class.cast()
和 Resource.class.cast()
.
完成所有转换
总的来说,我的解决方案简单、有效但不是很漂亮。我希望 Spring-Hateoas 将来能得到全面的 HAL 支持。
这里以我的 ResourceProcessor 为例:
@Bean
public ResourceProcessor<Resource<MyType>> typeProcessorToAddReference() {
// DO NOT REPLACE WITH LAMBDA!!!
return new ResourceProcessor<>() {
@Override
public Resource<MyType> process(Resource<MyType> resource) {
try {
// XXX all resources here are PersistentEntityResource instances, but they can't be cast normaly
PersistentEntityResource halResource = PersistentEntityResource.class.cast(resource);
List<EmbeddedWrapper> embedded = Lists.newArrayList(halResource.getEmbeddeds());
ReferenceObject reference = spineClient.findReferenceById(resource.getContent().getReferenceId());
embedded.add(embeddedWrappers.wrap(reference, "reference-relation"));
// XXX all resources here are PersistentEntityResource instances, but they can't be cast normaly
resource = Resource.class.cast(PersistentEntityResource.build(halResource.getContent(), halResource.getPersistentEntity())
.withEmbedded(embedded).withLinks(halResource.getLinks()).build());
} catch (Exception e) {
log.error("Something went wrong", e);
// swallow
}
return resource;
}
};
}
如果您希望以类型安全的方式工作并且仅使用链接(对自定义控制器方法的附加引用),您可以在这个示例代码中找到灵感:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@Configuration
public class MyTypeLinkConfiguration {
public static class MyType {}
@Bean
public RepresentationModelProcessor<EntityModel<MyType>> MyTypeProcessorAddLifecycleLinks(MyTypeLifecycleStates myTypeLifecycleStates) {
// WARNING, no lambda can be passed here, because type is crucial for applying this bean processor.
return new RepresentationModelProcessor<EntityModel<MyType>>() {
@Override
public EntityModel<MyType> process(EntityModel<MyType> resource) {
// add custom export link for single MyType
myTypeLifecycleStates
.listReachableStates(resource.getContent().getState())
.forEach(reachableState -> {
try {
// for each possible next state, generate its relation which will get us to given state
switch (reachableState) {
case DRAFT:
resource.add(linkTo(methodOn(MyTypeLifecycleController.class).requestRework(resource.getContent().getId(), null)).withRel("requestRework"));
break;
case IN_REVIEW:
resource.add(linkTo(methodOn(MyTypeLifecycleController.class).requestReview(resource.getContent().getId(), null)).withRel("requestReview"));
break;
default:
throw new RuntimeException("Link for target state " + reachableState + " is not implemented!");
}
} catch (Exception ex) {
// swallowed
log.error("error while adding lifecycle link for target state " + reachableState + "! ex=" + ex.getMessage(), ex);
}
});
return resource;
}
};
}
}
请注意,myTypeLifecycleStates
是自动装配的 "service"/"business logic" bean。
对于一个新项目,我正在构建一个 rest api,它引用第二个服务的资源。为了方便客户,我想添加此关联以序列化为 _embedded 条目。
这可能吗?我考虑过构建一个假的 CrudRepository(假客户端的门面)并使用资源处理器手动更改该假资源的所有 url。那行得通吗?
深入了解 spring-data-rest 的功能:
Data-Rest 将所有实体包装到 PersistentEntityResource
对象中,这些对象扩展了 spring HATEOAS 提供的 Resource<T>
接口。这个特定的实现有一个嵌入对象的列表,这些对象将被序列化为 _embedded
字段。
所以理论上我的问题的解决方案应该像实施 ResourceProcessor<Resource<MyType>>
并将我的参考对象添加到嵌入一样简单。
在实践中,这种方法有一些丑陋但可以解决的问题:
PersistentEntityResource
不是通用的,因此虽然您可以为其构建 ResourceProcessor,但该处理器默认情况下会捕获所有内容。我不确定当您开始使用投影时会发生什么。所以这不是解决方案。
PersistentEntityResource
实现了 Resource<Object>
,因此不能转换为 Resource<MyType>
,反之亦然。如果您想访问嵌入字段,则必须使用 PersistentEntityResource.class.cast()
和 Resource.class.cast()
.
总的来说,我的解决方案简单、有效但不是很漂亮。我希望 Spring-Hateoas 将来能得到全面的 HAL 支持。
这里以我的 ResourceProcessor 为例:
@Bean
public ResourceProcessor<Resource<MyType>> typeProcessorToAddReference() {
// DO NOT REPLACE WITH LAMBDA!!!
return new ResourceProcessor<>() {
@Override
public Resource<MyType> process(Resource<MyType> resource) {
try {
// XXX all resources here are PersistentEntityResource instances, but they can't be cast normaly
PersistentEntityResource halResource = PersistentEntityResource.class.cast(resource);
List<EmbeddedWrapper> embedded = Lists.newArrayList(halResource.getEmbeddeds());
ReferenceObject reference = spineClient.findReferenceById(resource.getContent().getReferenceId());
embedded.add(embeddedWrappers.wrap(reference, "reference-relation"));
// XXX all resources here are PersistentEntityResource instances, but they can't be cast normaly
resource = Resource.class.cast(PersistentEntityResource.build(halResource.getContent(), halResource.getPersistentEntity())
.withEmbedded(embedded).withLinks(halResource.getLinks()).build());
} catch (Exception e) {
log.error("Something went wrong", e);
// swallow
}
return resource;
}
};
}
如果您希望以类型安全的方式工作并且仅使用链接(对自定义控制器方法的附加引用),您可以在这个示例代码中找到灵感:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.RepresentationModelProcessor;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@Configuration
public class MyTypeLinkConfiguration {
public static class MyType {}
@Bean
public RepresentationModelProcessor<EntityModel<MyType>> MyTypeProcessorAddLifecycleLinks(MyTypeLifecycleStates myTypeLifecycleStates) {
// WARNING, no lambda can be passed here, because type is crucial for applying this bean processor.
return new RepresentationModelProcessor<EntityModel<MyType>>() {
@Override
public EntityModel<MyType> process(EntityModel<MyType> resource) {
// add custom export link for single MyType
myTypeLifecycleStates
.listReachableStates(resource.getContent().getState())
.forEach(reachableState -> {
try {
// for each possible next state, generate its relation which will get us to given state
switch (reachableState) {
case DRAFT:
resource.add(linkTo(methodOn(MyTypeLifecycleController.class).requestRework(resource.getContent().getId(), null)).withRel("requestRework"));
break;
case IN_REVIEW:
resource.add(linkTo(methodOn(MyTypeLifecycleController.class).requestReview(resource.getContent().getId(), null)).withRel("requestReview"));
break;
default:
throw new RuntimeException("Link for target state " + reachableState + " is not implemented!");
}
} catch (Exception ex) {
// swallowed
log.error("error while adding lifecycle link for target state " + reachableState + "! ex=" + ex.getMessage(), ex);
}
});
return resource;
}
};
}
}
请注意,myTypeLifecycleStates
是自动装配的 "service"/"business logic" bean。