#java #httprequest #java-11 #java-http-client #java-flow
#java #httprequest #java-11 #java-http-клиент #java-flow
Вопрос:
В тесте я хотел бы заглянуть внутрь тела HttpRequest. Я хотел бы получить тело в виде строки. Кажется, что единственный способ сделать это — подписаться на BodyPublisher, но как это работает?
Ответ №1:
Это интересный вопрос. Откуда вы получаете свой HttpRequest
? Самым простым способом было бы получить тело непосредственно из кода, который создает HttpRequest. Если это невозможно, то следующим шагом было бы клонировать этот запрос и поместить его body publisher в вашу собственную реализацию BodyPublisher
перед отправкой запроса через HttpClient. Должно быть относительно легко (если не утомительно) написать подкласс 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);
}
}
Комментарии:
1. Я получаю это из ArgumentCaptor от Mockito. Тело запроса создается из строки, в которой были заменены заполнители. Вот почему я хотел проверить HttpRequest, чтобы увидеть, правильно ли произошла замена.
2. У меня точно такой же сценарий, как @progonkpa, я получаю запрос от Mockitos
ArgumentCaptor
. Ваш пример кода отлично подходит для проверки отправляемого тела, большое спасибо.3. привет, @daniel… почему я получаю
Optional[() -> kotlin.String!]
? Я скопировал ваш код и настроил синтаксис на kotlin4. @placplacboom Извините, я не свободно говорю на kotlin. Может быть, вы сделали что-то не так при адаптации кода?