You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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的输出可能因程序快速退出而无法显示。

修复建议

  1. 修正异步调用方式:将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();
}
  1. 完善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();
}
  1. 修正Uri和请求头错误:确保HttpRequestMessage的Uri完整,请求头格式正确(如示例中修正了User-Agent的引号问题)。

内容的提问来源于stack exchange,提问作者Pavan Mahajan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 02:21:04