5

我有一个模型,我想用来自 Web 服务的详细信息来填充它。我想异步执行此操作,以免阻塞服务器线程。让我们假设它是一个登录服务。

现在我想做的是向远程服务器发出请求并最终返回一个用户模型对象。所以方法签名看起来像这样:

public static User loginUser(String username, String password) {

我知道要对 Web 服务进行异步调用,我应该使用Promise

Promise<WS.Response> wsPromise = WS.url("http://myserver.com/login")
            .setContentType("application/json; charset=utf-8")
            .post("... the username and password ...");

哪个还没有开始请求。我可以调用get()这个对象来对服务进行阻塞调用。这行得通。

要异步执行此操作,我认为我需要映射它并以某种方式执行它。

Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
    @Override
    public User apply(WS.Response response) throws Throwable {
        System.out.println(response.getBody());
        return new User(... based on something extracted from the returned JSON ...);
    }
});

现在如何触发此操作?如果我调用get()resultPromise它会进行调用,但最终会触发超时异常。我不能使用该async(...)方法,因为它只返回我一个Result.

查看其他示例(https://github.com/jroper/play-promise-presentation/blob/master/src/main/java/controllers/Application.java),这似乎是模式。即我们总是希望返回一个 Result 对象。但是,我无法查询Result对象,也没有计划将该特定对象发送给用户。

事实上,这些示例似乎调用了一个 Web 服务,将 JSON 结果映射到一个对象,然后立即将它们映射回相同的 JSON。当我想将用户(在我的情况下)传递回调用函数时没有多大用处。

老实说,无论如何,我对这种异步性质有点困惑(你可能已经猜到了)。特别是,这实际上是一个阻塞操作,因为我们必须等待 Web 服务返回响应。该文档似乎表明使用Promise/Future模式将避免这种阻塞。

底线是:如何在不阻塞 Play Framework 服务器中的线程的情况下将 Web 服务调用的结果映射回模型对象?

随意滥用我对 Play Framework 的缺乏经验......

4

2 回答 2

2

这个答案可能来得太晚了,但会发布它,以防现在其他人仍然想知道同样的问题并想要回答这个问题。

有两种方法可以实现这一点,即使两者都使用相同的JsonNode类:

  1. 第一个是您所期望的:“new User(..something...)”

在这种情况下,您可以使用 JsonNode 的“get”方法来获取创建用户对象所需的特定数据(在我的示例中为用户名和电子邮件,因为您没有指定必填字段)。

Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
    @Override
    public User apply(WS.Response response) throws Throwable {
        System.out.println(response.getBody());
        JsonNode json = response.asJson();
        return new User(json.get("username"), json.get("email"));
    }
});
  1. 另一种选择是在您知道 Web 服务确实返回用户对象的有效 Json 表示的情况下。

我个人认为这个选项更容易,并且依赖于 Json类中的 fromJson 方法。

Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
    @Override
    public User apply(WS.Response response) throws Throwable {
        System.out.println(response.getBody());
        return Json.fromJson(response.asJson(), User.class);
    }
});

我希望这个答案可以帮助人们想知道如何以一种简单的方式做到这一点。

于 2016-10-12T19:39:37.020 回答
1

.get()或者.post(...)在 WS 调用上确实触发 WS 调用出去。(不要混淆WS.get()......myPromise.get()它们完全不同)。

保持这种设计完全异步的诀窍是让 Promise 一直向下,从控制器响应一直向下。

为此,您必须明智地使用 Promise API 中的map()flatMat()sequence()方法。

在您的特定情况下,您在执行 WS 调用返回时发生的代码中使用WS 调用的结果。map()那就是您使用响应来实际做事的地方。

这种范式在 Scala 中使用“for comprehensions”更容易,但在 Java 中仍然可以使用以flatMap调用结束的嵌套调用链map

于 2014-03-31T16:36:42.860 回答