【问题标题】:Handle retrofit 2 GSON conversion errors处理改造 2 GSON 转换错误
【发布时间】:2017-06-30 07:53:39
【问题描述】:

我正在使用 GSON 和 RxJava 进行改造来执行网络请求。我试图弄清楚当 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 库尝试转换的原始响应正文。

【问题讨论】:

    标签: android gson retrofit2


    【解决方案1】:

    昨天我向一个类似的问题建议了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;
            }
    
        }
    
    }
    

    而不是记录它只会引发一个特殊的私有构造函数异常,该异常可能是在调用站点instanceofed。输入流必须缓冲到某个限制,原因也在相关问题中进行了评论(特别是为什么委托了原始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 方法是从 Google Guava ByteStreams 静态导入的。 outerr 分别是 System.outSystem.err 的静态导入。

    样本输出:

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

    【讨论】:

      猜你喜欢
      • 2018-11-19
      • 1970-01-01
      • 2014-11-07
      • 2016-05-03
      • 1970-01-01
      • 2014-12-28
      • 1970-01-01
      • 1970-01-01
      • 2017-04-10
      相关资源
      最近更新 更多