Grails REST Client Builder 在处理来自 Jersey Web 服务的响应时收到 JSON 反序列化错误

Posted

技术标签:

【中文标题】Grails REST Client Builder 在处理来自 Jersey Web 服务的响应时收到 JSON 反序列化错误【英文标题】:Grails REST Client Builder getting JSON deserialization error on processing response from Jersey web service 【发布时间】:2015-05-31 11:11:12 【问题描述】:

我有一个 Jersey Web 服务,它生成/使用 JSON,并使用 Jackson 进行 POJO 序列化/反序列化。我目前有一个基于 Jersey API 的客户端和一个基于 Grails rest-client-builder 插件的 Groovy 客户端。虽然 Jersey 客户端似乎没有问题,但我的基于 Grails 插件的客户端在反序列化我正在处理的发布请求的响应时遇到问题。根据堆栈跟踪,它似乎出于某种原因试图将响应正文中的 JSON 序列化为字符串?

org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of java.lang.String out of START_OBJECT token
    at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@239b0f9d; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
    at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@239b0f9d; line: 1, column: 1]
    at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:228)
    at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.read(MappingJackson2HttpMessageConverter.java:220)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:95)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:795)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:779)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:559)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:520)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:463)

这是我的 Jersey 服务中资源的相关代码

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response addStory(Story s) 

   if (null != s) 
      //underlying DynamoDB client saves data to table
      client.insertStory(s);
      Response resp = Response.status(200).entity(s).build();
      return resp;
   
   else
      return Response.status(404).entity("No story data submitted").build();
   

这与基于 Jersey API 的客户端相同——工作正常。

public class StoryServiceJerseyClient 
    private static final Logger logger = LogManager.getLogger(StoryServiceJerseyClient.class.getName());
    private  static Client client = ClientBuilder.newClient().register(JacksonFeature.class);
    WebTarget target = client.target("http://localhost:8080/jerseyexample/rest").path("/story");

    public Story createStory(Story s)
        Story storyPersisted = null;
        storyPersisted = target
            .request()
            .post(Entity.entity(s, MediaType.APPLICATION_JSON),
                Story.class);

        return storyPersisted;
    

现在是 Grails 客户端。 这里我要注意的是,Story 类与上面的服务/客户端中的类不是同一个类。在大多数情况下,它包含相同的代码,并且作为我正在检查的其他内容的临时解决方法而存在。但我认为这与问题没有任何关系,因为在处理响应之前一切似乎都运行良好(因此类型似乎成功地通过了序列化并正确转换)。该服务将发送到 DynamoDB 表的数据保存起来,我看到这种情况成功发生。我不明白为什么它试图序列化对字符串的响应,我认为会尝试创建一个 JSON 对象,因为接受类型是“application/json”。

def addStory(Story story)
    def restTemplate = new RestTemplate();

    //experimented with adding/removing these/the explicit restTemplate
    //but it hasn't had an effect
    restTemplate.getMessageConverters().add(new  MappingJackson2HttpMessageConverter())
    restTemplate.getMessageConverters().add(new FormHttpMessageConverter())
    restTemplate.getMessageConverters().add(new StringHttpMessageConverter())

    def rest = new RestBuilder(restTemplate)
    def resp = rest.post(STORY_RESOURCE_URI )
        accept("application/json")
        contentType("application/json")
        body(story)
    

    def json = resp.json
    return json

BuildConfig.groovy

dependencies 
    ...
    compile "com.fasterxml.jackson.core:jackson-databind:2.5.1"
    compile "com.fasterxml.jackson.core:jackson-core:2.5.1"


plugins 
    ...
    compile ":rest-client-builder:2.1.0"

我正在使用 Grails 2.4.4 和 Jersey 2.17。

更新 在尝试调试时,我还注意到一件事——我对 json 内容类型或消息转换器的引用似乎都没有产生任何影响。当我在 Grails 客户端中更改以下任意组合时,我得到了完全相同的响应:

根本不要使用 RestTemplate 仅将 RestTemplate 与 MappingJackson2HttpMessageConverter 一起使用 删除accept("application/json") 移除 contentType("application/json")

这些似乎都不重要。无论我做什么,代码似乎都意识到它正在获取 JSON 并尝试反序列化。不过,我真的不清楚它为什么要反序列化为 String 。服务器端没有可见的错误,并且由于帖子可以完成,我不知道是否/如何检查我得到的响应。

【问题讨论】:

【参考方案1】:

我的代码中出现了同样的错误(grails 版本 2.3.11 和 rest-client-builder 版本 2.0.0)。

我将插件版本升级为“rest-client-builder:2.1.1”,bug消失了。

【讨论】:

哇,我得重新挖掘这段代码,但我很想看看这是否能帮我解决问题——谢谢!

以上是关于Grails REST Client Builder 在处理来自 Jersey Web 服务的响应时收到 JSON 反序列化错误的主要内容,如果未能解决你的问题,请参考以下文章

REST 的 Grails 与 Spring 性能对比

Grails - grails-spring-security-rest - 无法从 application.yml 加载 jwt 机密

grails-spring-security-rest 插件和悲观锁定

Grails 2.4.3:使用 REST 服务

如何保护 Grails 中的所有 REST 请求和响应

Grails REST 客户端插件 - 指定标头数据