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

在API控制器动作中传递HttpContext时为何出现NullReferenceException?

问题:HttpContext.Items中存储的MediaTypeHeaderValue为null导致空引用异常

基于洋葱架构开发API,控制器位于独立项目,需根据Accept请求头返回JSON/XML格式的HATEOAS链接。通过自定义ActionFilter验证请求头并存储MediaType,但在业务层获取时触发空引用异常:

System.NullReferenceException: Object reference not set to an instance of an object.
mediaType was null.

相关代码

ValidateMediaTypeAttribute类

public class ValidateMediaTypeAttribute : IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        var acceptHeaderPresent = context.HttpContext.Request.Headers.ContainsKey("Accept");

        if (!acceptHeaderPresent)
        {
            context.Result = new BadRequestObjectResult($"Accept header is missing.");
            return;
        }

        var mediaType = context.HttpContext.Request.Headers["Accept"].FirstOrDefault();

        if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue? outMediaType))
        {
            context.Result = new BadRequestObjectResult($"Media type not present. Please add Accept header with the required media type.");
            return;
        }

        context.HttpContext.Items.Add("AcceptHeaderMediaType", outMediaType);
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
    }
}

控制器代码

public async Task<IActionResult> GetEmployeesForCompany(Guid companyId, [FromQuery] EmployeeParameters employeeParameters)
{
    var linkParams = new LinkParameters(employeeParameters, _httpContextAccessor.HttpContext);
    var pagedResult = await _service.EmployeeService.GetEmployeesAsync(companyId, linkParams, trackChanges: false);
    Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(pagedResult.metaData));

    return pagedResult.linkResponse.HasLinks ? Ok(pagedResult.linkResponse.LinkedEntities) : Ok(pagedResult.linkResponse.ShapedEntities);
}

TryGenerateLinks方法及相关逻辑

public LinkResponse TryGenerateLinks(IEnumerable<EmployeeDto> employeesDto, string fields, Guid companyId, HttpContext httpContext)
{
    var shapedEmployees = ShapeData(employeesDto, fields);

    if (ShouldGenerateLinks(httpContext))
        return ReturnLinkedEmployees(employeesDto, fields, companyId, httpContext, shapedEmployees);

    return ReturnShapedEmployees(shapedEmployees);
}

private List<Entity> ShapeData(IEnumerable<EmployeeDto> employeesDto, string fields) =>
    _dataShaper.ShapedData(employeesDto, fields).Select(e => e.Entity).ToList();

private bool ShouldGenerateLinks(HttpContext httpContext)
{
    var mediaType = (MediaTypeHeaderValue)httpContext.Items["AcceptHeaderMediaType"]; // 异常触发位置

    return mediaType.SubTypeWithoutSuffix.EndsWith("hateoas", StringComparison.InvariantCultureIgnoreCase);
}

问题原因

  1. Filter执行时机错误:ValidateMediaTypeAttribute的逻辑写在OnActionExecuted方法中,该方法是控制器动作执行完成后才触发的。但控制器动作里已经调用了_service.EmployeeService.GetEmployeesAsync,此时Filter还未执行,HttpContext.Items中还没有存入AcceptHeaderMediaType,导致后续取值为null。
  2. 缺少空值防护:即使Filter执行正常,也没有对HttpContext.Items中的值做空值检查,直接强制转换会触发异常。

解决方案

1. 调整Filter执行时机

将MediaType验证逻辑从OnActionExecuted移到OnActionExecuting(控制器动作执行前触发),确保业务层调用前已经完成MediaType的存储:

public class ValidateMediaTypeAttribute : IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        // 保留空实现或添加后置处理逻辑
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        var acceptHeaderPresent = context.HttpContext.Request.Headers.ContainsKey("Accept");

        if (!acceptHeaderPresent)
        {
            context.Result = new BadRequestObjectResult("Accept header is missing.");
            return;
        }

        var mediaType = context.HttpContext.Request.Headers["Accept"].FirstOrDefault();

        if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue? outMediaType))
        {
            context.Result = new BadRequestObjectResult("Media type not present. Please add Accept header with the required media type.");
            return;
        }

        // 若键已存在,避免重复添加
        if (!context.HttpContext.Items.ContainsKey("AcceptHeaderMediaType"))
        {
            context.HttpContext.Items.Add("AcceptHeaderMediaType", outMediaType);
        }
    }
}

2. 添加空值防护逻辑

修改ShouldGenerateLinks方法,先检查HttpContext.Items中的值是否存在且类型正确,再进行判断:

private bool ShouldGenerateLinks(HttpContext httpContext)
{
    if (httpContext?.Items == null)
        return false;

    if (httpContext.Items.TryGetValue("AcceptHeaderMediaType", out var mediaTypeObj) 
        && mediaTypeObj is MediaTypeHeaderValue mediaType)
    {
        return mediaType.SubTypeWithoutSuffix.EndsWith("hateoas", StringComparison.InvariantCultureIgnoreCase);
    }

    // 默认不生成链接,可根据业务需求调整
    return false;
}

3. 确认IHttpContextAccessor注册

确保在DI容器中正确注册IHttpContextAccessor(通常在Program.cs中):

builder.Services.AddHttpContextAccessor();

4. 确认Filter注册

确保ValidateMediaTypeAttribute已正确应用:

  • 全局注册(所有控制器生效):
builder.Services.AddControllers(options =>
{
    options.Filters.Add<ValidateMediaTypeAttribute>();
});
  • 或在目标控制器/动作上添加特性:
[ValidateMediaType]
public class EmployeesController : ControllerBase
{
    // ...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 05:07:22