如何记录请求体

我正在使用一个拦截器,我想要记录我正在发出的请求的主体,但是我看不到任何方法可以做到这一点。

有可能吗?

public class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();


long t1 = System.nanoTime();
Response response = chain.proceed(request);
long t2 = System.nanoTime();


double time = (t2 - t1) / 1e6d;


if (request.method().equals("GET")) {
Logs.info(String.format("GET " + F_REQUEST_WITHOUT_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), response.code(), response.headers(), response.body().charStream()));
} else if (request.method().equals("POST")) {
Logs.info(String.format("POST " + F_REQUEST_WITH_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), request.body(), response.code(), response.headers(), response.body().charStream()));
} else if (request.method().equals("PUT")) {
Logs.info(String.format("PUT " + F_REQUEST_WITH_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), request.body().toString(), response.code(), response.headers(), response.body().charStream()));
} else if (request.method().equals("DELETE")) {
Logs.info(String.format("DELETE " + F_REQUEST_WITHOUT_BODY + F_RESPONSE_WITHOUT_BODY, request.url(), time, request.headers(), response.code(), response.headers()));
}


return response;
}
}

结果是:

POST  [some url] in 88,7ms
ZoneName: touraine
Source: Android
body: retrofit.client.OkClient$1@1df53f05 <-request.body().toString() gives me this, but I would like the content string
Response: 500
Date: Tue, 24 Feb 2015 10:14:22 GMT
body: [some content]
75819 次浏览

Nikola's answer did not work for me. My guess is the implementation of ByteString#toString() changed. This solution worked for me:

private static String bodyToString(final Request request){


try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}

From the documentation of readUtf8():

Removes all bytes from this, decodes them as UTF-8, and returns the string.

which should be what you want.

I tried to comment on the correct answer from @msung, but my reputation isn't high enough.

Here's modification I did to print RequestBody before making it a full request. It works like a charm. Thanks

private static String bodyToString(final RequestBody request){
try {
final RequestBody copy = request;
final Buffer buffer = new Buffer();
copy.writeTo(buffer);
return buffer.readUtf8();
}
catch (final IOException e) {
return "did not work";
}
}

EDIT

Because I see there is still some people interested by this post, here is the final version (until next improvement) of my log interceptor. I hope it will save some of you guys's time.

Please note that this code is using OkHttp 2.2.0 (and Retrofit 1.9.0)

import com.squareup.okhttp.*;
import okio.Buffer;
import java.io.IOException;


public class LoggingInterceptor implements Interceptor {


private static final String F_BREAK = " %n";
private static final String F_URL = " %s";
private static final String F_TIME = " in %.1fms";
private static final String F_HEADERS = "%s";
private static final String F_RESPONSE = F_BREAK + "Response: %d";
private static final String F_BODY = "body: %s";


private static final String F_BREAKER = F_BREAK + "-------------------------------------------" + F_BREAK;
private static final String F_REQUEST_WITHOUT_BODY = F_URL + F_TIME + F_BREAK + F_HEADERS;
private static final String F_RESPONSE_WITHOUT_BODY = F_RESPONSE + F_BREAK + F_HEADERS + F_BREAKER;
private static final String F_REQUEST_WITH_BODY = F_URL + F_TIME + F_BREAK + F_HEADERS + F_BODY + F_BREAK;
private static final String F_RESPONSE_WITH_BODY = F_RESPONSE + F_BREAK + F_HEADERS + F_BODY + F_BREAK + F_BREAKER;


@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();


long t1 = System.nanoTime();
Response response = chain.proceed(request);
long t2 = System.nanoTime();


MediaType contentType = null;
String bodyString = null;
if (response.body() != null) {
contentType = response.body().contentType();
bodyString = response.body().string();
}


double time = (t2 - t1) / 1e6d;


if (request.method().equals("GET")) {
System.out.println(String.format("GET " + F_REQUEST_WITHOUT_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), response.code(), response.headers(), stringifyResponseBody(bodyString)));
} else if (request.method().equals("POST")) {
System.out.println(String.format("POST " + F_REQUEST_WITH_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), stringifyRequestBody(request), response.code(), response.headers(), stringifyResponseBody(bodyString)));
} else if (request.method().equals("PUT")) {
System.out.println(String.format("PUT " + F_REQUEST_WITH_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), request.body().toString(), response.code(), response.headers(), stringifyResponseBody(bodyString)));
} else if (request.method().equals("DELETE")) {
System.out.println(String.format("DELETE " + F_REQUEST_WITHOUT_BODY + F_RESPONSE_WITHOUT_BODY, request.url(), time, request.headers(), response.code(), response.headers()));
}


if (response.body() != null) {
ResponseBody body = ResponseBody.create(contentType, bodyString);
return response.newBuilder().body(body).build();
} else {
return response;
}
}




private static String stringifyRequestBody(Request request) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}


public String stringifyResponseBody(String responseBody) {
return responseBody;
}
}

Version that handles requests with or without a body:

private String stringifyRequestBody(Request request) {
if (request.body() != null) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
Log.w(TAG, "Failed to stringify request body: " + e.getMessage());
}
}
return "";
}

With a current version of OkHttp, you can use the HTTP Logging Interceptor and set the level to BODY

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(Level.BODY);

With this you cannot granularily configure the output for different HTTP methods, but it also works for other methods that might have a body.

Here an example showing the output of a PATCH request (minimally redacted):

--> PATCH https://hostname/api/something/123456 HTTP/1.1
Content-Type: application/json-patch+json; charset=utf-8
Content-Length: 49
Authorization: Basic YWRtaW46c2VjcmV0Cg==
Accept: application/json


[ { "op": "add", "path": "/path", "value": true }]
--> END PATCH (xx-byte body)

As you can see, this also prints out the headers and as the documentation states, you should really take care:

The logs generated by this interceptor when using the HEADERS or BODY levels have the potential to leak sensitive information such as "Authorization" or "Cookie" headers and the contents of request and response bodies. This data should only be logged in a controlled way or in a non-production environment.

You can redact headers that may contain sensitive information by calling redactHeader().

logging.redactHeader("Authorization");
logging.redactHeader("Cookie");

Create a Separate new class and implement Intercepter.

  override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
var logInfo = ""
val requestBody=loggerUtil.getRequestBody
return response
}


yourOkHttpClient.addInterceptor(yourInstance)

GetRequestBody

            var requestContent = ""
val requestBody = request.body
val buffer = Buffer()
if (requestBody != null) {
requestBody.writeTo(buffer)
}


val contentType = requestBody?.contentType()
val charset: Charset =
contentType?.charset(StandardCharsets.UTF_8) ?:StandardCharsets.UTF_8


if (buffer.isProbablyUtf8()) {
requestContent = buffer.readString(charset)
}

Extension to find Whether buffer data is UT8 format

fun Buffer.isProbablyUtf8(): Boolean {
try {
val prefix = Buffer()
val byteCount = size.coerceAtMost(64)
copyTo(prefix, 0, byteCount)
for (i in 0 until 16) {
if (prefix.exhausted()) {
break
}
val codePoint = prefix.readUtf8CodePoint()
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
return false
}
}
return true
} catch (_: EOFException) {
return false // Truncated UTF-8 sequence.
}
}

Kotlin version :

val buf = okio.Buffer()
requestBody.writeTo(buf)
Log.d("AppXMLPostReq", "reqBody = ${buf.readUtf8()}")

Are we supposed to call .close() on the buffer object ? or use the try-with-resources statement ?

private String getBodyAsString(Request request) throws IOException {
try(var buffer = new Buffer()) {
request.body().writeTo(buffer);
return buffer.readUtf8();
}
}