简体   繁体   English

如何使用 Spring 5 Webclient 打印原始 HTTP 请求和 HTTP 响应?

[英]How to print raw HTTP Request and HTTP Response with Spring 5 Webclient?

Spring MVC allows for logging of request and response body to allow for easier debugging and verification of message content. Spring MVC 允许记录请求和响应主体,以便更轻松地调试和验证消息内容。 This is required for my project for auditing purposes, the log messages MUST contain the full request and response body.这是我的项目出于审计目的所必需的,日志消息必须包含完整的请求和响应正文。

Using Spring Web Reactive and Webclient, how to log request and response body without hex values?使用 Spring Web Reactive 和 Webclient,如何记录没有十六进制值的请求和响应正文?

The required format is RAW HTTP request, Ex.所需的格式是 RAW HTTP 请求,例如。

PUT /api/v1/target/{id}

HTTP/1.1
Host: https://testsite.com:8080
Authorization: Bearer myToken
Content-Type: application/json

{
  "test": "test
}

Currently, other answers only provide a rough hex+msg output using Reactor Netty debug logging OR output only HTTP headers for request.目前,其他答案仅使用 Reactor Netty 调试日志或仅输出请求的 HTTP 标头提供粗略的 hex+msg 输出。 Ex.前任。 of current implementation:当前实现:

         +-------------------------------------------------+
     |  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..----|
.....
.....

This is much more difficult to read and for auditors to copy-paste.这更难阅读,审计员也难以复制粘贴。

Is there a way to alter this format?有没有办法改变这种格式? How does one simply print the request body.如何简单地打印请求正文。

I am OK with breaking the non-blocking recommendation within Spring Webflux.我可以打破 Spring Webflux 中的非阻塞建议。 RestTemplate is listed as targeted for deprecation which means Webclient will be used for blocking operations. RestTemplate 被列为弃用目标,这意味着 Webclient 将用于阻止操作。 Auditing is more important than performance for this project.对于这个项目,审计比绩效更重要。

Answering here after testing all the options in the included comments.在测试包含的评论中的所有选项后回答这里。

To pull in the relevant dependencies拉入相关的依赖项

# 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>

Create the method that will execute during the webclient flow.创建将在 webclient 流程中执行的方法。 Used in the definition of the webclient:在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;
}

Define the webclient using the following method (this includes an example of defining the webclient as a bean to be injected as needed):使用以下方法定义 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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM