【问题标题】:Feign and HAL/resourcesFeign 和 HAL/资源
【发布时间】:2023-03-29 19:55:01
【问题描述】:

据我了解 HAL 或 HATEOAS,我有一个通过 spring-data-rest 公开资源的服务器,并使用它。但是当我尝试将它与 Feign 结合使用时,我似乎无法注册一个被拾取的 Jackson2HalModule。

我需要做些什么来将 Feign“客户端”连接到新的转换器吗?它是否使用了另一个 ObjectMapper 而不是我在这里得到的?

代码:

@Inject
public void configureObjectMapper(ObjectMapper mapper, RestTemplate template) {
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.registerModule(new Jackson2HalModule());

    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
    converter.setObjectMapper(mapper);

    template.getMessageConverters().add(converter);
}

来自服务器的响应:

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:13372/user{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:13372/user/search"
    }
  },
  "_embedded" : {
    "user" : [ {
      "id" : "5567613e5da543dba4201950",
      "version" : 0,
      "created" : "2015-05-28T18:41:02.685Z",
      "createdBy" : "system test",
      "edited" : "2015-05-28T18:41:02.713Z",
      "editedBy" : "system test",
      "username" : "devuser",
      "email" : "dev@test.com",
      "roles" : [ "USER" ],
      "_links" : {
        "self" : {
          "href" : "http://localhost:13372/user/5567613e5da543dba4201950"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

例外:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
 at [Source: java.io.PushbackInputStream@7b6c6e70; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
    at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:762)
    at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:758)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.java:275)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:216)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:206)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:25)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3066)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2221)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:205)

【问题讨论】:

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


    【解决方案1】:

    我发现了问题。 由于来自 REST API 的响应是单个响应,因此发生异常。所以它没有将其视为实体列表。

    当我添加时(基于上面的代码):

    mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
    

    有效

    编辑: 顺便说一句,我已经像这样实现了 FeignClient:

    @Service
    @FeignClient(UsersConstants.USER_SERVICE_NAME)
    public interface UsersServices {
    
        @RequestMapping(method = RequestMethod.GET, value = "/user")
        List<User> getUsers();
    }
    

    但它应该是怎样的,因为它是一个可分页的资源:

    @Service
    @FeignClient(UsersConstants.USER_SERVICE_NAME)
    public interface UsersServices {
    
        @RequestMapping(method = RequestMethod.GET, value = "/user")
        List<PagedResources<User>> getUsers();
    }
    

    PagedResource 位于 HATEOAS 依赖项中:

    <dependency>
        <groupId>org.springframework.hateoas</groupId>
        <artifactId>spring-hateoas</artifactId>
    </dependency>
    

    它还有很多其他可以提供帮助的类,例如 Resource、Resources 等等。

    【讨论】:

    • 我已经尝试过同样的事情,但我无法让它工作。我是否必须在不同的类中编写“@inject”方法,或者在我有“@SpringBootApplication”注释的主类中是否足够。 @LG87
    • 据我所知,它应该可以在 Spring 作为组件管理的任何类中工作
    【解决方案2】:

    您应该在您的应用程序中定义一个feignDecoder bean。如果您的环境中有spring-hateoas,请尝试以下操作:

    @Bean
    public Decoder feignDecoder() {
        ObjectMapper mapper = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .registerModule(new Jackson2HalModule());
    
        return new ResponseEntityDecoder(new JacksonDecoder(mapper));
    }
    

    然后您可以将您的 HAL 用作 PagedResource

    【讨论】:

      【解决方案3】:

      这对我有用:

      @EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
      @SpringBootApplication
      @EnableFeignClients
      public class Application {
      ....
      }
      

      注意@EnableHypermediaSupport

      @FeignClient(url = "http://localhost:8080")
      public interface PersonClient {
          @RequestMapping(method = RequestMethod.GET, value = "/persons")
          Resources<Person> getPersons();
      
          @RequestMapping(method = RequestMethod.GET, value = "/persons/{id}")
          Resource<Person> getPerson(@PathVariable("id") long id);
      }
      

      我在这里创建了一个完整的示例:https://github.com/jtdev/spring-feign-data-rest-example

      请注意,只需将 maven pom 从 spring-boot 切换到 spring-cloud(不更改代码),可能很容易导致 json 错误。

      【讨论】:

      • 我们能否将所有来自 spring data rest 端点的请求映射到单个 faign 客户端?在上面的示例中,所有方法(如 GET、POST、PATCH)都在一个端点中而不在接口中声明它们?
      • 我查看了您的示例代码,当您不必乱用 ObjectMapper 和转换器时,这很好。现在,为了将 Java 8 ZonedDateTime 字段添加到您的 Person 对象中,您将添加的最小更改是什么?
      • 添加 @EnableHypermediaSupport 对我有用。奇怪的是,这对 Feign 来说是必要的;我首先有另一个与 RestTemplate 合作的解决方案,不需要这样做。
      【解决方案4】:

      我得到了这个为我工作(感谢@Devabc,你的榜样帮助了我很多):

      我想为用户获取资源的 PagedResources。

      记得添加 @EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL) 到您的主应用程序类。

      我的 Feign 客户端如下所示:

      @FeignClient("user-microservice")
      public interface UserClient {
        @RequestMapping(method = RequestMethod.GET, value = "/user")
        PagedResources<Resource<User>> findAll();
      }
      

      还记得为你的模型添加一个默认和参数化的构造函数。 (在我的情况下用户)我不知道为什么,但这似乎解决了我的序列化问题。

      最后我用了这个版本的 Feign

      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
        <version>1.1.5.RELEASE</version>
      </dependency>
      

      【讨论】:

        【解决方案5】:

        查看link

        Greg Turnquist 的以下评论确实适用于集合类型返回

        C) 从集合中提取的类型应该是Resources&lt;Resource&lt;Question&gt;&gt;。该集合具有链接,该集合的每个条目也具有链接。

        【讨论】:

          猜你喜欢
          • 2018-06-04
          • 1970-01-01
          • 2021-07-17
          • 2018-06-05
          • 1970-01-01
          • 2018-03-18
          • 2019-02-04
          • 1970-01-01
          • 2016-12-17
          相关资源
          最近更新 更多