Hot to get body as String from spring reactive ClientRequest?(从春季反应式客户端请求中获得身体作为弦的热门吗?)
问题描述
在测试方法中,收到org.springframework.web.reactive.function.client.ClientRequest的实例。
我要验证其HttpMethod、URI和Body。
如何获得除身体以外的所有东西是相当明显的。
ClientRequest request = makeInstance(...);
assertEquals(HttpMethod.POST, request.method());
assertEquals("somewhere/else", request.url().toString());
// ? unclear how to extract body using the BodyInserter
BodyInserter<?, ? super ClientHttpRequest> inserter = request.body();
inserter.insert(%outputMessage%, %context%);
我在Spring的源代码中找到了how BodyInserters are tested。如何创建BodyInserter.Context(第二个参数)我大概很清楚,但我不明白如何构造第一个参数,所以可以通过它提取请求正文。
请显示从ClientRequest实例获取请求正文的常规(或至少可用)方法。
推荐答案
对于这么简单的情况来说有点复杂,但我实现了5个类,从ClientRequest中提取了一个正文。
这似乎太多了,我仍然很好奇这个问题是否有一个短期的解决方案。欢迎您对这个问题提出另一个答案,这样我就可以接受了。
遗憾的是,<[2-2]、BodyInserters和org.springframework.web.reactive.***中的大多数其他内容的设计都有很大的改进空间。目前,它只是一堆接口,每个接口都有大量的方法,测试代码通常需要花费大量的精力,具体取决于这些类。
使用此方法的主要目标是:
static <T> T extractBody(ClientRequest request, Class<T> clazz) {
  InsertionReceiver<T> receiver = InsertionReceiver.forClass(clazz);
  return receiver.receiveValue(request.body());
}
以下是InsertionReceiver的实现:
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.web.reactive.function.BodyInserter;
public interface InsertionReceiver<T> {
  T receiveValue(BodyInserter<?, ? extends ReactiveHttpOutputMessage> bodyInserter);
  static <T> InsertionReceiver<T> forClass(Class<T> clazz) {
    return new SimpleValueReceiver<>(clazz);
  }
}
import java.util.concurrent.atomic.AtomicReference;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.web.reactive.function.BodyInserter;
    
class SimpleValueReceiver<T> implements InsertionReceiver<T> {
  private static final Object DUMMY = new Object();
  private final Class<T> clazz;
  private final AtomicReference<Object> reference;
  SimpleValueReceiver(Class<T> clazz) {
    this.clazz = clazz;
    this.reference = new AtomicReference<>(DUMMY);
  }
  @Override
  public T receiveValue(BodyInserter<?, ? extends ReactiveHttpOutputMessage> bodyInserter) {
    demandValueFrom(bodyInserter);
    return receivedValue();
  }
  private void demandValueFrom(BodyInserter<?, ? extends ReactiveHttpOutputMessage> bodyInserter) {    
    var inserter = (BodyInserter<?, ReactiveHttpOutputMessage>) bodyInserter;
    inserter.insert(
        MinimalHttpOutputMessage.INSTANCE,
        new SingleWriterContext(new WriteToConsumer<>(reference::set))
    );
  }
  private T receivedValue() {
    Object value = reference.get();
    reference.set(DUMMY);
    T validatedValue;
    if (value == DUMMY) {
      throw new RuntimeException("Value was not received, Check your inserter worked properly");
    } else if (!clazz.isAssignableFrom(value.getClass())) {
      throw new RuntimeException(
          "Value has unexpected type ("
              + value.getClass().getTypeName()
              + ") instead of (" + clazz.getTypeName() + ")");
    } else {
      validatedValue = clazz.cast(value);
    }
    return validatedValue;
  }
}
class WriteToConsumer<T> implements HttpMessageWriter<T> {
  private final Consumer<T> consumer;
  private final List<MediaType> mediaTypes;
  WriteToConsumer(Consumer<T> consumer) {
    this.consumer = consumer;
    this.mediaTypes = Collections.singletonList(MediaType.ALL);
  }
  @Override
  public List<MediaType> getWritableMediaTypes() {
    return mediaTypes;
  }
  @Override
  public boolean canWrite(ResolvableType elementType, MediaType mediaType) {
    return true;
  }
  @Override
  public Mono<Void> write(
      Publisher<? extends T> inputStream,
      ResolvableType elementType,
      MediaType mediaType,
      ReactiveHttpOutputMessage message,
      Map<String, Object> hints
  ) {
    inputStream.subscribe(new OneValueConsumption<>(consumer));
    return Mono.empty();
  }
}
class MinimalHttpOutputMessage implements ReactiveHttpOutputMessage {
  public static MinimalHttpOutputMessage INSTANCE = new MinimalHttpOutputMessage();
  private MinimalHttpOutputMessage() {
  }
  @Override
  public HttpHeaders getHeaders() {
    return HttpHeaders.EMPTY;
  }
  // other overridden methods are omitted as they do nothing,
  // i.e. return null, false, or have empty bodies
}
class OneValueConsumption<T> implements Subscriber<T> {
  private final Consumer<T> consumer;
  private int remainedAccepts;
  public OneValueConsumption(Consumer<T> consumer) {
    this.consumer = Objects.requireNonNull(consumer);
    this.remainedAccepts = 1;
  }
  @Override
  public void onSubscribe(Subscription s) {
    s.request(1);
  }
  @Override
  public void onNext(T o) {
    if (remainedAccepts > 0) {
      consumer.accept(o);
      remainedAccepts -= 1;
    } else {
      throw new RuntimeException("No more values can be consumed");
    }
  }
  @Override
  public void onError(Throwable t) {
    throw new RuntimeException("Single value was not consumed", t);
  }
  @Override
  public void onComplete() {
    // nothing
  }
}
class SingleWriterContext implements BodyInserter.Context {
  private final List<HttpMessageWriter<?>> singleWriterList;
  SingleWriterContext(HttpMessageWriter<?> writer) {
    this.singleWriterList = List.of(writer);
  }
  @Override
  public List<HttpMessageWriter<?>> messageWriters() {
    return singleWriterList;
  }
  @Override
  public Optional<ServerHttpRequest> serverRequest() {
    return Optional.empty();
  }
  @Override
  public Map<String, Object> hints() {
    return null;
  }
}
                        这篇关于从春季反应式客户端请求中获得身体作为弦的热门吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:从春季反应式客户端请求中获得身体作为弦的热门吗?
				
        
 
            
        - 在 Java 中,如何将 String 转换为 char 或将 char 转换 2022-01-01
 - 获取数字的最后一位 2022-01-01
 - GC_FOR_ALLOC 是否更“严重"?在调查内存使用情况时? 2022-01-01
 - 转换 ldap 日期 2022-01-01
 - 如何使 JFrame 背景和 JPanel 透明且仅显示图像 2022-01-01
 - 未找到/usr/local/lib 中的库 2022-01-01
 - 将 Java Swing 桌面应用程序国际化的最佳实践是什么? 2022-01-01
 - Eclipse 的最佳 XML 编辑器 2022-01-01
 - 如何指定 CORS 的响应标头? 2022-01-01
 - java.lang.IllegalStateException:Bean 名称“类别"的 BindingResult 和普通目标对象都不能用作请求属性 2022-01-01
 
						
						
						
						
						
				
				
				
				