考虑下面的代码,其中BaseAddress定义了部分URI路径。

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api");
    var response = await client.GetAsync("/resource/7");
}

我期望它执行到http://something.com/api/resource/7的GET请求。但事实并非如此。

经过一些搜索,我找到了这个问题和答案:HttpClient with BaseAddress。建议将/放在BaseAddress的末尾。

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api/");
    var response = await client.GetAsync("/resource/7");
}

还是不行。下面是文档:HttpClient。这里发生了什么?


当前回答

引用解析由RFC 3986统一资源标识符(URI):通用语法描述。而这正是它应有的运作方式。为了保留基本URI路径,您需要在基本URI的末尾添加斜杠,并在相对URI的开头删除斜杠。

如果基URI包含非空路径,合并过程将丢弃其最后一部分(在last /之后)。相关部门:

5.2.3. 合并路径

上面的伪代码引用了用于合并的“merge”例程 使用基本URI路径的相对路径引用。这是 完成如下工作:

如果基本URI具有已定义的权限组件和空 路径,然后返回由"/"连接到 参考的路径;否则

返回由引用的路径组件组成的字符串 附加到除了基URI路径的最后一段之外的所有部分(即, 排除基URI中最右边的“/”后面的任何字符 路径,或排除整个基本URI路径,如果它不包含 任何“/”字符)。

如果相对URI以斜杠开头,则称为绝对路径相对URI。在这种情况下,合并过程忽略所有基本URI路径。更多信息请查看5.2.2。“转换引用”部分。

其他回答

我也遇到了同样的问题与BaseAddress。我决定不使用BaseAddress,最简单的解决方案将是一个简单的一行添加:

Uri GetUri(string path) => new Uri("http://something.com/api" + path);

然后你的代码会变成:

Uri GetUri(string path) => new Uri("http://something.com/api" + path);
using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    // Remove BaseAddress completely
    // client.BaseAddress = new Uri("http://something.com/api");
    var response = await client.GetAsync(GetUri("/resource/7"));
}

我没有调查过使用BaseAddress的利弊,但对我来说,这是完美无缺的。希望这能帮助到一些人。

如果你正在使用httpClient.SendAsync(),就不会像Get或其他特定于动词的方法那样重载相对uri的字符串。

但是你可以通过给UriKind创建相对Uri。相对作为第二个参数

var httpRequestMessage = new HttpRequestMessage
{
    Method = httpMethod,
    RequestUri = new Uri(relativeRequestUri, UriKind.Relative),
    Content = content
};

using var httpClient = HttpClientFactory.CreateClient("XClient");
var response = await httpClient.SendAsync(httpRequestMessage);
var responseText = await response.Content.ReadAsStringAsync();

遇到了HTTPClient的问题,即使有了建议,仍然无法让它进行身份验证。结果是,我需要在相对路径中添加一个尾随'/'。

i.e.

var result = await _client.GetStringAsync(_awxUrl + "api/v2/inventories/?name=" + inventoryName);
var result = await _client.PostAsJsonAsync(_awxUrl + "api/v2/job_templates/" + templateId+"/launch/" , new {
                inventory = inventoryId
            });

引用解析由RFC 3986统一资源标识符(URI):通用语法描述。而这正是它应有的运作方式。为了保留基本URI路径,您需要在基本URI的末尾添加斜杠,并在相对URI的开头删除斜杠。

如果基URI包含非空路径,合并过程将丢弃其最后一部分(在last /之后)。相关部门:

5.2.3. 合并路径

上面的伪代码引用了用于合并的“merge”例程 使用基本URI路径的相对路径引用。这是 完成如下工作:

如果基本URI具有已定义的权限组件和空 路径,然后返回由"/"连接到 参考的路径;否则

返回由引用的路径组件组成的字符串 附加到除了基URI路径的最后一段之外的所有部分(即, 排除基URI中最右边的“/”后面的任何字符 路径,或排除整个基本URI路径,如果它不包含 任何“/”字符)。

如果相对URI以斜杠开头,则称为绝对路径相对URI。在这种情况下,合并过程忽略所有基本URI路径。更多信息请查看5.2.2。“转换引用”部分。

事实证明,在BaseAddress和传递给GetAsync方法(或HttpClient的任何其他方法)的相对URI上包含或排除尾斜杠或前导斜杠的四种可能的排列中,只有一种排列是有效的。必须在BaseAddress的末尾加上斜杠,而不能在相对URI的开头加上斜杠,如下例所示。

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api/");
    var response = await client.GetAsync("resource/7");
}

尽管我回答了我自己的问题,但我认为我应该在这里提供解决方案,因为这种不友好的行为是没有记录的。我的同事和我花了一天的大部分时间试图修复一个最终由HttpClient这个奇怪的特性引起的问题。