在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); }
问题原因
- Filter执行时机错误:
ValidateMediaTypeAttribute的逻辑写在OnActionExecuted方法中,该方法是控制器动作执行完成后才触发的。但控制器动作里已经调用了_service.EmployeeService.GetEmployeesAsync,此时Filter还未执行,HttpContext.Items中还没有存入AcceptHeaderMediaType,导致后续取值为null。 - 缺少空值防护:即使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
相关产品推荐
相关产品推荐

