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

使用HttpClient调用REST API失败:旧代码可用新代码崩溃

问题:异步HttpClient调用Jira API时无提示崩溃

一、可正常工作的旧函数(基于HttpWebRequest)

调用方式

string requestData = RestClient.JiraRequest(buildFilter());

函数实现

public static string JiraRequest(string api)
{
    string data = string.Empty;
    
    try
    {
        string requestUrl = @"https://jira.xxxxx.org/rest/api/2/" + api;
        string authType = "Basic";
        string httpMethod = "GET";

        string authHeader = Convert.ToBase64String(Encoding.ASCII.GetBytes(Config.UserName + ":" + Config.UserPassword));
        _ = Encoding.UTF8.GetBytes(authHeader);

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
        request.Method = httpMethod;
        request.Headers.Add("Authorization", authType + " " + authHeader);
        request.ContentType = "application/json";

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();

        if (response.StatusCode != HttpStatusCode.OK)
        {
            throw new ApplicationException("Exception error: " + response.StatusCode.ToString());
        }

        using (Stream responseStream = response.GetResponseStream())
        {
            if (responseStream != null)
            {
                using (StreamReader reader = new StreamReader(responseStream))
                {
                    data = reader.ReadToEnd();
                }
            }
        }
    }
    catch (Exception ex)
    {
        HandleExeption.Exception(ex);
        return data;
    }

    return data;
}

二、调用后崩溃的新异步函数(基于HttpClient)

调用方式

string requestData = await RestClient.JiraRequest(buildFilter());

函数实现

public static async Task<string> JiraRequest(string api)
{
    string data = string.Empty;

    try
    {
        string requestUrl = @"https://jira.xxxxx.org/rest/api/2/" + api;
        string authType = "Basic";

        using (HttpClient client = new())
        {
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authType, Convert.ToBase64String(Encoding.ASCII.GetBytes(Config.UserName + ":" + Config.UserPassword)));

            HttpResponseMessage responseMessage = await client.GetAsync(requestUrl);

            if (!responseMessage.IsSuccessStatusCode)
            {
                throw new ApplicationException("HttpClient exception: " + responseMessage.StatusCode.ToString());
            }

            data = await responseMessage.Content.ReadAsStringAsync();
            return data;
        }
    }
    catch (Exception ex)
    {
        HandleExeption.Exception(ex);
        return data;
    }
}

三、问题描述

旧代码基于HttpWebRequest实现,因收到该API已过时的提示,改用HttpClient开发异步调用函数。旧函数可正常调用Jira REST API,但新函数执行到HttpResponseMessage responseMessage = await client.GetAsync(requestUrl);时,程序直接崩溃退出,无异常信息输出,使用Debug.WriteLine也无法捕获日志。

四、异常处理函数实现

public static void Exception(Exception ex)
{
    StackTrace stackTrace = new(ex, true);

#nullable enable
    StackFrame? stackFrame = stackTrace.GetFrame(stackTrace.FrameCount - 1);
#nullable disable

    Debug.WriteLine($"Exception message: {ex.Message ?? "No message"}");

    if (stackFrame != null)
    {
        var method = stackFrame.GetMethod();
        Debug.WriteLine($"Exception in method: {method}");

        var lineNumber = stackFrame.GetFileLineNumber();
        Debug.WriteLine($"Exception in line: {lineNumber}");
    }
}

问题排查与解决方案

1. 异步调用未正确等待导致主线程退出

这是无提示崩溃最常见的原因:如果调用await RestClient.JiraRequest的方法是同步方法(比如控制台程序的Main未标记async),直接使用await会导致异步操作未完成时主线程提前退出,程序直接终止,不会触发任何异常捕获。

解决办法:

  • 控制台程序将Main改为异步:
    static async Task Main(string[] args)
    {
        string requestData = await RestClient.JiraRequest(buildFilter());
        // 后续逻辑
    }
    
  • 无法修改调用方法为异步时,用.GetAwaiter().GetResult()替代await(注意:非UI/非ASP.NET环境下谨慎使用,避免死锁):
    string requestData = RestClient.JiraRequest(buildFilter()).GetAwaiter().GetResult();
    

2. HttpClient实例滥用导致资源问题

HttpClient设计为可复用对象,每次请求创建新实例可能导致套接字耗尽;同时using包裹实例在同步等待场景下可能提前释放对象。

优化方案:改为静态单例模式

private static readonly HttpClient _httpClient = new HttpClient();

public static async Task<string> JiraRequest(string api)
{
    string data = string.Empty;

    try
    {
        string requestUrl = @"https://jira.xxxxx.org/rest/api/2/" + api;
        string authType = "Basic";

        // 仅首次调用时设置授权头,避免重复添加
        if (!_httpClient.DefaultRequestHeaders.Contains("Authorization"))
        {
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authType, 
                Convert.ToBase64String(Encoding.ASCII.GetBytes(Config.UserName + ":" + Config.UserPassword)));
        }

        HttpResponseMessage responseMessage = await _httpClient.GetAsync(requestUrl);

        if (!responseMessage.IsSuccessStatusCode)
        {
            throw new ApplicationException("HttpClient exception: " + responseMessage.StatusCode.ToString());
        }

        data = await responseMessage.Content.ReadAsStringAsync();
        return data;
    }
    catch (Exception ex)
    {
        HandleExeption.Exception(ex);
        return data;
    }
}

3. 未捕获全局异步异常

异步操作的异常可能抛到全局上下文,未被当前try/catch捕获,需添加全局异常处理:

  • 控制台程序:
    AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
    {
        var ex = e.ExceptionObject as Exception;
        Debug.WriteLine($"未处理异常: {ex?.Message ?? "未知错误"}");
        Debug.WriteLine($"堆栈跟踪: {ex?.StackTrace}");
    };
    
    TaskScheduler.UnobservedTaskException += (sender, e) =>
    {
        Debug.WriteLine($"未观察到的任务异常: {e.Exception.Message}");
        Debug.WriteLine($"堆栈跟踪: {e.Exception.StackTrace}");
        e.SetObserved();
    };
    
  • WPF/WinForms:分别添加Application.DispatcherUnhandledException或Application.ThreadException事件处理。

4. 验证授权头正确性

虽然新旧代码逻辑一致,可打印授权头确认是否与旧代码生成的一致,排除配置读取问题:

var credentials = $"{Config.UserName}:{Config.UserPassword}";
var base64Credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials));
Debug.WriteLine($"Base64授权字符串: {base64Credentials}");

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:05:22