ASP.NET Core 7中OData批量请求内部接口返回404问题求助
解决ASP.NET Core 7中OData批量端点处理普通API请求的问题
问题根源
- 请求格式不符合OData批量规范:OData的
$batch端点遵循OData Batch Protocol,要求使用multipart/mixed媒体类型,而非你当前使用的JSON数组格式,这会导致批量处理器无法正确解析内部请求。 - DefaultODataBatchHandler仅支持OData路由:你的
api/i18n是普通API控制器路由,未注册到OData的EdmModel中,因此即使请求格式正确,默认批量处理器也无法匹配到对应的端点。
解决方案
方案1:修正请求格式并自定义批量处理器
1.1 使用标准OData批量请求格式
OData批量请求必须采用multipart/mixed格式,示例如下:
POST /api/$batch HTTP/1.1 Host: localhost:5000 Content-Type: multipart/mixed; boundary=batch_abc123 --batch_abc123 Content-Type: application/http; msgtype=request GET /api/i18n HTTP/1.1 Accept: application/json --batch_abc123 Content-Type: application/http; msgtype=request PUT /api/i18n/1 HTTP/1.1 Content-Type: application/json {"id":1,"key":"test","value":"测试"} --batch_abc123--
1.2 创建自定义批量处理器
编写自定义处理器,让它在OData原生处理失败时,触发ASP.NET Core的端点路由匹配普通API:
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.OData.Batch; using Microsoft.OData; public class NonODataBatchHandler : DefaultODataBatchHandler { protected override async Task<ODataBatchResponseItem> CreateResponseMessageAsync( HttpRequest request, HttpResponse response, Guid batchId, CancellationToken cancellationToken) { // 优先尝试OData原生逻辑处理 try { return await base.CreateResponseMessageAsync(request, response, batchId, cancellationToken); } catch (ODataException) { // OData处理失败,触发普通API路由匹配 var routeContext = new RouteContext(request.HttpContext); await request.HttpContext.RequestServices .GetRequiredService<Microsoft.AspNetCore.Routing.IRouteMatcher>() .MatchAsync(routeContext); // 执行匹配到的端点 var endpoint = routeContext.HttpContext.GetEndpoint(); if (endpoint != null) { await endpoint.RequestDelegate(routeContext.HttpContext); } return new ODataBatchResponseItem(response); } } }
1.3 注册自定义处理器
在Program.cs中替换默认批量处理器:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddOData(options => { // 使用空EdmModel,避免启用OData查询特性 options.AddRouteComponents("api", new EdmModel(), new NonODataBatchHandler()); }); var app = builder.Build(); app.UseODataBatching(); app.MapControllers(); app.Run();
方案2:实现自定义批量端点(更灵活支持JSON格式)
如果不想依赖OData的批量规范,可以自己实现一个支持JSON格式的批量端点:
2.1 编写批量控制器及模型
[Route("api/batch")] [ApiController] public class BatchController : ControllerBase { private readonly IHttpContextFactory _httpContextFactory; public BatchController(IHttpContextFactory httpContextFactory) { _httpContextFactory = httpContextFactory; } [HttpPost] public async Task<IActionResult> ProcessBatch([FromBody] BatchRequest batchRequest) { var responses = new List<BatchResponse>(); foreach (var subReq in batchRequest.Requests) { try { // 创建子请求上下文 var subHttpContext = _httpContextFactory.Create(new DefaultHttpContextOptions { RequestServices = HttpContext.RequestServices }); // 复制请求信息 subHttpContext.Request.Method = subReq.Method; subHttpContext.Request.Path = subReq.Url; foreach (var (key, value) in subReq.Headers) { subHttpContext.Request.Headers.TryAdd(key, value); } // 写入请求体(针对POST/PUT请求) if (!string.IsNullOrEmpty(subReq.Body)) { await using var writer = new StreamWriter(subHttpContext.Request.Body); await writer.WriteAsync(subReq.Body); await writer.FlushAsync(); subHttpContext.Request.Body.Position = 0; } // 触发路由匹配并执行端点 await subHttpContext.RequestServices .GetRequiredService<Microsoft.AspNetCore.Routing.IRouteMatcher>() .MatchAsync(new RouteContext(subHttpContext)); var endpoint = subHttpContext.GetEndpoint(); if (endpoint != null) { await endpoint.RequestDelegate(subHttpContext); } // 收集响应 responses.Add(new BatchResponse { Id = subReq.Id, Status = (int)subHttpContext.Response.StatusCode, Body = await new StreamReader(subHttpContext.Response.Body).ReadToEndAsync(), Headers = subHttpContext.Response.Headers.ToDictionary(kv => kv.Key, kv => kv.Value.First()) }); } catch (Exception ex) { responses.Add(new BatchResponse { Id = subReq.Id, Status = 500, Body = ex.Message }); } } return Ok(new { Responses = responses }); } } // 批量请求模型 public class BatchRequest { public List<BatchSubRequest> Requests { get; set; } = new(); } public class BatchSubRequest { public string Id { get; set; } public string Method { get; set; } public string Url { get; set; } public Dictionary<string, string> Headers { get; set; } = new(); public string Body { get; set; } } // 批量响应模型 public class BatchResponse { public string Id { get; set; } public int Status { get; set; } public Dictionary<string, string> Headers { get; set; } = new(); public string Body { get; set; } }
2.2 使用自定义批量端点
直接用你原来的JSON格式请求/api/batch即可完成批量操作。
内容的提问来源于stack exchange,提问作者CheloXL
相关产品推荐
相关产品推荐

