web-dev-qa-db-fra.com

L'accès à deux reprises à la chaîne de corps d'une réponse OkHttp donne lieu à IllegalStateException: closed

J'implémente mes appels http via la bibliothèque OkHttp. Tout fonctionne bien, mais j'ai remarqué que, lorsque j'accède au corps en tant que chaîne de la réponse, une variable IllegalStateException est lancée. C'est-à-dire que je le fais (par exemple): Log.d("TAG", response.body().string()) et que je souhaite ensuite utiliser cette chaîne comme processResponse(response.body().string()). Mais ce deuxième appel lève l'exception avec le message closed

Comment est-il possible que l'accès à une chaîne deux fois entraîne un échec? Je souhaite traiter cette réponse sans avoir besoin d'ajouter un objet wrapper/fictif uniquement pour enregistrer certaines valeurs (telles que l'en-tête, le corps et le code d'état).

29
degill

La méthode string de la réponse lira le flux d’entrée (réseau) et le convertira en chaîne. Donc, il construit dynamiquement la chaîne et vous la renvoie. La deuxième fois que vous l'appelez, le flux réseau a déjà été consommé et n'est plus disponible. 

Vous devez enregistrer le résultat de string dans une variable String, puis y accéder autant de fois que nécessaire.

28
Greg Ennis

Pour une explication du problème, voir Réponse de Greg Ennis .

Cependant, si vous ne pouvez pas facilement passer le résultat dans une variable, mais que vous devez toujours accéder au corps de la réponse deux fois, vous avez une autre option:

Cloner le tampon avant de le lire. De ce fait, le tampon d'origine n'est ni vidé ni fermé. Voir cet extrait:

ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // request the entire body.
Buffer buffer = source.buffer();
// clone buffer before reading from it
String responseBodyString = buffer.clone().readString(Charset.forName("UTF-8"))
Log.d("TAG", responseBodyString);

Cette approche est utilisée dans HttpLoggingInterceptor dans projet okhttp par carré eux-mêmes.

46
Peter F

BTW en plus de la réponse de Greg Ennis, je peux dire à quelle occasion cela m'est arrivé lorsque j'ai oublié response.body (). String () dans la fenêtre d'affichage. Ainsi, sous le débogueur, le corps lisait dans la montre et le flux de réseau était fermé par la suite.

3
Alexandr

En développant un peu les réponses de user2011622 et Greg Ennis , j'ai créé une méthode qui vous aide à créer un clone complet du corps, vous permettant de consommer chaque copie du corps séparément. J'utilise cette méthode moi-même avec Retrofit2

/**
 * Clones a raw buffer so as not to consume the original
 * @param rawResponse the original {@link okhttp3.Response} as returned
 *                    by {@link Response#raw()}
 * @return a cloned {@link ResponseBody}
 */
private ResponseBody cloneResponseBody(okhttp3.Response rawResponse) {
    final ResponseBody responseBody = rawResponse.body();
    final Buffer bufferClone = responseBody.source().buffer().clone();
    return ResponseBody.create(responseBody.contentType(), responseBody.contentLength(), bufferClone);
}
0
Nick Cardoso

Vous pouvez appeler response.peekBody(Long.MAX_VALUE); pour mettre en mémoire tampon la réponse entière et en obtenir une copie allégée. Ce sera beaucoup moins de gaspillage. Voir ce numéro sur GitHub .

0
mugwort