Android OkHttp如何记录请求正文
我正在使用一个拦截器,我想记录我正在发出的请求的主体,但我看不到任何方法可以做到这一点 可能吗Android OkHttp如何记录请求正文,android,okhttp,Android,Okhttp,我正在使用一个拦截器,我想记录我正在发出的请求的主体,但我看不到任何方法可以做到这一点 可能吗 public class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); long t1 = Syste
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]
以88,7ms的速度发布[一些url]
ZoneName:touraine
资料来源:安卓
正文:reformation.client.OkClient$1@1df53f05尼古拉的回答对我不起作用。我猜ByteString#toString()
的实现已经改变。这个解决方案对我有效:
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";
}
}
从readUtf8()
的文档中:
从中删除所有字节,将其解码为UTF-8,并返回字符串
这应该是你想要的。我试图对@msung的正确答案发表评论,但我的声誉不够高
这是我在发出完整请求之前对打印RequestBody所做的修改。它就像一个符咒。谢谢
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";
}
}
编辑
因为我看到仍然有一些人对这篇文章感兴趣,这里是我的日志拦截器的最终版本(直到下一次改进)。我希望这能节省你们一些人的时间
请注意,此代码正在使用OkHttp 2.2.0
(和改装1.9.0
)
处理带或不带正文的请求的版本:
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 "";
}
对于OkHttp的当前版本,您可以使用并将级别设置为BODY
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(Level.BODY);
使用这种方法,您无法为不同的HTTP方法粒度配置输出,但它也适用于可能有正文的其他方法
下面的示例显示了补丁
请求的输出(最小编辑):
如您所见,这也会打印出标题,正如文档中所述,您应该特别注意:
此拦截器在使用标题
或正文
级别时生成的日志有可能泄漏敏感信息,如“授权”或“Cookie”标题以及请求和响应正文的内容。此数据只能以受控方式或在非生产环境中记录
您可以通过调用redactHeader()
来编辑可能包含敏感信息的标题
创建一个单独的新类并实现拦截器
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)
}
用于查找缓冲区数据是否为UT8格式的扩展
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版本:
val buf = okio.Buffer()
requestBody.writeTo(buf)
Log.d("AppXMLPostReq", "reqBody = ${buf.readUtf8()}")
@MathieudBrito tryresponse.body().string()
@aga谢谢,但这不会复制响应正文,因此当我稍后尝试读取正文时(在拦截器之后),它会告诉我responsebody为null并且已经被读取。您使用哪个“缓冲区”来执行此操作writeTo()
仅对java.nio.Buffer未实现的BufferedSink对象执行。@Graeme Use okio.Buffer^希望看到此类示例中提到的正确导入语句只返回一组随机字符串。不工作你有没有序列化过它?没有,我没有序列化过它…你能不能编辑你的答案,以精确的方式打印请求正文?很抱歉,我不记得它从内存打印了什么,我会在我处理那个项目时添加它。为什么我们需要创建副本?如果你能够将导入内容上传到顶部,这将是非常有帮助的,因为有多个版本的OKHttp,我正在玩猜测和检查,以找出您在这里有哪些版本。谢谢你完成了,希望有帮助;)是的,非常感谢您对Mathieu的澄清。如何仅从请求对象获取[{“op”:“add”,“path”:“/path”,“value”:true})?
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)
}
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.
}
}
val buf = okio.Buffer()
requestBody.writeTo(buf)
Log.d("AppXMLPostReq", "reqBody = ${buf.readUtf8()}")