我有一个示例,我向客户端发出请求以调试对FB api的令牌请求,然后将结果返回给客户端。
根据访问令牌是否有效,应返回适当的标头:
@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));
}
但是,我得到的结果是:
这不是我真正期望的。我希望收到的是错误的HTTP状态代码401,以及代码中定义的json字符串。
不知道为什么我在响应正文中需要标题信息。
当我想返回HeaderServiceCall时,还会发生一个奇怪的错误:
我不确定这是否是错误,也不清楚ServerServiceCall
和HeaderServiceCall
之间的区别。
有人可以帮忙吗?
答案 0 :(得分:1)
HeaderServiceCall
的类型是这样定义的:
interface HeaderServiceCall<Request,Response>
和
CompletionStage<Pair<ResponseHeader,Response>> invokeWithHeaders(RequestHeader requestHeader,
Request request)
这意味着在定义响应类型时,返回值应该是具有响应类型的CompletionStage
的{{1}}中的Pair
。
在您的代码中,响应类型应该为ResponseHeader
,但是您已将其定义为String
,这意味着它期望嵌套返回值:Pair<ResponseHeader, String>
。请注意额外的嵌套CompletionStage<Pair<ResponseHeader,Pair<ResponseHeader, String>>>
。
当与Pair<ResponseHeader, String>
一起使用时,需要实现HeaderServiceCall
,会出现编译错误,指示类型不匹配。这是您上面的屏幕截图中的错误。
当您实施invokeWithHeaders
时,您的方法被推断为实施ServiceCall.invoke
,其定义为:
ServerServiceCall
换句话说,该方法的返回类型不需要附加的CompletionStage<Response> invoke()
,因此您的实现可以编译,但是会产生错误的结果。包含Pair<ResponseHeader, Response>
的货币对会自动序列化为JSON,并以这种方式返回给客户端。
更正代码需要更改方法签名:
ResponseHeader
您还需要更改@Override
public HeaderServiceCall<LoginUser, String> login() {
return this::loginUser;
}
方法以接受loginUser
参数,即使该参数未使用,也要与RequestHeader
的签名匹配:
invokeWithHeaders
这应该可以解决您的问题,但是对于Lagom服务而言,更直接地使用域类型并依赖于内置的JSON序列化支持,而不是直接在服务实现中进行序列化,这将更为典型。您还需要注意public CompletionStage<Pair<ResponseHeader, String>> loginUser(RequestHeader requestHeader, LoginUser user)
值。在任何情况下,您都不应返回null
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;
}
}
是一个阻塞方法(直到调用完成才返回)。在Lagom服务呼叫中应避免阻塞,因为它可能导致性能问题和不稳定。如果这是您控制的代码,则应将其编写为使用非阻塞样式(返回fbClient.verifyFacebookToken
)。如果不是,则应使用CompletableFuture.supplyAsync
将调用包装在CompletionStage
中,然后在另一个线程池中执行。
我在GitHub上发现了这个示例,您也许可以对其进行调整:https://github.com/dmbuchta/empty-play-authentication/blob/0a01fd1bd2d8ef777c6afe5ba313eccc9eb8b878/app/services/login/impl/FacebookLoginService.java#L59-L74