生成来自java.net.http.HttpRequest的CURL命令

huangapple go评论57阅读模式
英文:

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.HttpRequestStringSubscriber 是实现了 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,则将执行以下操作:

  • 创建一个类型为 StringBodySubscriber(意味着您将订阅一个可以提供其主体的 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 type String (meaning you will subscribe to a publisher that can provide a String 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)

huangapple
  • 本文由 发表于 2023年2月6日 19:11:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/75360555.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定