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

.NET 4.6.1 WebAPI2 HttpClient发GET请求报NullReferenceException

空引用异常触发原因

代码存在多个触发NullReferenceException的隐患,核心问题如下:

  • Action方法使用async void签名:ASP.NET Web API管道无法感知async void方法内部的await异步操作,会在方法第一次执行await时就判定请求处理完成,提前释放HttpContext等请求关联对象,后续代码操作已释放的响应/请求对象时直接触发空引用。
  • 无类型反序列化结果可能为null:JsonConvert.DeserializeObject(eventResponse)不传目标类型时,如果Eventbrite返回空内容、非预期格式内容,反序列化结果会是null,后续直接调用events.WriteToReponse()属于对null对象执行方法调用,必然触发异常。
  • 静态HttpClient全局头操作存在线程安全问题:静态HttpClient的DefaultRequestHeaders是全局共享资源,并发请求下执行Clear、Add操作会出现头值丢失、对象状态异常,极端场景下会返回null引用。
  • 异步方法同步阻塞隐患:res.Content.ReadAsStringAsync().Result用同步阻塞方式调用异步方法,在ASP.NET .NET Framework环境下极易触发死锁,若请求上下文被提前释放,res.Content也可能为null触发空引用。
  • 异常分支响应未正确输出:catch块中仅创建了JsonResponse对象,没有将其写入响应流,且如果上下文已提前释放,创建响应对象时访问内部HttpContext也会触发空引用。
  • 构造函数初始化请求/响应对象逻辑错误:就算放开构造函数中_response、_request的赋值注释,构造函数执行时Controller实例还未绑定当前请求上下文,拿到的对象要么是null要么是其他请求的残留实例,后续操作也会触发异常。
修复方案

按以下规则调整代码即可解决问题:

  • 将Action方法签名从async void改为async Task<IHttpActionResult>,让Web API管道可以正确等待异步执行完成,避免上下文提前释放。
  • 不要修改静态HttpClient的全局DefaultRequestHeaders,每次请求通过HttpRequestMessage独立构造请求头,解决并发场景下的线程安全问题。
  • 所有异步调用全程使用await,禁止用.Result、.Wait()做同步阻塞,避免死锁。
  • 反序列化后先做判空校验再执行后续操作,所有返回结果通过Web API自带的Ok()、BadRequest()等方法返回,不要依赖自定义方法直接操作响应流写入。
  • 移除控制器中注释掉的_response、_request字段,这类请求相关对象不要在构造函数中初始化。
  • 修正WriteToReponse的拼写错误(正确拼写为WriteToResponse),如果是自定义扩展方法需确认方法内部做了null值校验,避免对null实例调用时触发异常。

修复后的参考代码:

public class CANWebsiteV3Controller : ApiController
{
    // 静态HttpClient全局复用,禁止每次请求新建实例
    private static readonly HttpClient client = new HttpClient();

    [Route("api2.0/CANWebsiteV3/GetEvents")]
    [HttpGet]
    public async Task<IHttpActionResult> GetEvents()
    {
        const string API_TOKEN = "XXXXXXXXXXXX";
        const string ORGANIZATION_ID = "YYYYYYYYYY";
        string endpoint = $"https://www.eventbriteapi.com/v3/organizations/{ORGANIZATION_ID}/events/?time_filter=current_future";
        
        try
        {
            // 每次请求独立构造请求消息,不修改全局默认请求头
            using (var request = new HttpRequestMessage(HttpMethod.Get, endpoint))
            {
                request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {API_TOKEN}");
                request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                
                var res = await client.SendAsync(request);
                if (res.IsSuccessStatusCode)
                {
                    string eventResponse = await res.Content.ReadAsStringAsync();
                    if (string.IsNullOrWhiteSpace(eventResponse))
                    {
                        return BadRequest("Eventbrite returned empty response");
                    }
                    var events = JsonConvert.DeserializeObject(eventResponse);
                    if (events == null)
                    {
                        return BadRequest("Failed to parse event data");
                    }
                    // 直接返回序列化结果,不要直接操作响应流写入
                    return Ok(events);
                }
                string errorContent = await res.Content.ReadAsStringAsync();
                return BadRequest($"Could not fetch events: Eventbrite returned {res.StatusCode}, {errorContent}");
            }
        }
        catch(Exception ex)
        {
            return BadRequest($"Could not fetch events: {ex.Message}");
        }
    }
}

注意:如果项目需要用自定义JsonResponse类返回统一格式,只需要把上述代码中Ok(events)、BadRequest()的返回值替换为统一响应格式即可,不要直接操作Response流写入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 17:06:26