【问题标题】:Custom DelegatingHandler never return after await base.SendAsync等待 base.SendAsync 后自定义 DelegatingHandler 永远不会返回
【发布时间】:2012-10-25 02:37:53
【问题描述】:

我正在使用 HttpClient 构建一个 API 使用者。因为提供者要求消费者使用 Digest Authentication 进行身份验证,所以我需要编写一个自定义 DelegatingHandler,如下所示:

public class DigestAuthDelegatingHandler : DelegatingHandler
{
    public DigestAuthDelegatingHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);
        if (!response.IsSuccessStatusCode && response.StatusCode == HttpStatusCode.Unauthorized)//This line of code is never reached
        {
            //Generate the Digest Authorization header string and add to the request header,
            //then try to resend the request to the API provider
        }
        return response;
    }
}

我创建一个 HttpClient 并将我的自定义 DelegatingHandler 添加到消息处理程序 pineline

HttpClient httpClient = new HttpClient(new DigestAuthDelegatingHandler(new HttpClientHandler()));            
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.BaseAddress = new Uri("http://127.0.0.1/");
HttpResponseMessage response = httpClient.GetAsync("api/getTransactionInfo?TransactionNumber=1000).Result;   

这样做之后,看起来我的消费者会永远运行。当我在上面的代码行 await base.SendAsync 之后添加断点时,我看到代码永远不会返回,因此我无法检查响应是否获得 401 未授权以提取摘要授权标头详细信息。 API 提供者没有任何问题,因为我已经使用传统的 WebHttpRequest 支持 Digest Authenticate 成功构建了另一个 API 消费者站点,并且运行良好。

重要提示:如果我切换到将使用者编写为控制台应用程序,那么它运行良好。所以,我不确定,但我认为这是在异步模式下运行时与 ASP.NET 线程有关的问题?

我做错了什么吗?

【问题讨论】:

    标签: c# async-await dotnet-httpclient


    【解决方案1】:

    我同意 Darrel 的观点 - 很可能,任务出错并且没有结果......您可以使用显式延续来检查任务状态 - 例如,

    return base.SendAsync(request, cancellationToken).ContinueWith(task =>
        {
            // put the code to check the task state here...
        });
    

    另外,我不确定您是否需要创建自定义DelegatingHandler 进行身份验证...尝试使用HttpClientHandlerCredentials 属性(或UseDefaultCredentials 传递当前用户的默认凭据)

    var httpClient = new HttpClient(new HttpClientHandler() {
           PreAuthenticate = true,
           Credentials = new NetworkCredentials(...
       }); 
    

    编辑:找到与使用凭证缓存的 http 客户端一起使用摘要身份验证的示例 - 请参阅此 SO Q & A:HttpRequestMessage and Digest Authentication

    这应该可以解决您的实际问题 w.r.t.无需构建自己的处理程序即可消化身份验证。

    【讨论】:

    【解决方案2】:

    我的猜测是 .Result 阻止了处理程序中的继续。尝试将 .Result 更改为 .ContinueWith

    【讨论】:

    • 更好的是,如果可用,请使用await 关键字。
    • 但是 ASP.NET 不使用 STA 线程来响应 Web 请求,所以这似乎不是问题。
    • @Andrew Arnott 它仍然有一个同步上下文
    【解决方案3】:

    遇到了同样的问题,这就是最终对我有用的方法。问题是端点抛出了 500 Internal Server Error 并因此阻塞了线程。

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return await base.SendAsync(request, cancellationToken)
                             .ContinueWith<HttpResponseMessage>(task =>
            {
                return task.Result;
            });
        }
    

    注意,anon函数内部的return返回到SendAsync函数,然后我们实际返回Result。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-10
      • 1970-01-01
      • 2014-09-01
      • 2023-03-25
      • 2019-05-21
      • 1970-01-01
      相关资源
      最近更新 更多