How to do custom action composition to log request

2019-04-14 10:14发布

I'm working on Play 2.3 (Java) application and I need a custom Action Composition to log request and response. With what I've got so far I am able to get the body of request, but not response:

import play.libs.F;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;

public class LogAction extends Action.Simple {

    public F.Promise<Result> call(Http.Context ctx) throws Throwable {
        //Request body
        String requestBody = ctx.request().body().asText();
        //Need to get response body here
        //String responseBody = ???
        return delegate.call(ctx);
    }

}

How to I get the response body in this scenario? If it's difficult to do in java, it may as well be in scala, however it has to work with a java controller method @With annotation.

2条回答
冷血范
2楼-- · 2019-04-14 10:39
    @Override
    public F.Promise<Result> call(Http.Context ctx) throws Throwable {
        Promise<Result> call = delegate.call(ctx);
        return call.map((r) -> {
            byte[] body = JavaResultExtractor.getBody(r, 0l);
            Logger.info(new String(body));
            return r;
        });
    }

You can use play.core.j.JavaResultExtractor to extract the body from the response. Keep in mind, getBody(..) blocks until response is ready, thus consider calling onRedeem instead of map.

查看更多
时光不老,我们不散
3楼-- · 2019-04-14 10:40

Have you tried something like this:

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

    public F.Promise<Result> call(Http.Context ctx) throws Throwable {
        Logger.info("Calling action for " + ctx);
        F.Promise<Result> resultPromise = delegate.call(ctx);
        resultPromise.map(result -> {
            Logger.info(result.toScala().header().status());
            Logger.info(result.toScala().body().toString());
            return result;
        });
        return resultPromise;
    }
}

The body will be returned as a play.api.libs.iteratee.Enumerator. Now the hard part is to work with this. First you need to understand the concept of Iteratee and what role the Enumerator plays in it. Hint: think of the Enumerator as a producer of data and think of the Iteratee as the consumer of this data.

Now on this Enumerator you can run an Iteratee that will transform the data chunks into the type you want.

The bad news is that you need to implement the play.api.libs.iteratee.Iteratee trait. As you can see it resides in the api subpackage, that means it is part of the Scala world in Play. Maybe in this case it woul be much easier to use Scala for this part of your task. Unfortunately I cannot provide you with some example implementation but I hope this would be not that hard. I think this is something really missing on the Java side of Play.

查看更多
登录 后发表回答