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

ASP.NET Core 7中OData批量请求内部接口返回404问题求助

解决ASP.NET Core 7中OData批量端点处理普通API请求的问题

问题根源

  1. 请求格式不符合OData批量规范:OData的$batch端点遵循OData Batch Protocol,要求使用multipart/mixed媒体类型,而非你当前使用的JSON数组格式,这会导致批量处理器无法正确解析内部请求。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 05:55:05