4

我正在使用PlayFramework的 2.0.2 版本,并且正在尝试创建一个游戏play.mvc.Action来实现http 结果的后过滤器。

play docs中解释了使用actors进行预处理的示例。

但是我想要实现的有点不同。我需要:

  • 拿一个play.mvc.Result
  • 提取身体
  • 对身体应用变换
  • 然后创建一个新的结果。

Result接口不公开 http 正文,即使假设您有特定的子类(例如SimpleResult,或AsyncResult),我也不知道如何提取消息正文。

我的具体用例是对结果正文进行 GZip 过滤并添加正确的“内容编码”标头。我希望能够GZip通过添加类似于文档中实现身份验证的方式的注释来将此过滤器应用于任何现有控制器。

下面是我正在尝试做的一个例子

public class Compress {
  @With(GZipResult.class)
  public @interface GZip {
  }

  public static class GZipResult extends Action<GZip> {
    @Override
    public Result call(Http.Context ctx) throws Throwable {
      Result result = delegate.call(ctx);
      if (requestSupportsGZip(ctx) {
         result = extractAndGZipResult(result); //how to extract http body?
      }
      return result;
    }
  }
}

可以用作

@Compress.GZip
class MyController extends Controller {
   public static Result index() {
      return ok(someHtml);
   }
}
4

2 回答 2

4

我在修改 http 结果时遇到了类似的情况。我想分享我在 Java 中处理这个问题的方法。

通过查看Play 2.0.3的Scala源代码,我发现可以在Javaplay.core.j.JavaResultExtractor中检索响应体、cookies、headers和类的状态,并且可以编写响应体的内容。Resultplay.core.j.JavaResults

尽管如此,我仍然不知道如何以简单的方式用修改后的主体替换当前的响应主体。也许用 Scala 实现处理这个问题会更容易,但我仍然发现 Scala 太难读了:-(

我找到的方式是看play.mvc.Results.Status是如何实现的,它使用play.core.j.JavaResults来编写内容主体。我提到的类是Java中类中生成的ok()、、、和其他类似方法的核心。此类的实现如下所示:(重构 Scala 类以提高可读性)notFound()forbidden()ResultController

// scala classes
import play.api.mvc.Codec;
import play.api.mvc.Content;
import play.core.j.JavaResults;
// ...    

    public static class Status implements Result {
        final private play.api.mvc.Result wrappedResult;
        // ... 

        // there are a lot of constructors for this class to reference from
        // this particular constructor is the general approach for most cases
        public Status(play.api.mvc.Results.Status status, Content content, Codec codec) {
            // ...
            wrappedResult = status.apply(
                content,
                JavaResults.writeContent(codec),
                JavaResults.contentTypeOf(content.contentType() + "; charset=" + codec.charset())
            );
        }

}

如果要实现上面的代码,还需要实现play.api.mvc.Content接口。总而言之,按照我所说的实现你的代码看起来像这样:

import play.api.mvc.Codec;
import play.api.mvc.Content;
import play.core.j.JavaResultExtractor;
import play.core.j.JavaResults;
// ...

public static class GZipResult extends Action<GZip> {

    @Override
    public Result call(Http.Context ctx) throws Throwable {
        Result result = delegate.call(ctx);
        if (requestSupportsGZip(ctx)) {
            // copy parts of current response
            final int statusCode = JavaResultExtractor.getStatus(result);
            final Map<String,String> headers = JavaResultExtractor.getHeaders(result);
            final byte[] body = JavaResultExtractor.getBody(result);

            // create a gzip result
            result = new GZipResult(statusCode, new String(body), headers.get("Content-Type"));

            // add appropriate headers here
            // ...
        }

        return result;
    }
}

private static class GZipResult implements Result {
    final private play.api.mvc.Result wrappedResult;

    public GZipResult(final int StatusCode, final String content, final String contentType) {
        if(content == null) throw new NullPointerException("null content");

        // i guess this is the good place in transforming the content body
        String gzippedContent = someMethodToGzipContent(content);

        this.wrappedResult = JavaResults.Status(statusCode).apply(
                // implement the play.api.mvc.Content interface
                new Content() {
                    @Override public String body() { return gzippedContent; }
                    @Override public String contentType() { return contentType; }
                },
                JavaResults.writeContent(Codec.utf_8),
                JavaResults.contentTypeOf(contentType))
            );
    }

    // ...
}

通过上面的实现,我可以修改 http 结果。我希望这可以为您的特定用例提供帮助。

干杯!

更新

上面的代码可以解决大多数修改 http 结果的用例。您的特殊情况,即 gzipping 响应,需要另一种方法,因为 gzipped 正文byte[]不在String. 该类play.mvc.Results.Status还提供处理方法byte[]。实现如下所示:

// scala classes
import play.core.j.JavaResults;
// ...    

    public static class Status implements Result {
        final private play.api.mvc.Result wrappedResult;
        // ... 

        public Status(play.api.mvc.Results.Status status, byte[] content) {
            // ...
            wrappedResult = status.apply(
                content,
                JavaResults.writeBytes(),
                JavaResults.contentTypeOfBytes()
            );
        }

}

您可以在我更新之前根据我的实现找出如何对您的代码执行此操作。请注意,您不再需要实现play.api.mvc.Content并确保在响应中返回适当的内容类型。

快乐编码:-)

于 2012-09-17T03:09:01.517 回答
3

AFAIK,经过一番挖掘,您可以使用result.getWrappedResult(); 它返回一个play.api.mvc.Result可以(通常)转换为play.api.mvc.SimpleResult包含body()方法的a:

play.api.mvc.SimpleResult wrappedResult = (play.api.mvc.SimpleResult) result.getWrappedResult();
Enumerator body = wrappedResult.body();

然后,我建议您阅读Enumerator 文档;目前,我还没有弄清楚这些东西是如何工作的:-)。

希望这对您有所帮助...

于 2012-08-31T09:37:48.020 回答