英文:
generate CURL from java.net.http.HttpRequest
问题
有没有一种从 java.net.http.HttpRequest
生成 curl
命令的好方法?似乎没有很好的方法来获取请求的主体(如果有主体的话),最接近的方法是使用 bodyPublisher()
,它返回一个 Optional<BodyPublisher>
,其中只有一个 long contentLength()
方法。
英文:
is there a good way to generate curl
from java.net.http.HttpRequest
? It seems like there is no good way to get the body (if there is a body), the closest thing to it is bodyPublisher()
that returns Optional<BodyPublisher>
that has only long contentLength()
method.
答案1
得分: 0
的确,它比看起来要棘手得多。
java.net.http
包中的 HttpRequest
是一个通用对象,可以用作任何类型的 HttpClient<?>
请求,因此它不知道请求的主体是什么,通用地提供了一个主体发布器(未知类型),特定类型的订阅者可以以自定义方式订阅以从中获取内容。
这与通常从具有特定类型(通常是 String
)的 HttpClient<?>
获取的 HttpResponse<?>
完全不同,后者可以轻松返回 String getBody()
。
如果您不想使用任何第三方库(已实现以下逻辑),这是从发布器中提取(String
)主体的方式:
String requestBody = httpRequest.bodyPublisher().map(p -> {
HttpResponse.BodySubscriber<String> bodySubscriber = HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8);
StringSubscriber stringSubscriber = new StringSubscriber(bodySubscriber);
p.subscribe(stringSubscriber);
return bodySubscriber.getBody().toCompletableFuture().join();
}).orElse("");
其中 httpRequest
是您的 java.net.http.HttpRequest
,StringSubscriber
是实现了 Flow.Subscriber<ByteBuffer>
的类,如下所示:
class StringSubscriber implements Flow.Subscriber<ByteBuffer> {
private final HttpResponse.BodySubscriber<String> wrapped;
private StringSubscriber(HttpResponse.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();
}
}
解释:
如果请求中没有 BodyPublisher
,则简单地返回一个空字符串(假设这意味着没有主体,您可以自定义为 null
或任何其他您想要的值)。
否则,如果请求中有 BodyPublisher
,则将执行以下操作:
- 创建一个类型为
String
的BodySubscriber
(意味着您将订阅一个可以提供其主体的String
版本的发布器)。 - 创建
StringSubscriber
的新实例(您上面发布的自己的实现)。 - 使用您的订阅者订阅发布器,以便发布器可以通过流内容回调您。
- 当您从发布者回调时,获取未来结果(包含主体),因为这次调用是异步的,所以您需要使用
.join()
来等待它。
英文:
Indeed, it's way trickier than it looks like.
The HttpRequest
of java.net.http
package is a generic object that can be used as request for any type of HttpClient<?>
, hence it doesn't know what the body is, and generically provides a body publisher (type unknown) to which subscribers of a specific type can subscribe in a custom way in order to get the content from it.
This is very different from HttpResponse<?>
, that you usually get from a HttpClient<?>
with a specific type (usually String
) and that can so trivially return you a String getBody()
.
If you don't want to use any third-party library (which already implement the below logic), this is the way you can extract the (String
) body from the publisher:
String requestBody = httpRequest.bodyPublisher().map(p -> {
HttpResponse.BodySubscriber<String> bodySubscriber = HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8);
StringSubscriber stringSubscriber = new StringSubscriber(bodySubscriber);
p.subscribe(stringSubscriber);
return bodySubscriber.getBody().toCompletableFuture().join();
}).orElse("");
... where httpRequest
is your java.net.http.HttpRequest
and StringSubscriber
is an implementation of Flow.Subscriber<ByteBuffer>
like follows:
class StringSubscriber implements Flow.Subscriber<ByteBuffer> {
private final HttpResponse.BodySubscriber<String> wrapped;
private StringSubscriber(HttpResponse.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();
}
}
Explanation:
If there is no BodyPublisher
in your request, you simply return an empty string (assuming that means no body, you can customize it to null
or whatever you want).
Else, if there is a BodyPublisher
in your request, then you will do the following:
- You will create a
BodySubscriber
of typeString
(meaning you will subscribe to a publisher that can provide aString
version of its body) - Create a new instance of
StringSubscriber
(your own implementation that I posted above) - Subscribe to the publisher with your subscriber, so that the publisher can call you back with the stream content.
- Get the future result (containing the body) when you're called back from the publisher (the call is asynchronous so you need to
.join()
it)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论