处理改造2 GSON转换错误

问题描述:

我正在使用GSON和RxJava进行改造以执行网络请求。我试图找出如何获得响应时,Gson库无法转换它。处理改造2 GSON转换错误

这发生在服务器发生错误并且响应与Gson库试图将响应转换为的类不匹配时发生。

解决方法是在尝试转换它之前创建一个拦截器并缓存响应。但是这只是糟糕的编程,因为一旦我们开始做并发请求,问题就变得难以管理。

服务定义如下:响应类只包含状态码和称为数据的泛型。

Retrofit getService() { 
    return new Retrofit.Builder() 
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 
      .addConverterFactory(GsonConverterFactory.create()) 
      .baseUrl(url) 
      .client(clientBuilder.build()) 
      .build(); 
} 
public Observable<Response<String>> userLogin(String username, String password) { 

    return getService().create(Account.class) 
      .login(username, password) 
      .subscribeOn(Schedulers.newThread()) 
      .observeOn(AndroidSchedulers.mainThread()); 
} 

别的地方在代码中创建了一个请求

getService().userLogin(email, password) 
     .subscribeOn(Schedulers.newThread()) 
     .observeOn(AndroidSchedulers.mainThread()) 
     .subscribe(onSuccess(), onError()); 

protected Action1<Response<String>> onSuccess(){ 

    return new Action1<Response<String>>() { 
     @Override 
     public void call(Response<String> response) { 
      // Process the response 
     } 
    }; 
} 
protected Action1<Throwable> onError(){ 
    return new Action1<Throwable>() { 
     @Override 
     public void call(Throwable throwable) { 

      if (throwable instanceof HttpException) { 
       ResponseBody body = ((HttpException) throwable).response().errorBody(); 
       // Handle the error 
      } 
     } 
    }; 

当服务器返回字符串以外的东西出现问题。例如对象或数组。这里GsonConverterFactory会抛出一个错误,这个错误会被onError方法捕获。我想知道如何得到回应。

返回的throwable类型为JsonSyntaxException,可悲的是它不包含GSON库试图转换的原始响应体。

昨天我给一个类似的问题建议similar solution,但是你似乎需要稍微修改答案。答案还包含一些关于效率和内存消耗问题的评论。如果您想使用该解决方案,您可以创建一个能创建一个特殊的例外,拿着一些故障信息的特殊故障处理程序:

final class BadPayloadExceptionConversionThrowableConsumer 
     implements IConversionThrowableConsumer { 

    private static final int MAX_STREAM_BUFFER_LENGTH = 8 * 1024; 

    private static final IConversionThrowableConsumer badPayloadExceptionConversionThrowableConsumer = new BadPayloadExceptionConversionThrowableConsumer(); 

    private BadPayloadExceptionConversionThrowableConsumer() { 
    } 

    static IConversionThrowableConsumer getBadPayloadExceptionConversionThrowableConsumer() { 
     return badPayloadExceptionConversionThrowableConsumer; 
    } 

    @Override 
    public void accept(final MediaType contentType, final long contentLength, final InputStream inputStream, final Throwable ex) 
      throws IOException { 
     final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(MAX_STREAM_BUFFER_LENGTH); 
     copy(limit(inputStream, MAX_STREAM_BUFFER_LENGTH), byteArrayOutputStream); 
     final ByteArrayInputStream bufferedInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); 
     throw new BadPayloadException(ex, contentType, contentLength, bufferedInputStream); 
    } 

    static final class BadPayloadException 
      extends IOException { 

     private final MediaType contentType; 
     private final long contentLength; 
     private final InputStream inputStream; 

     private BadPayloadException(final Throwable cause, final MediaType contentType, final long contentLength, final InputStream inputStream) { 
      super(null, cause); 
      this.contentType = contentType; 
      this.contentLength = contentLength; 
      this.inputStream = inputStream; 
     } 

     MediaType getContentType() { 
      return contentType; 
     } 

     long getContentLength() { 
      return contentLength; 
     } 

     InputStream getInputStream() { 
      return inputStream; 
     } 

    } 

} 

而不用注销,只抛出一个特殊的私有构造函数的异常,可能是instanceof版在呼叫现场。由于在相关问题中也被注释的原因,输入流必须被缓冲到一定的限制(特别是为什么原始的InputStream被委托而不是ResponseBody)。现在可以使用的方法:

service.getFooBar() 
     .subscribe(
       out::println, 
       t -> { 
        if (t instanceof BadPayloadException) { 
         try { 
          final BadPayloadException badPayloadException = (BadPayloadException) t; 
          err.println("Content type = " + badPayloadException.getContentType()); 
          err.println("Content length = " + badPayloadException.getContentLength()); 
          err.print("Content  = "); 
          copy(badPayloadException.getInputStream(), err); 
         } catch (final IOException ex) { 
          throw new RuntimeException(ex); 
         } 
        } else { 
         err.println(t.getClass()); 
        } 
       } 
     ); 

注意,copylimit方法是从谷歌番石榴ByteStreams静态导入。 outerr分别是System.outSystem.err的静态进口。

输出示例:

Content type = application/json 
Content length = -1 
Content  = {#"foo":1,"bar":2}