使用HttpClient调用霍尼韦尔REST API时遇401未授权问题求助
霍尼韦尔温控器API调用401未授权问题排查
我正在开发一个控制台应用,用于与霍尼韦尔温控器通信。霍尼韦尔提供免费的REST API,我完成注册应用、获取access token的步骤后,调用GET接口时返回401未授权异常,但将相同的access token复制到curl命令中却能成功请求,希望排查问题原因。
操作流程
- 注册应用获取AppID和Secret(步骤正常)
- 通过OAuth2认证获取access token(步骤正常)
- 使用access token调用REST API(此步骤报错)
详细信息
项目配置
控制台项目的csproj内容:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>true</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>
1. 应用注册(正常完成)
已从Resideo获取以下信息:
- AppId:
ABCD1234 - Secret:
WXYZ9876
2. 获取Access Token(正常完成)
用于反序列化token响应的类:
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"; using HttpClient client = new() { BaseAddress = new Uri(uriString: "https://api.honeywell.com/", uriKind: UriKind.Absolute) }; KeyValuePair<string, string>[] encodedContentCollection = [ new("grant_type", "client_credentials") ]; using 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); using HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); // 未抛出异常,认证成功 using Stream responseContentStream = await response.Content.ReadAsStreamAsync(); ResideoToken token = await JsonSerializer.DeserializeAsync<ResideoToken>(responseContentStream, JsonSerializerOptions.Default) ?? throw new Exception("无法将响应流反序列化为ResideoToken");
注:原代码中把Content-Type放入FormUrlEncodedContent是错误操作,FormUrlEncodedContent会自动添加该请求头,此处已修正
3. API调用(返回401未授权)
调用获取位置和设备列表的GET接口,代码如下:
// 原错误写法:client.DefaultRequestHeaders.Add("Bearer", token.AccessToken); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // 已通过HttpClient设置BaseAddress using HttpResponseMessage locationResponse = await client.GetAsync($"v2/locations?apikey={appId}"); locationResponse.EnsureSuccessStatusCode(); // 此处抛出401异常 // 从未执行到以下代码 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执行相同请求却成功:
$ curl -X GET --header "Authorization: Bearer AbCdEfGhIjKlMnOpQrStUvWxYz==" "https://api.honeywell.com/v2/locations?apikey=ABCD1234" # 返回完整的位置和设备JSON数据
疑问
- 是否正确复用了获取到的access token字符串?
- 是否正确复用HttpClient?同一认证的请求是否应该复用同一个实例?
- Bearer头应该设置为HttpClient默认头,还是需要手动创建HttpRequestMessage单独设置?如果是后者该如何操作?
- Accept媒体类型设置为
application/json是否正确? - 认证时设置的HttpClient默认头会影响后续请求吗?是否需要清除?
- GetAsync传入的URL(未包含基础URL)是否正确?
- 直接在URL中拼接
apikey参数是否正确?正确的做法是什么? - 既然curl请求正常,有哪些调试401响应的有效建议?
补充说明:已修正Bearer token的设置代码,但仍返回401异常,同时已补全所有缺失的using语句。
内容的提问来源于stack exchange,提问作者Carlos Sánchéz López
相关产品推荐
相关产品推荐

