处理改造2 GSON转换错误

时间:2017-02-12 07:35:51

标签: android gson retrofit2

我正在使用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库试图转换的原始响应主体。

1 个答案:

答案 0 :(得分:0)

昨天我向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方法是来自Google Guava ByteStreams的静态导入。 outerr分别是System.outSystem.err的静态导入。

示例输出:

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