在 Windows 窗体的 HttpClient 类中使用 DelegatingHandler - 内部处理程序尚未设置

Posted

技术标签:

【中文标题】在 Windows 窗体的 HttpClient 类中使用 DelegatingHandler - 内部处理程序尚未设置【英文标题】:Using a DelegatingHandler in HttpClient class from windows forms - Inner handler has not been set 【发布时间】:2017-04-29 23:01:13 【问题描述】:

首先,我收到的错误信息如下: 尚未设置内部处理程序

我正在编写一个自定义消息处理程序来处理我的 API 的身份验证 cookie 超时。例如,如果我的代码库调用 API 并反过来收到 401,那么它应该重试登录 url 以获取更新的 cookie。我计划按如下方式完成:

public class CkApiMessageHandler : DelegatingHandler

    private readonly string _email;
    private readonly string _password;
    private const string _loginPath = "Account/Login";

    public CkApiMessageHandler(string email, string password)
    
        _email = email;
        _password = password;

        //InnerHandler = new HttpControllerDispatcher(null);
    

    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    

        var response = await base.SendAsync(request, cancellationToken);

        if(response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
        
            Logging.Logger.LogInformation("Authentication cookie timed out");
            var baseAddress = request.RequestUri.Host;
            var loginPath = baseAddress + _loginPath;

            var originalRequest = request;

            var loginRequest = new HttpRequestMessage(new HttpMethod("POST"), loginPath);
            var dict = new Dictionary<string, string>();
            dict.Add("Email", _email);
            dict.Add("Password", _password);

            var form = new FormUrlEncodedContent(dict);
            loginRequest.Content = form;
            loginRequest.Headers.Clear();

            foreach(var header in originalRequest.Headers)
            
                loginRequest.Headers.Add(header.Key, header.Value);
            

            var loginResponse = await base.SendAsync(loginRequest, cancellationToken);

            if (loginResponse.IsSuccessStatusCode)
            
                Logging.Logger.LogInformation("Successfully logged back in");
                return await base.SendAsync(originalRequest, cancellationToken);
            
            else
            
                Logging.Logger.LogException(new Exception("Failed to log back in"), "Could not log back in");
            
        

        return response;
    


  

我正在将用于直接访问数据库的旧服务转换为访问 Web api 的服务,并且我正在尝试这样做而无需更改此类的任何消费者。因此奇怪的处理程序.这是服务类的示例方法。

 public void UpdateClientInstallDatabaseAndServiceData(string dbAcronym, string clientVersion, string clientEnginePort, Guid installationId, string sqlserver)
    
        var dict = new Dictionary<string, string>();
        dict.Add("dbAcronym", dbAcronym);
        dict.Add("clientVersion", clientVersion);
        dict.Add("clientEnginePort", clientEnginePort);
        dict.Add("desktopClientId", installationId.ToString());
        dict.Add("sqlServerIp", sqlserver);

        var form = new FormUrlEncodedContent(dict);

        _client.PostAsync(_apiPath + "/UpdateClientInstallDatabaseAndServiceData", form);
      

因此,如果上述代码因 401 失败,服务将自动重新登录并重试原始代码,而服务的使用者不必检查请求并重新登录。使用者不应该知道它正在处理一个网络 API。

我的问题是我的消息处理程序期望设置 InnerHandler,这需要 HttpConfiguration 类的实例。当我查看规范 here 时,它似乎是用于设置 Web api 服务的某种类型的类。这很好,只是这段代码不是在 api 中执行的。它是在 windows 窗体应用程序上执行的。委托处理程序正在HttpClient 类中使用,就像这样......

_client = new HttpClient(new CKEnterprise.Common.CkApiMessageHandler(email, password));  

所以我的问题是我怎样才能让这个 DelegatingHandler 在 web api 项目的上下文之外完成它的工作?

进行更新。看起来我可以只使用 HttpClientHandler 类 https://blogs.msdn.microsoft.com/henrikn/2012/08/07/httpclient-httpclienthandler-and-webrequesthandler-explained/

【问题讨论】:

【参考方案1】:

我应该早点意识到这一点,但也许将内部处理程序设置为 HttpClient 使用的 default 处理程序是有意义的。因此,在DelegatingHandler 的子类中,您应该将内部处理程序设置为HttpClient 使用的默认处理程序,如下所示:

    public CkApiMessageHandler(string email, string password, Guid moduleId)
    
        _email = email;
        _password = password;
        _moduleId = moduleId;
        InnerHandler = new HttpClientHandler();

    

【讨论】:

这根本不直观。很好的发现:)【参考方案2】:

阿德里安的答案是正确的。您必须为自定义处理程序设置内部处理程序,但有更好的方法来设置内部处理程序。

public MyDelegatingHandler( ) : base( new HttpClientHandler( ) )

【讨论】:

但是这里的HttpClientHandler是手动创建的?是否由框架管理(池化、生存时间等)?【参考方案3】:

或者你可以使用 HttpClientFactory

_client  = HttpClientFactory.Create(new CKEnterprise.Common.CkApiMessageHandler(email, password))

https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/httpclient-message-handlers#adding-message-handlers-to-the-client-pipeline

【讨论】:

这对我来说更有意义。谢谢拉杰

以上是关于在 Windows 窗体的 HttpClient 类中使用 DelegatingHandler - 内部处理程序尚未设置的主要内容,如果未能解决你的问题,请参考以下文章

无法在 JavaScript 中为 Windows.Web.Http.HttpClient 添加超时

Windows PCL HttpClient

HttpClient 和 windows phone 8 便携版

c# Httpclient 请求在 Windows 10 上工作正常返回 403 在 Windows 7 上被禁止(相同的代码)

如何在windows窗体里面添加窗口

怎么把指定网页嵌套在windows窗体程序中