使用Digest认证调用Post方法失败:执行SendAsync后程序直接退出
问题:Digest认证下POST请求无响应直接退出
我尝试通过Digest认证向设备调用POST方法,但无法正常工作。调用await client.SendAsync时,程序未返回响应便直接退出,无法定位问题。
相关代码如下:
private void fnTest() { var client = new HttpClient(); client.BaseAddress = new Uri("http://192.168.1.12/TestDev"); var request = new HttpRequestMessage(HttpMethod.Post, "http: request.Headers.Add("Accept", "*/*"); request.Headers.Add("User-Agent", "BC \"A\""); //var response = await HTTPClientDigestAuth.HttpClientExtensions.SendWithDigestAuthAsync(client, request, HttpCompletionOption.ResponseContentRead, "user", "password"); HttpClientExtensionTests(); } public static async Task<HttpResponseMessage> SendWithDigestAuthAsync(this HttpClient client, HttpRequestMessage request, HttpCompletionOption httpCompletionOption, string username, string password) { var newRequest = CloneBeforeContentSet(request); try { using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) // --> 程序从此处直接退出 { Console.WriteLine(response); // --> 未执行到此处 } } catch (Exception ex) { Console.WriteLine(ex.Message); } return null; }
可能的问题点
- 同步方法调用异步代码导致主线程提前退出:
fnTest是void类型的同步方法,内部调用异步方法却未等待(即使启用注释的代码,void方法也无法直接使用await),导致主线程执行完毕后直接退出,异步请求还未完成就被终止。 - HttpRequestMessage初始化错误:代码中
new HttpRequestMessage(HttpMethod.Post, "http:是截断的不完整Uri,会导致请求无法正确构造,可能引发未被妥善处理的异常,直接导致程序退出。 - Digest认证逻辑缺失:当前
SendWithDigestAuthAsync仅发送了原始请求,未处理Digest认证的标准流程——需要先发送无认证请求,接收401响应后提取认证参数,计算认证凭证再重新发送请求,这会导致设备无法识别认证信息,可能无响应或返回401但未被处理。 - 异常捕获局限性:异步方法中的异常如果未关联到主线程,可能无法通过当前的
try-catch捕获,且Console.WriteLine的输出可能因程序快速退出而无法显示。
修复建议
- 修正异步调用方式:将
fnTest改为异步方法,确保等待异步操作完成:
private async Task fnTest() { // 建议HttpClient单例复用,避免频繁创建 using var client = new HttpClient(); client.BaseAddress = new Uri("http://192.168.1.12/TestDev"); // 修正为完整的相对或绝对Uri var request = new HttpRequestMessage(HttpMethod.Post, "/your-target-endpoint"); request.Headers.Add("Accept", "*/*"); request.Headers.Add("User-Agent", "BC \"A\""); var response = await HTTPClientDigestAuth.HttpClientExtensions.SendWithDigestAuthAsync(client, request, HttpCompletionOption.ResponseContentRead, "user", "password"); // 若HttpClientExtensionTests是异步方法,同样需要await // await HttpClientExtensionTests(); } // 控制台程序的Main方法改为异步(.NET Core 3.0+支持) static async Task Main(string[] args) { await fnTest(); }
- 完善Digest认证逻辑:补充401响应的处理流程,实现标准的Digest认证:
public static async Task<HttpResponseMessage> SendWithDigestAuthAsync(this HttpClient client, HttpRequestMessage request, HttpCompletionOption httpCompletionOption, string username, string password) { var originalRequest = CloneRequest(request); var response = await client.SendAsync(originalRequest, httpCompletionOption); if (response.StatusCode == HttpStatusCode.Unauthorized) { var authHeader = response.Headers.WwwAuthenticate.FirstOrDefault(h => h.Scheme.Equals("Digest", StringComparison.OrdinalIgnoreCase)); if (authHeader != null) { // 解析Digest认证参数(nonce, realm等) var digestParams = ParseDigestParameters(authHeader.Parameter); // 生成Authorization头 var authValue = GenerateDigestAuthorization(request.RequestUri.AbsolutePath, username, password, digestParams); var newRequest = CloneRequest(request); newRequest.Headers.Authorization = new AuthenticationHeaderValue("Digest", authValue); // 重新发送带认证的请求 response = await client.SendAsync(newRequest, httpCompletionOption); } } return response; } // 辅助方法:克隆请求 private static HttpRequestMessage CloneRequest(HttpRequestMessage request) { var clone = new HttpRequestMessage(request.Method, request.RequestUri); foreach (var header in request.Headers) { clone.Headers.TryAddWithoutValidation(header.Key, header.Value); } if (request.Content != null) { // 若有请求体,需要重新读取并复制 var contentStream = new MemoryStream(); request.Content.CopyToAsync(contentStream).Wait(); contentStream.Position = 0; clone.Content = new StreamContent(contentStream); foreach (var header in request.Content.Headers) { clone.Content.Headers.TryAddWithoutValidation(header.Key, header.Value); } } return clone; } // 辅助方法:解析Digest参数 private static Dictionary<string, string> ParseDigestParameters(string parameter) { return parameter.Split(',') .Select(p => p.Trim().Split('=')) .ToDictionary(kv => kv[0], kv => kv[1].Trim('"')); } // 辅助方法:生成Digest Authorization值 private static string GenerateDigestAuthorization(string path, string username, string password, Dictionary<string, string> digestParams) { var realm = digestParams["realm"]; var nonce = digestParams["nonce"]; var qop = digestParams.TryGetValue("qop", out var q) ? q : "auth"; var nc = "00000001"; var cnonce = Guid.NewGuid().ToString("N").Substring(0, 8); var ha1 = Hash($"{username}:{realm}:{password}"); var ha2 = Hash($"POST:{path}"); var response = Hash($"{ha1}:{nonce}:{nc}:{cnonce}:{qop}:{ha2}"); return $"username=\"{username}\", realm=\"{realm}\", nonce=\"{nonce}\", uri=\"{path}\", qop={qop}, nc={nc}, cnonce=\"{cnonce}\", response=\"{response}\""; } private static string Hash(string input) { using var md5 = MD5.Create(); var bytes = Encoding.ASCII.GetBytes(input); var hashBytes = md5.ComputeHash(bytes); return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); }
- 修正Uri和请求头错误:确保
HttpRequestMessage的Uri完整,请求头格式正确(如示例中修正了User-Agent的引号问题)。
内容的提问来源于stack exchange,提问作者Pavan Mahajan
相关产品推荐
相关产品推荐

