如何在Java11中读取HttpRequest的主体?

如何在Java11中读取HttpRequest的主体?,java,httprequest,java-11,java-http-client,java-flow,Java,Httprequest,Java 11,Java Http Client,Java Flow,在测试中,我想查看HttpRequest的主体内部。我想把尸体当作一根绳子。看来唯一的办法就是订阅BodyPublisher,但这是怎么回事?这是一个有趣的问题。您从哪里获得您的HttpRequest?最简单的方法是直接从创建HttpRequest的代码中获取主体。如果不可能,那么下一步就是克隆该请求,并在通过HttpClient发送请求之前,将其body publisher包装在您自己的body publisher实现中。编写HttpRequest的子类应该相对容易(如果繁琐的话),该子类包装

在测试中,我想查看HttpRequest的主体内部。我想把尸体当作一根绳子。看来唯一的办法就是订阅BodyPublisher,但这是怎么回事?

这是一个有趣的问题。您从哪里获得您的
HttpRequest
?最简单的方法是直接从创建HttpRequest的代码中获取主体。如果不可能,那么下一步就是克隆该请求,并在通过HttpClient发送请求之前,将其body publisher包装在您自己的
body publisher
实现中。编写
HttpRequest
的子类应该相对容易(如果繁琐的话),该子类包装
HttpRequest
的另一个实例,并将每个调用委托给包装的实例,但重写
HttpRequest::bodyPublisher
以执行以下操作:

return request.bodyPublisher().map(this::wrapBodyPublisher);
否则,您也可以尝试订阅请求正文发布服务器并从中获取正文字节,但请注意并非所有
BodyPublisher
的实现都支持多个订阅服务器(无论是并发的还是顺序的)

为了说明我上面的建议:根据body publisher的具体实现,如果您可以防止并发订阅body publisher,下面的内容可能会起作用。也就是说,在一个受控测试环境中,您了解所有各方,那么它可能是可行的。不要在生产中使用任何此类产品:

public class HttpRequestBody {

    // adapt Flow.Subscriber<List<ByteBuffer>> to Flow.Subscriber<ByteBuffer>
    static final class StringSubscriber implements Flow.Subscriber<ByteBuffer> {
        final BodySubscriber<String> wrapped;
        StringSubscriber(BodySubscriber<String> wrapped) {
            this.wrapped = wrapped;
        }
        @Override
        public void onSubscribe(Flow.Subscription subscription) {
            wrapped.onSubscribe(subscription);
        }
        @Override
        public void onNext(ByteBuffer item) { wrapped.onNext(List.of(item)); }
        @Override
        public void onError(Throwable throwable) { wrapped.onError(throwable); }
        @Override
        public void onComplete() { wrapped.onComplete(); }
    }

    public static void main(String[] args) throws Exception {
        var request = HttpRequest.newBuilder(new URI("http://example.com/blah"))
                .POST(BodyPublishers.ofString("Lorem ipsum dolor sit amet"))
                .build();

        // you must be very sure that nobody else is concurrently 
        // subscribed to the body publisher when executing this code,
        // otherwise one of the subscribers is likely to fail.
        String reqbody = request.bodyPublisher().map(p -> {
            var bodySubscriber = BodySubscribers.ofString(StandardCharsets.UTF_8);
            var flowSubscriber = new StringSubscriber(bodySubscriber);
            p.subscribe(flowSubscriber);
            return bodySubscriber.getBody().toCompletableFuture().join();
        }).get();
        System.out.println(reqbody);
    }

}
公共类HttpRequestBody{
//使Flow.Subscriber适应Flow.Subscriber
静态最终类StringSubscriber实现Flow.Subscriber{
最终包装;
StringSubscriber(BodySubscriber包装){
this.wrapped=wrapped;
}
@凌驾
订阅时的公共作废(Flow.Subscription){
包装。认购(认购);
}
@凌驾
public void onNext(ByteBuffer项){wrapped.onNext(List.of(项));}
@凌驾
公共无效onError(可丢弃){wrapped.onError(可丢弃);}
@凌驾
public void onComplete(){wrapped.onComplete();}
}
公共静态void main(字符串[]args)引发异常{
var request=HttpRequest.newBuilder(新URI(“http://example.com/blah"))
.POST(bodypublisher.ofString(“Lorem ipsum door sit amet”))
.build();
//你必须非常确定没有其他人同时参加
//执行此代码时已订阅body publisher,
//否则,其中一个订阅服务器可能会失败。
String reqbody=request.bodyPublisher().map(p->{
var bodySubscriber=BodySubscribers.ofString(StandardCharsets.UTF_8);
var flowSubscriber=新的StringSubscriber(bodySubscriber);
p、 订阅(flowSubscriber);
返回bodySubscriber.getBody().toCompletableFuture().join();
}).get();
系统输出打印项次(reqbody);
}
}

我是从Mockito的ArgumentCaptor那里得到的。请求正文由替换占位符的字符串生成。这就是为什么我想检查HttpRequest,看看交换是否正确发生。希望这有帮助。我的场景与@progonkpa完全相同,我从Mockitos
ArgumentCaptor
收到请求。您的示例代码非常适合验证发送的正文,非常感谢。嘿@daniel…为什么我会收到
可选[()->kotlin.String!]
?我已经复制了您的代码并将语法调整为kotlin@daniel能够你能帮我一下吗?