ASP.NET Core WebApi项目中如何结合Swagger使用OData筛选功能?
问题原因
Swagger生成OpenAPI文档时,无法自动解析ODataQueryOptions<>泛型类型的结构,会默认将其识别为请求Body参数,最终导致swagger.json生成失败,触发你遇到的报错。
解决方案
方案一:自定义Swagger过滤器(推荐,可保留OData调试能力)
步骤1:创建OData参数处理过滤器
新建ODataQueryOptionsFilter.cs类,实现Swagger的操作过滤器接口,移除无法解析的ODataQueryOptions参数,手动补充OData标准查询参数:
using Microsoft.AspNetCore.OData.Query; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; public class ODataQueryOptionsFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { // 移除参数列表中的ODataQueryOptions类型参数,避免Swagger解析失败 var odataParamDescriptions = context.ApiDescription.ParameterDescriptions .Where(p => p.Type.IsGenericType && p.Type.GetGenericTypeDefinition() == typeof(ODataQueryOptions<>)) .ToList(); foreach (var paramDesc in odataParamDescriptions) { var targetParam = operation.Parameters.FirstOrDefault(p => p.Name == paramDesc.Name); if (targetParam != null) { operation.Parameters.Remove(targetParam); } } // 手动添加OData常用查询参数,可按需扩展$select、$expand等 operation.Parameters.AddRange(new List<OpenApiParameter> { new() { Name = "$filter", In = ParameterLocation.Query, Description = "数据筛选条件", Required = false, Schema = new OpenApiSchema { Type = "string" } }, new() { Name = "$top", In = ParameterLocation.Query, Description = "返回最大数据条数", Required = false, Schema = new OpenApiSchema { Type = "integer", Minimum = 1 } }, new() { Name = "$skip", In = ParameterLocation.Query, Description = "跳过的前置数据条数", Required = false, Schema = new OpenApiSchema { Type = "integer", Minimum = 0 } }, new() { Name = "$orderby", In = ParameterLocation.Query, Description = "排序规则,示例:CreateTime desc", Required = false, Schema = new OpenApiSchema { Type = "string" } }, new() { Name = "$count", In = ParameterLocation.Query, Description = "是否返回符合条件的总条数", Required = false, Schema = new OpenApiSchema { Type = "boolean" } } }); } }
步骤2:注册过滤器到Swagger配置
在你的服务注册文件(Program.cs或Startup.cs)中,将上述过滤器添加到Swagger生成配置中:
builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Title = "你的接口服务", Version = "v1" }); // 注册OData参数处理过滤器 options.OperationFilter<ODataQueryOptionsFilter>(); });
步骤3:修改接口入参标记
给接口的ODataQueryOptions参数添加[FromQuery]标记,避免ASP.NET Core框架默认将其识别为Body参数,同时补充你代码中待实现的分页逻辑:
[HttpGet] [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All, AllowedFunctions = AllowedFunctions.AllFunctions, MaxTop = 500)] public async Task<PagedProduct> Search([FromQuery] ODataQueryOptions<Product> queryOptions) { // 分页参数计算 var pageSize = queryOptions.Top?.Value ?? 100; var skipCount = queryOptions.Skip?.Value ?? 0; var currentPage = skipCount / pageSize + 1; var countQuery = queryOptions.ApplyTo(_db.Set<Product>(), AllowedQueryOptions.Top | AllowedQueryOptions.Skip) as IQueryable<Product>; var query = queryOptions.ApplyTo(_db.Set<Product>()) as IQueryable<Product>; var result = new PagedProduct() { CurrentPage = currentPage, PageSize = pageSize, TotalRecords = await countQuery.CountAsync(), Data = await query.ToListAsync() }; return result; }
方案二:极简修复(不需要修改Swagger配置)
如果你不需要手动处理分页逻辑,仅需要OData的查询能力,可以直接将接口返回值改为IQueryable<Product>,去掉ODataQueryOptions入参,由[EnableQuery]特性自动处理所有OData查询逻辑,不会触发Swagger报错:
[HttpGet] [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All, AllowedFunctions = AllowedFunctions.AllFunctions, MaxTop = 500)] public IQueryable<Product> Search() { return _db.Set<Product>(); }
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

