如何使用 Spring 5 Webclient 打印原始 HTTP 请求和 HTTP 响应?
How to print raw HTTP Request and HTTP Response with Spring 5 Webclient?
Spring MVC 允许记录请求和响应 body 以便更轻松地调试和验证消息内容。这是我的项目出于审计目的所必需的,日志消息必须包含完整的请求和响应 body.
使用 Spring Web Reactive 和 Webclient,如何在没有十六进制值的情况下记录请求和响应 body?
所需格式为 RAW HTTP 请求,例如
PUT /api/v1/target/{id}
HTTP/1.1
Host: https://testsite.com:8080
Authorization: Bearer myToken
Content-Type: application/json
{
"test": "test
}
目前,其他答案仅使用 Reactor Netty 调试日志记录提供粗略的 hex+msg 输出或仅输出 HTTP headers 请求。前任。当前实施:
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.|
|00000010| 0a 53 65 72 76 65 72 3a 20 6e 67 69 6e 78 2f 31 |.Server: nginx/1|
|00000020| 2e 31 36 2e 31 0d 0a 44 61 74 65 3a 20 4d 6f 6e |.16.1..Date: Mon|
|00000030| 2c 20 30 35 20 4f 63 74 20 32 30 32 30 20 31 33 |, 05 Oct 2020 13|
|00000040| 3a 35 39 3a 33 36 20 47 4d 54 0d 0a 43 6f 6e 74 |:59:36 GMT..Cont|
|00000050| 65 6e 74 2d 54 79 70 65 3a 20 61 70 70 6c 69 63 |ent-Type: applic|
|00000060| 61 74 69 6f 6e 2f 6a 73 6f 6e 3b 20 63 68 61 72 |ation/json; char|
|00000070| 73 65 74 3d 75 74 66 2d 38 0d 0a 43 6f 6e 74 65 |set=utf-8..Conte|
|00000080| 6e 74 2d 4c 65 6e 67 74 68 3a 20 31 30 37 38 0d |nt-Length: 1078.|
|00000090| 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 |.Connection: kee|
|000000a0| 70 2d 61 6c 69 76 65 0d 0a 58 2d 50 6f 77 65 72 |p-alive..----|
.....
.....
这更难阅读,审计员 copy-paste。
有没有办法改变这种格式?如何简单地打印请求 body.
我同意违反 Spring Webflux 中的 non-blocking 建议。 RestTemplate 被列为弃用目标,这意味着 Webclient 将用于阻止操作。对于此项目,审计比绩效更重要。
在测试包含的评论中的所有选项后在此处回答。
拉入相关依赖
# Gradle
implementation group: 'org.eclipse.jetty', name: 'jetty-reactive-httpclient', version: '1.1.4'
# Maven
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-reactive-httpclient</artifactId>
<version>1.1.4</version>
</dependency>
创建将在网络客户端流程中执行的方法。在webclient的定义中使用:
// org.eclipse.jetty.client.api.Request
private Request enhance(Request inboundRequest) {
StringBuilder log = new StringBuilder();
// Request Logging
inboundRequest.onRequestBegin(request ->
log.append("Request: \n")
.append("URI: ")
.append(request.getURI())
.append("\n")
.append("Method: ")
.append(request.getMethod()));
inboundRequest.onRequestHeaders(request -> {
log.append("\nHeaders:\n");
for (HttpField header : request.getHeaders()) {
log.append("\t\t" + header.getName() + " : " + header.getValue() + "\n");
}
});
inboundRequest.onRequestContent((request, content) ->
log.append("Body: \n\t")
.append(content.toString()));
log.append("\n");
// Response Logging
inboundRequest.onResponseBegin(response ->
log.append("Response:\n")
.append("Status: ")
.append(response.getStatus())
.append("\n"));
inboundRequest.onResponseHeaders(response -> {
log.append("Headers:\n");
for (HttpField header : response.getHeaders()) {
log.append("\t\t" + header.getName() + " : " + header.getValue() + "\n");
}
});
inboundRequest.onResponseContent(((response, content) -> {
var bufferAsString = StandardCharsets.UTF_8.decode(content).toString();
log.append("Response Body:\n" + bufferAsString);
}));
// Add actual log invocation
logger.info("HTTP ->\n");
inboundRequest.onRequestSuccess(request -> logger.info(log.toString()));
inboundRequest.onResponseSuccess(response -> logger.info(log.toString()));
// Return original request
return inboundRequest;
}
使用以下方法定义 webclient(这包括将 webclient 定义为根据需要注入的 bean 的示例):
@Bean
public WebClient jettyHttpClient() {
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
HttpClient httpClient = new HttpClient(sslContextFactory) {
@Override
public Request newRequest(URI uri) {
Request request = super.newRequest(uri);
return enhance(request);
}
};
return WebClient.builder().clientConnector(new JettyClientHttpConnector(httpClient)).build();
}
Spring MVC 允许记录请求和响应 body 以便更轻松地调试和验证消息内容。这是我的项目出于审计目的所必需的,日志消息必须包含完整的请求和响应 body.
使用 Spring Web Reactive 和 Webclient,如何在没有十六进制值的情况下记录请求和响应 body?
所需格式为 RAW HTTP 请求,例如
PUT /api/v1/target/{id}
HTTP/1.1
Host: https://testsite.com:8080
Authorization: Bearer myToken
Content-Type: application/json
{
"test": "test
}
目前,其他答案仅使用 Reactor Netty 调试日志记录提供粗略的 hex+msg 输出或仅输出 HTTP headers 请求。前任。当前实施:
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.|
|00000010| 0a 53 65 72 76 65 72 3a 20 6e 67 69 6e 78 2f 31 |.Server: nginx/1|
|00000020| 2e 31 36 2e 31 0d 0a 44 61 74 65 3a 20 4d 6f 6e |.16.1..Date: Mon|
|00000030| 2c 20 30 35 20 4f 63 74 20 32 30 32 30 20 31 33 |, 05 Oct 2020 13|
|00000040| 3a 35 39 3a 33 36 20 47 4d 54 0d 0a 43 6f 6e 74 |:59:36 GMT..Cont|
|00000050| 65 6e 74 2d 54 79 70 65 3a 20 61 70 70 6c 69 63 |ent-Type: applic|
|00000060| 61 74 69 6f 6e 2f 6a 73 6f 6e 3b 20 63 68 61 72 |ation/json; char|
|00000070| 73 65 74 3d 75 74 66 2d 38 0d 0a 43 6f 6e 74 65 |set=utf-8..Conte|
|00000080| 6e 74 2d 4c 65 6e 67 74 68 3a 20 31 30 37 38 0d |nt-Length: 1078.|
|00000090| 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 |.Connection: kee|
|000000a0| 70 2d 61 6c 69 76 65 0d 0a 58 2d 50 6f 77 65 72 |p-alive..----|
.....
.....
这更难阅读,审计员 copy-paste。
有没有办法改变这种格式?如何简单地打印请求 body.
我同意违反 Spring Webflux 中的 non-blocking 建议。 RestTemplate 被列为弃用目标,这意味着 Webclient 将用于阻止操作。对于此项目,审计比绩效更重要。
在测试包含的评论中的所有选项后在此处回答。
拉入相关依赖
# Gradle
implementation group: 'org.eclipse.jetty', name: 'jetty-reactive-httpclient', version: '1.1.4'
# Maven
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-reactive-httpclient</artifactId>
<version>1.1.4</version>
</dependency>
创建将在网络客户端流程中执行的方法。在webclient的定义中使用:
// org.eclipse.jetty.client.api.Request
private Request enhance(Request inboundRequest) {
StringBuilder log = new StringBuilder();
// Request Logging
inboundRequest.onRequestBegin(request ->
log.append("Request: \n")
.append("URI: ")
.append(request.getURI())
.append("\n")
.append("Method: ")
.append(request.getMethod()));
inboundRequest.onRequestHeaders(request -> {
log.append("\nHeaders:\n");
for (HttpField header : request.getHeaders()) {
log.append("\t\t" + header.getName() + " : " + header.getValue() + "\n");
}
});
inboundRequest.onRequestContent((request, content) ->
log.append("Body: \n\t")
.append(content.toString()));
log.append("\n");
// Response Logging
inboundRequest.onResponseBegin(response ->
log.append("Response:\n")
.append("Status: ")
.append(response.getStatus())
.append("\n"));
inboundRequest.onResponseHeaders(response -> {
log.append("Headers:\n");
for (HttpField header : response.getHeaders()) {
log.append("\t\t" + header.getName() + " : " + header.getValue() + "\n");
}
});
inboundRequest.onResponseContent(((response, content) -> {
var bufferAsString = StandardCharsets.UTF_8.decode(content).toString();
log.append("Response Body:\n" + bufferAsString);
}));
// Add actual log invocation
logger.info("HTTP ->\n");
inboundRequest.onRequestSuccess(request -> logger.info(log.toString()));
inboundRequest.onResponseSuccess(response -> logger.info(log.toString()));
// Return original request
return inboundRequest;
}
使用以下方法定义 webclient(这包括将 webclient 定义为根据需要注入的 bean 的示例):
@Bean
public WebClient jettyHttpClient() {
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
HttpClient httpClient = new HttpClient(sslContextFactory) {
@Override
public Request newRequest(URI uri) {
Request request = super.newRequest(uri);
return enhance(request);
}
};
return WebClient.builder().clientConnector(new JettyClientHttpConnector(httpClient)).build();
}