You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 15:30:23