2017-02-12 8 views
1

Ich verwende Retrofit mit GSON und RxJava, um Netzwerkanfragen auszuführen. Ich versuche herauszufinden, wie man die Antwort erhält, wenn die Gson-Bibliothek es nicht konvertiert.Handle Retrofit 2 GSON Konvertierungsfehler

Dies geschieht, wenn ein Fehler auf dem Server auftritt und die Antwort nicht mit der Klasse übereinstimmt, auf die die Gson-Bibliothek die Antwort zu konvertieren versucht.

Eine Arbeit wäre, einen Interzeptor zu erstellen und die Antwort zwischenzuspeichern, bevor wir versuchen, sie zu konvertieren. Aber das ist nur eine schlechte Programmierung, da das Problem nicht mehr bewältigt werden kann, wenn wir gleichzeitig Anfragen stellen.

Der Service ist wie folgt definiert: Die Antwortklasse enthält nur einen Statuscode und einen generischen Typ namens Daten.

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()); 
} 

Irgendwo anders im Code erstellen wir eine Anfrage

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 
      } 
     } 
    }; 

Das Problem tritt auf, wenn der Server etwas anderes als eine Zeichenfolge zurückgibt. Zum Beispiel ein Objekt oder ein Array. Hier wird die GsonConverterFactory einen Fehler werfen, der von der onError-Methode abgefangen wird. Ich frage mich, wie ich eine Antwort bekommen kann.

Das zurückgegebene Throwable ist vom Typ JsonSyntaxException und enthält leider nicht den ursprünglichen Antworttext, den die GSON-Bibliothek zu konvertieren versucht hat.

Antwort

0

Gestern schlug ich eine similar solution zu einer ähnlichen Frage, aber Sie scheinen eine leichte Modifikation der Antwort zu brauchen. Die Antwort enthält auch einige Kommentare zu den Bedenken hinsichtlich Effizienz und Speicherverbrauch. Wenn Sie diese Lösung verwenden würden, könnten Sie einen speziellen Ausfall-Handler erstellen, die eine spezielle Ausnahme halten einige Fehlerinformationen schaffen könnten:

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; 
     } 

    } 

} 

Anstatt Protokollierung es nur spezielle Ausnahme wirft eine privaten Konstruktor, instanceof ed sein könnte an der Anrufstelle. Der Eingabestream muss aus den Gründen gepuffert sein, die auch in der zugehörigen Frage kommentiert sind (insbesondere warum ein roher InputStream delegiert wird, aber kein ResponseBody). Nun ist die Art und Weise sie verwendet werden kann:

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()); 
        } 
       } 
     ); 

Beachten Sie, dass die copy und limit Methoden sind statisch Einfuhren von Google Guava ByteStreams. out und err sind statische Importe für System.out bzw. System.err.

Beispielausgabe:

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