如何处理不遵守 Accept: application/json 使用 Jersey 和 Jackson 的 Web 服务

How to deal with a web service that doesn't obey Accept: application/json using Jersey and Jackson

我正在使用 Jersey 和 Jackson 访问 REST Web 服务,该服务正确返回格式正确的 JSON 数据,但响应为 header:

Content-Type: text/html; charset=UTF-8

即使我在请求中指定了 Accept: application/json header 结果导致 Jersey 抛出:

org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=text/html;charset=UTF-8

我正在使用我的代码使用其他 Web 服务,但我想知道是否有办法创建我自己的 MessageBodyReader 来处理 mis-match,但是我还没有弄清楚如何实现它正确。我打算让网络服务的所有者修复 mis-match 但我不抱太大希望。

I am wondering if there is a way to create my own MessageBodyReader to deal with the mis-match.

Jersey 文档中的此页面解释了如何创建自定义 MessageBodyReader:

在您的情况下,您也许能够找到通常用于您的 JSON 和 "tweak" 的 reader 的源代码。理论上。

然而,经过更多的阅读,我发现了这个:

这告诉我 Jersey 已经对 JSON 提供了广泛的支持。您很有可能可以通过简单地调整配置来解决您的问题,以便 Jersey 知道如何处理不寻常的 content-type。但这将取决于您正在解析 JSON 响应主体的多种可能方式中的哪一种......目前。


有人这样评价:

I think however easier just to retrieve the data, ignore the header and just parse it into your json object.

这是个坏主意。 header 告诉您 JSON 可以包含 multi-byte 个字符。如果您只是忽略它并将字节解码为默认字符集中的字符,那么如果存在多字节字符,您将得到 "mojibake"。

如果您自己解析 JSON,那么将解析器的输入流配置为使用 UTF-8 或其他 content-type header 应该是一件简单的事情表示字符编码是。


最后是"who is wrong"的问题。

其实我觉得是你的错。如果你只发送一个 "Accept: application/json" header,你就是在告诉服务器你不关心字符集是什么。然后服务器可以自由地为它知道将正确表示响应文本的响应选择任何字符集。 (本例中的文本内容为JSON。)

如果您特别想要(比如)ASCII 或 Latin-1,那么您应该添加 "Accept-charset:" header.

如果>>THAT<< 不起作用,那么可能是服务器的问题。但请记住,如果响应确实/可能包含无法在您首选的字符集上编码的字符,那么服务器可能/应该向您发送 406 错误。

好吧,我基本上是按照 Stephen C 的建议设法解决了这个问题,但我想我会 post 提供一些更多的细节,以防其他人处于同一条船上。首先,我实际上是从 Jersey 指南的前几节开始的,特别是这一节:

https://jersey.java.net/documentation/latest/user-guide.html#d0e6825

显然我正在使用 Jersey 进行 javax.ws.rs.client 并且我正在使用 Genson 进行 JSON 反序列化。因此,我实现了以下 class 来实现 MessageBodyReader:

public class BTCEURTradeMessageBodyReader 
        implements MessageBodyReader<BTCEURTrades> {

  final org.slf4j.Logger logger = 
          LoggerFactory.getLogger(BTCEURTradeMessageBodyReader.class);

  @Override
  public boolean isReadable(Class<?> type, Type genericType, 
          Annotation[] annotations, MediaType mediaType) {
    logger.info("isReadable being checked for: {} and media type: {}", type, mediaType);
    return type == BTCEURTrades.class;
  }

  @Override
  public BTCEURTrades readFrom(Class<BTCEURTrades> type, Type genericType, 
          Annotation[] annotations, MediaType mediaType, 
          MultivaluedMap<String, String> httpHeaders, InputStream entityStream) 
          throws IOException, WebApplicationException {
    logger.info("readFrom being called for: {}", type);

    BTCEURTrades btceurTrades;
    try {
      btceurTrades = new Genson().deserialize(entityStream, type);
    } catch(Exception e) {
      logger.error("Error processing JSON reponse.", e);
      throw new ProcessingException("Error processing JSON reponse.");
    }
    return btceurTrades;
  }

}

然后在创建后向客户端注册,如下所示:

client = ClientBuilder.newClient();
client.register(BTCEURTradeMessageBodyReader.class);