【问题标题】:Can Spring-Data-Rest handle associations to Resources on other Microservices?Spring-Data-Rest 可以处理与其他微服务上的资源的关联吗?
【发布时间】:2018-04-11 09:46:14
【问题描述】:

对于一个新项目,我正在构建一个引用来自第二个服务的资源的 rest api。为了方便客户,我想添加这个关联,以序列化为 _embedded 条目。

这可能吗?我考虑过构建一个假的 CrudRepository(假客户端的外观)并使用资源处理器手动更改该假资源的所有 url。那行得通吗?

【问题讨论】:

    标签: spring-data-rest spring-hateoas


    【解决方案1】:

    深入了解 spring-data-rest 的功能:

    Data-Rest 将所有实体包装到 PersistentEntityResource 对象中,这些对象扩展了 spring HATEOAS 提供的 Resource<T> 接口。这个特定的实现有一个嵌入对象的列表,这些对象将被序列化为_embedded 字段。

    所以理论上我的问题的解决方案应该像实现ResourceProcessor<Resource<MyType>> 并将我的引用对象添加到嵌入一样简单。

    在实践中,这种方法有一些丑陋但可以解决的问题:

    PersistentEntityResource 不是通用的,因此虽然您可以为它构建一个 ResourceProcessor,但该处理器默认情况下会捕获所有内容。我不确定当您开始使用 Projections 时会发生什么。所以这不是一个解决方案。

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

    【讨论】:

      【解决方案2】:

      如果您想以类型安全的方式工作并且仅使用链接(添加对自定义控制器方法的引用),您可以在此示例代码中找到灵感:

      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 是自动装配的“服务”/“业务逻辑”bean。

      【讨论】:

        猜你喜欢
        • 2018-09-04
        • 2017-04-22
        • 2015-12-11
        • 2017-07-23
        • 2019-01-07
        • 2019-04-11
        • 2023-04-09
        • 2014-10-08
        • 1970-01-01
        相关资源
        最近更新 更多