Подводя итог, мой процесс состоит из трех шагов:
- Зарегистрируйте приложение. чтобы получить AppID и секрет (здесь все хорошо).
- Пройдите аутентификацию с помощью OAuth2, чтобы получить токен доступа (здесь все хорошо).
- Вызовите любой REST API, использующий предоставленный токен доступа (проблема здесь).
Моя консоль csproj очень проста:
Код: Выделить всё
Exe
net8.0
true
enable
1. Зарегистрируйте приложение, чтобы получить AppID и секрет.
Приложение регистрируется здесь, это довольно стандартно: https://developer.honeywellhome.com/user/me/ appsВ этом примере предположим, что Resideo предоставляет мне следующие значения, которые я буду использовать позже:
- AppId: ABCD1234
- Секрет: WXYZ9876
2. Пройдите аутентификацию, чтобы получить токен доступа.
Структура ответа json токена доступа описана здесь: https://developer.honeywellhome.com/aut ... uth2/apis/ post/accesstokenВот как я определяю класс для десериализации json:
Код: Выделить всё
internal class ResideoToken
{
[JsonPropertyName("refresh_token_expires_in")]
public string RefreshTokenExpiration { get; set; } = string.Empty;
[JsonPropertyName("api_product_list")]
public string ApiProductList { get; set; } = string.Empty;
[JsonPropertyName("organization_name")]
public string OrganizationName { get; set; } = string.Empty;
[JsonPropertyName("developer.email")]
public string DeveloperEmail { get; set; } = string.Empty;
[JsonPropertyName("token_type")]
public string TokenType { get; set; } = string.Empty;
[JsonPropertyName("issued_at")]
public string IssuedAt { get; set; } = string.Empty;
[JsonPropertyName("client_id")]
public string ClientId { get; set; } = string.Empty;
[JsonPropertyName("access_token")]
public string AccessToken { get; set; } = string.Empty;
[JsonPropertyName("application_name")]
public string ApplicationName { get; set; } = string.Empty;
[JsonPropertyName("scope")]
public string Scope { get; set; } = string.Empty;
[JsonPropertyName("expires_in")]
public string ExpiresIn { get; set; } = string.Empty;
[JsonPropertyName("refresh_count")]
public string RefreshCount { get; set; } = string.Empty;
[JsonPropertyName("status")]
public string Status { get; set; } = string.Empty;
}
Код: Выделить всё
string appId = "ABCD1234";
string secret = "WXYZ9876";
HttpClient client = new()
{
BaseAddress = new Uri(uriString: "https://api.honeywell.com/", uriKind: UriKind.Absolute)
};
KeyValuePair[] encodedContentCollection =
[
new("Content-Type", "application/x-www-form-urlencoded"),
new("grant_type", "client_credentials")
];
HttpRequestMessage request = new(HttpMethod.Post, "oauth2/accesstoken")
{
Content = new FormUrlEncodedContent(encodedContentCollection)
};
string base64AppIdAndSecret = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{appId}:{secret}"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64AppIdAndSecret);
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode(); // Should throw if not 200-299
Stream responseContentStream = await response.Content.ReadAsStreamAsync();
ResideoToken token = await JsonSerializer.DeserializeAsync(responseContentStream, JsonSerializerOptions.Default) ??
throw new Exception("Could not deserialize response stream to a ResideoToken");
3. Вызовите любой REST API, используя предоставленный токен доступа.
Самый простой случай, который я нашел, — это получить список местоположений и устройств с помощью метода GET и передачи одного параметра: https:/ /developer.honeywellhome.com/lyric/apis/get/locationsКод: Выделить всё
// I had this originally, but it was incorrect -> client.DefaultRequestHeaders.Add("Bearer", token.AccessToken);
client.DefaultRequestHeaders.Authentication = new AuthenticationHeaderValue("Bearer", token.AccessToken);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Base URL has already been established in the client
// According to the instructions, the apikey is the AppID
HttpResponseMessage locationResponse = await client.GetAsync($"v2/locations?apikey={appId}");
locationResponse.EnsureSuccessStatusCode(); // This is failing with 401 unauthorized
// I am never able to reach this
string result = await locationResponse.Content.ReadAsStringAsync();
Console.WriteLine($"Locations: {result}");
Код: Выделить всё
Unhandled exception. System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
Код: Выделить всё
$ curl -X GET --header "Authorization: Bearer AbCdEfGhIjKlMnOpQrStUvWxYz==" "https://api.honeywell.com/v2/locations?apikey=ABCD1234"
# This prints a huge valid json file describing all the details of the locations and my devices as described in https://developer.honeywellhome.com/lyric/apis/get/locations
На третьем этапе возникают все проблемы, поэтому я не уверен в следующем:
[*]Правильно ли я повторно использую строку токена доступа, полученную в результате успешной аутентификации?
[*]Правильно ли я повторно использую HttpClient? Насколько я понимаю, рекомендуется продолжать использовать один и тот же экземпляр для одной и той же группы запросов, идентифицированных с одной и той же аутентификацией.
[*]Я устанавливаю заголовок Bearer в правильном месте в качестве заголовка по умолчанию или мне следует вручную создать запрос и установить там заголовок? Если второе, то как мне это сделать?
[*]Устанавливаю ли я для параметра Accept media type заголовка запроса по умолчанию допустимое значение «application/json»? Если нет, то где мне его нужно поместить?
[*]Вызывают ли параметры по умолчанию, изначально установленные в HttpClient при аутентификации, какие-либо проблемы с последующими вызовами запросов? Другими словами, нужно ли мне, например, очищать заголовки по умолчанию?
[*]Передаю ли я правильный URL-адрес вызову GetAsync? Он не содержит базовый URL-адрес.
[*]Правильно ли установить параметр GET (apikey=1234ABCD) непосредственно в строке URL-адреса GetAsync? Если нет, то как правильно?
[*]Есть ли предложения по отладке ответа 401, учитывая, что он работал при использовании Curl?
< /ul>
Заранее спасибо.
Изменить: я исправил строку, которая устанавливает токен носителя, но я все еще получаю то же самое исключение 401.
Подробнее здесь: https://stackoverflow.com/questions/785 ... httpclient
Мобильная версия