Lagom http 状态代码 / header 返回为 json

Lagom http status code / header returned as json

我有一个示例,我向 FB api 发出客户端请求以调试令牌请求,然后 return 将结果发送给客户端。
根据访问令牌是否有效,应 return 编辑适当的 header:

@Override
public ServerServiceCall<LoginUser, Pair<ResponseHeader, String>> login() {
    return this::loginUser;
}

public CompletionStage<Pair<ResponseHeader, String>> loginUser(LoginUser user) {
    ObjectMapper jsonMapper = new ObjectMapper();
    String responseString = null;
    DebugTokenResponse.DebugTokenResponseData response = null;
    ResponseHeader responseHeader = null;

    try {
        response = fbClient.verifyFacebookToken(user.getFbAccessToken(), config.underlying().getString("facebook.app_token"));
        responseString = jsonMapper.writeValueAsString(response);

    } catch (ExecutionException | InterruptedException | JsonProcessingException e) {
        LOG.error(e.getMessage());

    }

    if (response != null) {
        if (!response.isValid()) {
            responseHeader = ResponseHeader.NO_CONTENT.withStatus(401);
        } else {
            responseHeader = ResponseHeader.OK.withStatus(200);
        }
    }

    return completedFuture(Pair.create(responseHeader, responseString));
}

然而,我得到的结果是:

这不是我所期望的。我期望收到的是 401 的错误 http 状态代码,以及代码中定义的 json 字符串。
不确定为什么我需要 header 响应中的信息 body。

当我想 return 一个 HeaderServiceCall:

时也会出现一个奇怪的错误

我不确定这是否是一个错误,我也不清楚 ServerServiceCallHeaderServiceCall 之间的区别。

有人可以帮忙吗?

HeaderServiceCall 的类型定义如下:

interface HeaderServiceCall<Request,Response>

CompletionStage<Pair<ResponseHeader,Response>> invokeWithHeaders(RequestHeader requestHeader,
                                                                 Request request)

这意味着当你定义一个响应类型时,return 值应该是 CompletionStageResponseHeaderPair 与响应类型.

在您的代码中,响应类型应为 String,但您已将其定义为 Pair<ResponseHeader, String>,这意味着它期望嵌套 return 值:CompletionStage<Pair<ResponseHeader,Pair<ResponseHeader, String>>>.注意额外的嵌套 Pair<ResponseHeader, String>.

HeaderServiceCall 一起使用时,需要您实现 invokeWithHeaders,您会收到编译错误,表明类型不匹配。这是您上面的屏幕截图中的错误。

当您改为实施 ServerServiceCall 时,您的方法将被推断为实施 ServiceCall.invoke,其定义为:

CompletionStage<Response> invoke()

换句话说,return 类型的方法不需要额外的 Pair<ResponseHeader, Response>,因此您的实现可以编译,但会产生不正确的结果。包含 ResponseHeader 的对会自动序列化为 JSON 并 return 以这种方式发送给客户端。

更正代码需要更改方法签名:

@Override
public HeaderServiceCall<LoginUser, String> login() {
    return this::loginUser;
}

您还需要更改 loginUser 方法以接受 RequestHeader 参数,即使它没有被使用,以便它与 invokeWithHeaders 的签名相匹配:

public CompletionStage<Pair<ResponseHeader, String>> loginUser(RequestHeader requestHeader, LoginUser user)

这应该可以解决您的问题,但是 Lagom 服务更典型的做法是直接使用域类型并依赖内置的 JSON 序列化支持,而不是直接在您的服务实现中进行序列化。您还需要注意 null 值。在任何情况下你都不应该 return null ResponseHeader

@Override
public ServerServiceCall<LoginUser, Pair<ResponseHeader, DebugTokenResponse.DebugTokenResponseData>> login() {
    return this::loginUser;
}

public CompletionStage<Pair<ResponseHeader, DebugTokenResponse.DebugTokenResponseData>> loginUser(RequestHeader requestHeader, LoginUser user) {
    try {
        DebugTokenResponse.DebugTokenResponseData response = fbClient.verifyFacebookToken(user.getFbAccessToken(), config.underlying().getString("facebook.app_token"));
        ResponseHeader responseHeader;

        if (!response.isValid()) {
            responseHeader = ResponseHeader.NO_CONTENT.withStatus(401);
        } else {
            responseHeader = ResponseHeader.OK.withStatus(200);
        }
        return completedFuture(Pair.create(responseHeader, response));
    } catch (ExecutionException | InterruptedException | JsonProcessingException e) {
        LOG.error(e.getMessage());
        throw e;    
    }
}

最后,fbClient.verifyFacebookToken 似乎是一种阻塞方法(在调用完成之前它不会 return)。在 Lagom 服务调用中应避免阻塞,因为它有可能导致性能问题和不稳定。如果这是您控制的代码,则应将其编写为使用非阻塞样式(即 return 为 CompletionStage)。如果没有,你应该使用 CompletableFuture.supplyAsync 将调用包装在 CompletionStage 中,并在另一个线程池中执行它。

我在 GitHub 上找到了这个例子,您也许可以改编:https://github.com/dmbuchta/empty-play-authentication/blob/0a01fd1bd2d8ef777c6afe5ba313eccc9eb8b878/app/services/login/impl/FacebookLoginService.java#L59-L74