在.NET 4.5和C#中使用HttpClient发送HTTP HEAD请求

74

在.NET 4.5的新HttpClient中,是否可以创建HTTP HEAD请求?我能找到的唯一方法是GetAsyncDeleteAsyncPutAsyncPostAsync。我知道HttpWebRequest类可以做到这一点,但我想使用现代的HttpClient


可能是添加Http Headers到HttpClient (ASP.NET Web API)的重复问题。 - Arsen Mkrtchyan
20
这不是一个重复的问题。我想仅阅读我发出请求的响应头,这可以通过使用HTTP HEAD请求来实现。这与你提到的线程完全无关。 - The Wavelength
4个回答

134

使用SendAsync方法和一个使用HttpMethod.Head构建的HttpRequestMessage实例。

GetAsyncPostAsync等是SendAsync方便包装;不常见的HTTP方法(例如HEADOPTIONS等)则没有包装。

简而言之:

client.SendAsync(new HttpRequestMessage(HttpMethod.Head, url))


19

您也可以按照以下步骤仅获取头部信息:

this.GetAsync($"http://url.com", HttpCompletionOption.ResponseHeadersRead).Result;

7
这种方法的美妙之处在于,即使目标服务器明确禁止“HEAD”请求(例如Amazon AWS),它仍然有效。 - Ian Kemp
9
从代码角度来看,我喜欢这个选项,Ian的评论也是一个优点。但我实现了这个和Smigs的答案,后者的表现要快得多......尽管我的URL指向30-100MB的文件,可能与此有关。但这个答案发起了GET请求而不是HEAD,所以请记住这一点。 - jasonxz
我收到了一个“方法不允许”的错误。 - Enrico
如果存在连接更改/重定向,此解决方案实际上将遵循连接。仅检索原始URL的标题,即使进行完整获取可能会失败,也会返回200响应。 - Fernando Sibaja
1
正如@jasonxz所观察到的那样,这确实会进行完整的GET请求。它允许您在可用时立即访问标头,但仍会拉取其余数据。因此,如果您想在只需要标头但不想下载可能很大的主体时节省带宽,这不是解决方案。 - wojtow

6

我需要执行以下操作,从Web API的GET方法返回的ATMs中获取TotalCount

当我尝试使用@Smig的答案时,我从我的Web API获得了以下响应。

MethodNotAllowed:Pragma:no-cache X-SourceFiles:=?UTF-8?B?dfdsf Cache-Control:no-cache Date:Wed, 22 Mar 2017 20:42:57 GMT Server:Microsoft-IIS / 10.0 X-AspNet-Version:4.0.30319 X-Powered-By:ASP.NET

必须在@Smig的答案基础上进行构建才能成功运行此操作。我发现Web API方法需要通过在 Action 方法中将其作为属性显式允许 Http HEAD 动词。

这是带有内联代码注释的完整代码。我删除了敏感代码。

在我的Web客户端中:

        HttpClient client = new HttpClient();

        // set the base host address for the Api (comes from Web.Config)
        client.BaseAddress = new Uri(ConfigurationManager.AppSettings.Get("ApiBase"));
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add( 
          new MediaTypeWithQualityHeaderValue("application/json"));

        // Construct the HEAD only needed request. Note that I am requesting
        //  only the 1st page and 1st record from my API's endpoint.
        HttpRequestMessage request = new HttpRequestMessage(
          HttpMethod.Head, 
          "api/atms?page=1&pagesize=1");

        HttpResponseMessage response = await client.SendAsync(request);

        // FindAndParsePagingInfo is a simple helper I wrote that parses the 
        // json in the Header and populates a PagingInfo poco that contains 
        // paging info like CurrentPage, TotalPages, and TotalCount, which 
        // is the total number of records in the ATMs table.
        // The source code is pasted separately in this answer.
        var pagingInfoForAtms = HeaderParser.FindAndParsePagingInfo(response.Headers);

        if (response.IsSuccessStatusCode)
            // This for testing only. pagingInfoForAtms.TotalCount correctly
            //  contained the record count
            return Content($"# of ATMs {pagingInfoForAtms.TotalCount}");

            // if request failed, execution will come through to this line 
            // and display the response status code and message. This is how
            //  I found out that I had to specify the HttpHead attribute.
            return Content($"{response.StatusCode} : {response.Headers.ToString()}");
        }

在 Web API 中。
    // Specify the HttpHead attribute to avoid getting the MethodNotAllowed error.
    [HttpGet, HttpHead]
    [Route("Atms", Name = "AtmsList")]
    public IHttpActionResult Get(string sort="id", int page = 1, int pageSize = 5)
    {
        try
        {
            // get data from repository
            var atms =  _atmRepository.GetAll().AsQueryable().ApplySort(sort);
            // ... do some code to construct pagingInfo etc.
            // .......
            // set paging info in header.
            HttpContext.Current.Response.Headers.Add(
              "X-Pagination", JsonConvert.SerializeObject(paginationHeader));
            // ...
            return Ok(pagedAtms));
        }
        catch (Exception exception)
        {
            //... log and return 500 error
        }
    }

FindAndParsePagingInfo

是一个帮助方法,用于解析分页头数据。
public static class HeaderParser
{
public static PagingInfo FindAndParsePagingInfo(HttpResponseHeaders responseHeaders)
{
    // find the "X-Pagination" info in header
    if (responseHeaders.Contains("X-Pagination"))
    {
        var xPag = responseHeaders.First(ph => ph.Key == "X-Pagination").Value;

        // parse the value - this is a JSON-string.
        return JsonConvert.DeserializeObject<PagingInfo>(xPag.First());
    }

    return null;
}

public static string GetSingleHeaderValue(HttpResponseHeaders responseHeaders, 
    string keyName)
{
    if (responseHeaders.Contains(keyName))
        return responseHeaders.First(ph => ph.Key == keyName).Value.First();

    return null;
}

}


1
@Kiquenet 你好,我已经更新了答案,并附上了HeaderParser.FindAndParsePagingInfo的源代码。 - Shiva
方法 IHttpActionResult Get 中的变量 paginationHeader 是什么? - Kiquenet

3
通过以下方法解决了这个问题。
 var result = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接