Я пытаюсь зарегистрировать веб-перехватчик Office365 Api для официальные API-документы. Попробовал в почтальоне, все работает как положено.
Версия Java: 1.7 (я знаю ...)
Я работаю с версией фреймворка Play 1.2.7.2.
HttpClient: org.apache.http.client.HttpClient
Соответствующая документация:
The subscription process goes as follows:
A client app makes a subscription request (POST) for a specific resource. It includes a notification URL, among other properties.
The Outlook notifications service tries to validate the notification URL with the listener service. It includes a validation token in the validation request.
If the listener service successfully validates the URL, it returns a success response within 5 seconds as follows:
Sets the content type in the response header to text\plain. Includes the same validation token in the response body. Returns an HTTP 200 response code. The listener can discard the validation token subsequently. Depending on the URL validation result, the Outlook notifications service sends a response to the client app:
If URL validation was successful, the service creates the subscription with a unique subscription ID and sends the response to the client. If URL validation was unsuccessful, the service sends an error response with an error code and other details. Upon receiving a successful response, the client app stores the subscription ID to correlate future notifications with this subscription.
Запрос почтальона:
Оба запроса были перехвачены 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"
}
(захвачено в wirehark) (части ### удалены, я убедился, что аутентификация и идентификаторы совпадают)
На ###/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();
Независимо от того, насколько высоко я установил лимит, я никогда не получаю ответа, офисный api. Даже не ответ с ошибкой.
TL; DR: мой запрос отлично работает в почтальоне, точно такой же запрос в Java истекает (независимо от продолжительности тайм-аута)
@declension прокси не используется
POST /api/v2.0/me/subscriptions HTTP/1.1 против POST /me/subscriptions HTTP/1.1. Есть некоторые другие изменения, которые действительно примечательны, поэтому запросы похожи, но НЕ идентичны. Возможно, одно из различий вызывает проблемы. Вы даже получили запрос, отправленный от вашего клиента, и были ли обработаны той же логикой сервера?
Верно ли, что вы использовали разные версии API от Java и Postman?
@DmitrySurin, извините, нет, это была несвязанная ошибка, связанная с тем, что я пробовал разные способы захвата запросов. Обновлено
Просто совет: если вы установите награду за вопрос, но отметите один ответ как принятый, люди будут считать, что награда предназначена для принятого ответа, и просто прокрутят (то есть проигнорируют) ваш вопрос, таким образом, ваши очки репутации будут потрачены впустую. . В следующий раз дождитесь «льготного периода», прежде чем принимать ответ, чтобы максимально увеличить доступ.




Проблема в том, что org.apache.http.client.HttpClient не является потокобезопасным (по крайней мере, для игровой платформы 1.2.7). Поскольку игровая среда повторно использует потоки, я могу предположить, что она так и не получила ответа (поскольку запрос регистрации веб-перехватчика занимает немного больше времени, чем обычные запросы).
Я переключился на WS, http-клиент, поставляемый самой игровой платформой. Это имеет недостаток, заключающийся в том, что он имеет ограниченную поддержку всех http-глаголов.
Новые методы выглядят так:
private final WS.WSRequest getRequest (String url) {
WS.WSRequest req = WS.url(overwriteBaseUrl(url) + getPath());
for (String key: headers.keySet()) {
req.setHeader(key, headers.get(key));
}
String body = getBody();
if (body == null) {
body = "";
}
req.body(body);
return req;
}
public void send (WSMockableSender client, Office365Credentials cred, String url) throws IOException, InvalidCrmCredentialsException, MalformedResponseException {
WS.WSRequest req = getRequest(url);
addAuthHeaderToRequest(cred, req);
// execute the request
WSResponse response;
response = new WSResponse(client.post(req));
System.out.println("Response received!");
// check for 401, not authorized
if (response.getStatus() == 401) {
throw new InvalidCrmCredentialsException("401 - Not authorized! " + req.url);
}
processResponse(response);
}
Вы используете прокси-сервер?