【发布时间】:2018-11-07 16:45:18
【问题描述】:
我正在尝试根据official api docs 注册一个 Office365 Api webhook。我在邮递员中尝试过,一切正常。
Java 版本:1.7(我知道...)
我正在使用 Play 框架版本 1.2.7.2
HttpClient:org.apache.http.client.HttpClient
相关文档:
订阅过程如下:
客户端应用程序为特定资源发出订阅请求 (POST)。它包括一个通知 URL 以及其他属性。
Outlook 通知服务尝试使用侦听器服务验证通知 URL。它在验证请求中包含一个验证令牌。
如果监听服务成功验证 URL,它会在 5 秒内返回成功响应,如下所示:
将响应标头中的内容类型设置为text\plain。 在响应正文中包含相同的验证令牌。 返回 HTTP 200 响应代码。侦听器随后可以丢弃验证令牌。 根据 URL 验证结果,Outlook 通知服务会向客户端应用发送响应:
如果 URL 验证成功,服务会创建具有唯一订阅 ID 的订阅并将响应发送给客户端。 如果 URL 验证不成功,服务会发送一个错误响应,其中包含错误代码和其他详细信息。 收到成功响应后,客户端应用会存储订阅 ID,以便将未来的通知与此订阅相关联。
邮递员请求:
两个请求都被wireshark截获:
邮递员:
Ý`!Ë2@ʸ1cÊþßV:
ðîPOST /api/v2.0/me/subscriptions HTTP/1.1
Content-Type: application/json
cache-control: no-cache
Postman-Token: a24df796-c49e-4245-a1cf-0949cd6538b6
Authorization: Bearer ###
User-Agent: PostmanRuntime/7.4.0
Accept: */*
Host: localhost:3000
accept-encoding: gzip, deflate
content-length: 430
Connection: keep-alive
{
"@odata.type":"#Microsoft.OutlookServices.PushSubscription",
"ChangeType": "Created, Deleted, Updated",
"ClientState": "some-token",
"NotificationURL": "https://###/office365/receive.php?subId=5",
"Resource": "https://outlook.office.com/api/v2.0/me/Calendars('###')/events"
}
(在 wireshark 中捕获)(### 部分已被删除信息,我确保 auth 和 ids 匹配)
###/office365/receive.php 有一个 php 脚本,它只是回显$_GET['validationtoken']。这在 Postman 中完美运行。
在 Java 中,我创建了一个具有相同标头和相同正文的请求
E/@@5â$¸³zêð-gÄV$
Là¼Là¼POST /api/v2.0/me/subscriptions HTTP/1.1
Accept: */*
Content-Type: application/json
Authorization: Bearer ###
Content-Length: 476
Host: localhost:3000
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.2 (Java/1.7.0_80)
Accept-Encoding: gzip,deflate
{
"@odata.type":"#Microsoft.OutlookServices.PushSubscription",
"ChangeType": "Created, Deleted, Updated",
"ClientState": "1fdb3e372212622e0b7e68abe09e1201a81a8bcc040cce9a6f013f71a09bfbe1",
"NotificationURL": "https://###/office365/receive.php",
"Resource": "https://outlook.office.com/api/v2.0/me/Calendars('###')/events"
}
我验证了请求的所有(重要)部分都完全相同。
我的 java 代码有点复杂,在这里完全分享,但重要的部分是:
// build url
private final RequestBuilder getRequest (String url) {
RequestBuilder req = RequestBuilder.create(getMethod()); // e.g. POST
req.setUri(overwriteBaseUrl(url) + getPath());
// headers is a hashmap<String, String>
for (String key: headers.keySet()) {
req.setHeader(key, headers.get(key));
}
// set body (handle empty cases)
String body = getBody();
if (body == null) {
body = "";
}
req.setEntity(new StringEntity(body, "UTF-8"));
return req;
}
public void send (HttpClient client, Office365Credentials cred, String url) throws IOException, InvalidCrmCredentialsException, MalformedResponseException {
// creates request (with headers, body, etc)
RequestBuilder req = getRequest(url);
// adds auth
addAuthHeaderToRequest(cred, req);
// execute the request
Response response;
try {
response = new Response(client.execute(req.build()));
} catch (ConnectionPoolTimeoutException e) {
// The 10 second limit has passed
throw new MalformedResponseException("Response missing (response timed out)!", e);
}
// check for 401, not authorized
if (response.getStatus() == 401) {
throw new InvalidCrmCredentialsException("401 - Not authorized! " + req.getUri().toString());
}
// process response
processResponse(response);
}
HttpClient 是这样构造的:
int CONNECTION_TIMEOUT_MS = 10 * 1000; // 10 second timeout
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(CONNECTION_TIMEOUT_MS)
.setConnectTimeout(CONNECTION_TIMEOUT_MS)
.setSocketTimeout(CONNECTION_TIMEOUT_MS)
.build();
client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
无论我设置多高的限制,我都没有得到响应,office api。甚至没有错误响应。
TL;DR:我的请求在邮递员中运行良好,Java 中完全相同的请求超时(无论超时长度如何)
【问题讨论】:
-
你使用代理服务器吗?
-
@declension 没有使用代理
-
POST /api/v2.0/me/subscriptions HTTP/1.1与POST /me/subscriptions HTTP/1.1。实际上还有一些值得注意的进一步变化,因此请求相似但不完全相同。也许差异之一是导致问题。你有没有收到你的客户端发来的请求,并被相同的服务器逻辑处理? -
您使用来自 Java 和 Postman 的不同版本的 API 是否正确?
-
@DmitrySurin 抱歉,不,当我尝试不同的方法来捕获请求时,这是一个无关的错误。更新