web-dev-qa-db-fra.com

Comment enregistrer la demande / réponse en utilisant Java.net.http.HttpClient?

HttpClient introduit expérimentalement dans Java 9 est maintenant stable dans Java 11, mais sans surprise, très peu de projets semblent réellement utiliser La documentation est presque inexistante.

L'une des demandes les plus courantes lors d'un appel HTTP est l'enregistrement de la demande/réponse. Comment feriez-vous cela en utilisant le HttpClient, sans bien sûr le journaliser manuellement à chaque appel? Existe-t-il un mécanisme d'intercepteur comme celui proposé par tous les autres clients HTTP?

9
Abhijit Sarkar

Si nous regardons jdk.internal.net.http.common.DebugLogger code source, nous pouvons voir quelques enregistreurs utilisant System.Logger , qui à son tour utilisera System.LoggerFinder pour sélectionner la structure de l'enregistreur. JUL est le choix par défaut. Les noms des enregistreurs sont:

  • jdk.internal.httpclient.debug
  • jdk.internal.httpclient.websocket.debug
  • jdk.internal.httpclient.hpack.debug

Ils peuvent être activés en les définissant comme propriété système. Par exemple, exécuter avec -Djdk.internal.httpclient.debug=true produira:

DEBUG: [main] [147ms] HttpClientImpl(1) proxySelector is Sun.net.spi.DefaultProxySelector@6dde5c8c (user-supplied=false)
DEBUG: [main] [183ms] HttpClientImpl(1) ClientImpl (async) send https://http2.github.io/ GET
DEBUG: [main] [189ms] Exchange establishing exchange for https://http2.github.io/ GET,
     proxy=null
DEBUG: [main] [227ms] PlainHttpConnection(?) Initial receive buffer size is: 43690
DEBUG: [main] [237ms] PlainHttpConnection(SocketTube(1)) registering connect event
DEBUG: [HttpClient-1-SelectorManager] [239ms] SelectorAttachment Registering jdk.internal.net.http.PlainHttpConnection$ConnectEvent@354bf356 for 8 (true)
...
7
Karol Dowbecki

Vous pouvez consigner les demandes et les réponses en spécifiant -Djdk.httpclient.HttpClient.log=requests sur la ligne de commande Java.

En ce qui concerne les tests/moqueries, vous voudrez peut-être jeter un œil au test hors ligne: http://hg.openjdk.Java.net/jdk/jdk/file/tip/test/jdk/Java/net/httpclient/hors ligne /

Selon ce que vous cherchez à réaliser, vous pouvez également utiliser un "DelegatingHttpClient" pour intercepter et enregistrer les demandes et les réponses.

Outre la Java, il existe également une documentation de haut niveau sur http://openjdk.Java.net/groups/net/httpclient/index.html

Remarque additionnelle:

Le jdk.httpclient.HttpClient.log La propriété est une propriété spécifique à l'implémentation dont la valeur est une liste séparée par des virgules qui peut être configurée sur la ligne de commande Java à des fins de diagnostic/débogage avec les valeurs suivantes:

-Djdk.httpclient.HttpClient.log=
       errors,requests,headers,
       frames[:control:data:window:all],content,ssl,trace,channel
8
daniel

De notre côté, nous n'avons pas trouvé la journalisation fournie par -Djdk.internal.httpclient.debug assez lisible. La solution que nous avons trouvée consiste à envelopper le HttpClient avec un décorateur qui pourra intercepter les appels et fournir la journalisation. Voici à quoi cela ressemble (devrait être fait non seulement pour les méthodes send mais sendAsync):

public class HttpClientLoggingDecorator extends HttpClient {

  private static final Logger logger = Logger.getLogger(HttpClientLoggingDecorator.class.getName());

  private final HttpClient client;

  ...

  @Override
  public <T> HttpResponse<T> send(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler)
    throws IOException,
      InterruptedException
  {
    subscribeLoggerToRequest(req);

    HttpResponse<T> response = client.send(req, responseBodyHandler);

    logResponse(response);
    return response;
  }

  private void subscribeLoggerToRequest(HttpRequest req) {
    // define a consumer for how you want to log
    // Consumer<String> bodyConsumer = ...;
    if (req.bodyPublisher().isPresent()) {
      req.bodyPublisher()
              .ifPresent(bodyPublisher -> bodyPublisher.subscribe(new HttpBodySubscriber(bodyConsumer)));
    } else {
      bodyConsumer.accept(NO_REQUEST_BODY);
    }
  }

  private <T> void logResponse(HttpResponse<T> response) {
    // String responseLog = ...;
    logger.info(responseLog);
  }

}

Et voici le HttpBodySubscriber:

public class HttpBodySubscriber implements Flow.Subscriber<ByteBuffer> {

  private static final long UNBOUNDED = Long.MAX_VALUE;

  private final Consumer<String> logger;

  public HttpBodySubscriber(Consumer<String> logger) {
    this.logger = logger;
  }

  @Override
  public void onSubscribe(Flow.Subscription subscription) {
    subscription.request(UNBOUNDED);
  }

  @Override
  public void onNext(ByteBuffer item) {
    logger.accept(new String(item.array(), StandardCharsets.UTF_8));
  }

  @Override
  public void onError(Throwable throwable) {
  }

  @Override
  public void onComplete() {
  }

}