Resolve the response body(). String() cannot read multiple times

Project scenario:

Recently, the requirement of the project is to obtain a new token if the token fails when requesting the interface, and finally use the new token to request the original requested interface. So here we will use the response Interceptor, which is used in my project EasyHttp , is the encapsulation of okhttp. You can have a look if you are interested. Here, we need to write a response interceptor to intercept the server's request to return whether the token is invalid. If the returned token is invalid, we will synchronously request the new token back in the interceptor, and then fill the new token into the previous request to obtain the data again.

Interceptor part code:

/**
 * The response interceptor is used to verify the token
 */
public class TokenInterceptor implements Interceptor {
    private static final String TAG = "TokenInterceptor";

    @NonNull
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        try {
            //Judge whether the token is expired
            if (isTokenExpired(response)) {
                //The synchronization requester obtains the latest Token
                String newToken = getNewToken();
                //Use the new Token and use newBuilder to reverse create a new request
                Request newRequest = chain.request()
                        .newBuilder()
                        .header(DEV_SESSION, newToken)
                        .build();
                //Re request
                return chain.proceed(newRequest);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response;
    }

Problem description

But the problem comes. In the interceptor, we need to obtain the returned Response extraction data to judge whether the token is expired. We need to use Response body. String () to read data.

   try {
      String  text = response.body.string();
    } catch (Exception e) {
        // Return result reading exception
        e.printStackTrace();
    }

It is normal to read here, and then use Response again in the class that returns Response after unified resolution body. When string (), it will report an exception Java Lang. IllegalStateException: closed. Then I found a solution on the Internet. This damn Baidu is getting worse and worse. 9 of the 10 found are the same article content. It is still necessary to use Google to develop and check things

Cause analysis:

First, I checked the reason on the Internet. The original response body. String () can only be used once. I have used it once in the interceptor and will report an error when I use it again. According to the source code analysis, it turns out that okhttp will start and close by default after reading the returned results. Therefore, when I use it the second time, it will prompt the closed exception, so I consider solving this problem in the interceptor and using other methods instead.

Solution:

If you don't have much to say, go directly to the solution code:

 /**
     * Resolve ResponseBody
     * @param responseBody -
     * @return Analytical results
     */
    private String getResponseBody(ResponseBody responseBody) throws Exception {
        BufferedSource source = responseBody.source();
        source.request(Long.MAX_VALUE);
        Buffer buffer = source.getBuffer();

        Charset charset = StandardCharsets.UTF_8;
        MediaType contentType = responseBody.contentType();
        if (contentType != null) {
            try {
                charset = contentType.charset(StandardCharsets.UTF_8);
            } catch (UnsupportedCharsetException e) {
                DebugLog.e("take http Data write stream exception,Cause of abnormality:" + Arrays.toString(e.getStackTrace()));
            }
        }

        if (!isPlaintext(buffer)) {
            return null;
        }

        if (responseBody.contentLength() != 0) {
            assert charset != null;
            return buffer.clone().readString(charset);
        }
        return null;
    }


    private boolean isPlaintext(Buffer buffer) throws EOFException {
        try {
            Buffer prefix = new Buffer();
            long byteCount = buffer.size() < 64 ? buffer.size() : 64;
            buffer.copyTo(prefix, 0, byteCount);
            for (int i = 0; i < 16; i++) {
                if (prefix.exhausted()) {
                    break;
                }
                int codePoint = prefix.readUtf8CodePoint();
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false;
                }
            }
            return true;
        } catch (EOFException e) {
            return false;
        }
    }

The second way to read is to get the ResponseBody BufferedSource in source () is parsed so that the shutdown will not be triggered. Later, use response again body. String () will be fine.

By the way, when I get a new token, I use Okhttp to recreate a synchronization request. The reading here is a separate request, so I don't need to consider the second time.

Take this as a record. I hope it will be helpful to you when you encounter problems.

Reference article: http://t.zoukankan.com/duguxiaobiao-p-12092231.html

Keywords: Java Android http

Added by ralphuk100 on Wed, 09 Mar 2022 05:28:36 +0200