处理改造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());
}
}
);
注意,copy
和limit
方法是从谷歌番石榴ByteStreams
静态导入。 out
和err
分别是System.out
和System.err
的静态进口。
输出示例:
Content type = application/json
Content length = -1
Content = {#"foo":1,"bar":2}