简体   繁体   English

播放2.5:在自定义http操作中获取响应正文

[英]Play 2.5: get response body in custom http action

I'm trying to create a custom http action ( https://playframework.com/documentation/2.5.x/JavaActionsComposition ) to log request and response bodies with Play 2.5.0 Java. 我正在尝试创建一个自定义的http操作( https://playframework.com/documentation/2.5.x/JavaActionsComposition ),以使用Play 2.5.0 Java记录请求和响应正文。 This is what I've got so far: 到目前为止,这是我得到的:

public class Log extends play.mvc.Action.Simple {

    public CompletionStage<Result> call(Http.Context ctx) {
        CompletionStage<Result> response = delegate.call(ctx);
        //request body is fine
        System.out.println(ctx.request().body().asText())
        //how to get response body string here while also not sabotaging http response flow of the framework?
        //my guess is it should be somehow possible to access it below?
        response.thenApply( r -> {
            //???
            return null;
        });
        return response;
    }

}

Logging is often considered a cross-cutting feature. 日志记录通常被认为是跨领域的功能。 In such cases the preferred way to do this in Play is to use Filters: 在这种情况下,在Play中执行此操作的首选方法是使用过滤器:

The filter API is intended for cross cutting concerns that are applied indiscriminately to all routes. 过滤器API旨在解决不加区分地应用于所有路径的交叉问题。 For example, here are some common use cases for filters: 例如,以下是一些常见的过滤器用例:

  • Logging/metrics collection 记录/指标收集
  • GZIP encoding GZIP编码
  • Security headers 安全头

This works for me: 这对我有用:

import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import javax.inject.Inject;
import akka.stream.*;
import play.Logger;
import play.mvc.*;

public class LoggingFilter extends Filter {

    Materializer mat;

    @Inject
    public LoggingFilter(Materializer mat) {
        super(mat);
        this.mat = mat;
    }

    @Override
    public CompletionStage<Result> apply(
            Function<Http.RequestHeader, CompletionStage<Result>> nextFilter,
            Http.RequestHeader requestHeader) {
        long startTime = System.currentTimeMillis();
        return nextFilter.apply(requestHeader).thenApply(result -> {
            long endTime = System.currentTimeMillis();
            long requestTime = endTime - startTime;

            Logger.info("{} {} took {}ms and returned {}",
                requestHeader.method(), requestHeader.uri(), requestTime, result.status());
            akka.util.ByteString body = play.core.j.JavaResultExtractor.getBody(result, 10000l, mat);
            Logger.info(body.decodeString("UTF-8"));

            return result.withHeader("Request-Time", "" + requestTime);
        });
    }
}

What is it doing? 到底在做什么

First this creates a new Filter which can be used along with other filters you may have. 首先,这将创建一个新的过滤器,该过滤器可与您可能拥有的其他过滤器一起使用。 In order to get the body of the response we actually use the nextFilter - once we have the response we can then get the body. 为了获取响应的主体,我们实际上使用nextFilter一旦获得响应,我们就可以获取主体。

As of Play 2.5 Akka Streams are the weapon of choice. 从Play 2.5开始, Akka Streams是首选武器。 This means that once you use the JavaResultExtractor , you will get a ByteString , which you then have to decode in order to get the real string underneath. 这意味着,一旦使用JavaResultExtractor ,您将获得一个ByteString ,然后必须对其进行解码才能获取下面的真实字符串。


Please keep in mind that there should be no problem in copying this logic in the Action that you are creating. 请记住,在您创建的Action中复制此逻辑应该没有问题。 I just chose the option with Filter because of the reason stated at the top of my post. 由于帖子顶部所述的原因,我只选择了Filter选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM