![](/img/trans.png)
[英]How to log request/response using java.net.http.HttpClient?
[英]Unreliable response from java.net.http.HttpClient call using Java 11 and Spring Boot
我正在 Spring Boot 中编写一个后端应用程序,该应用程序从第三方调用另一个 API。
我遇到了这个特定调用的问题,它检索了一个包含不记名令牌的令牌 object,然后我在他们的其他端点中使用它。 在调用其他端点时,检索到的令牌有时会起作用,但大多数时候不会,从而导致未经授权的响应。
@RestController
public class CotizacionController {
Logger logger = LoggerFactory.getLogger(CotizacionController.class);
@Value("${service.credentials.tokenServer}")
private String tokenServer;
@Value("${service.credentials.grantType}")
private String grantType;
@Value("${service.credentials.username}")
private String username;
@Value("${service.credentials.password}")
private String password;
HttpClient client = HttpClient.newHttpClient();
@RequestMapping("/create")
public Object Create() throws IOException, InterruptedException {
HashMap<String, String> parameters = new HashMap<>();
parameters.put("grant_type", grantType);
parameters.put("username", username);
parameters.put("password", password);
String form = parameters.keySet().stream()
.map(key -> key + "="
+ URLEncoder.encode(parameters.get(key),
StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(tokenServer))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(BodyPublishers.ofString(form)).build();
HttpResponse<?> response = client.send(request, BodyHandlers.ofString());
TokenResponse result = new ObjectMapper().readValue(response
.body().toString(), TokenResponse.class);
return result;
}
}
这是一个示例令牌 object:
{
"access_token": "z-bu-Pde6M2dlPiaRzd5XpTrT7ohpFQZe157HHVLfdKJWsdmKCloK7AYGEw7SLCe28tjYAxo8MZOE_3W00HEa-bqgUvcrAKfxIubAq0UGXv7jLPWbRwWzhAUCDon3kdstUrJ_OKRN2y26W6qyDBGDqlP5NRSF4unH_pD_ShmpDlSxZdYUqD0da5Y2_uO6YRs5GuWA7XhI9sPa98SxuXN_dwiDJVif418xK646fUgWR8",
"token_type": "bearer",
"expires_in": "3599"
}
使用postman检索令牌可以正常工作,因此第三方 API 不会有问题。 我也在.NET Core 3 中实现了同样的服务,它在那里也可以正常工作。
最让我困惑的是,实际的 HttpClient 调用有效,我确实得到了正确的 Json,它映射到我的 TokenResponse object 就好了。 只是令牌值无效......有时。
我也尝试过使用 RestTemplate 和 WebClient Spring 库,但结果是一样的。 通话有效,但检索到的令牌无效。
起初我以为我遇到了竞争条件,因为最初我在那里有另一个 HttpClient 和另一个端点,使用来自令牌调用的响应。 所以我将其简化为仅令牌调用,并将令牌值手动复制到 postman 请求中。 没用。
然后我想也许我的 HttpClient 授权 header 格式不正确,但这是不正确的,因为使用 postman 请求将令牌复制到受保护的端点就表明令牌不起作用。
以及我尝试过的其他事情:
在这一点上我很迷茫,唯一想到的是,也许 HttpClient.send() 方法可能正在以一种可能会影响内容的方式解析正文? 我对此表示怀疑,但我看不出还会发生什么。
解决方案与 cookie 有关!
令牌服务器响应正在发送 2 个set-cookie
标头,这些标头在 Postman 和 .NET Core 中被自动处理并设置为后续的 HTTP 请求。 第 3 方 API 位于负载均衡器后面并生成这些 session cookies。
我通过在我的main
方法中使用以下代码实现系统范围的CookieHandler解决了这个问题。
public static void main(String[] args) {
CookieHandler.setDefault(new CookieManager());
SpringApplication.run(Main.class, args);
}
然后像这样构建我的 HttpClient object:
...
HttpClient client = HttpClient.newBuilder().cookieHandler(CookieHandler.getDefault()).build();
...
这样,响应set-cookie
和请求cookie
标头将被自动处理,并在此 HttpClient 进行的所有调用中起作用。
默认情况下,CookieHandler 是使用CookiePolicy.ACCEPT_ORIGINAL_SERVER
参数创建的。 我的理解是,这使得 cookies 仅在同一主机设置和请求时才起作用。 查看文档以获取有关CookiePolicy的更多选项
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.