有没有办法让 Swashbuckle 将 OData 参数添加到 Web API 2 IQueryable<T> 端点?

Is there a way to get Swashbuckle to add OData parameters to Web API 2 IQueryable<T> endpoint?

我有一个支持 OData 查询的 ASP.Net Web API 2 端点。是这样的:

[HttpGet, Route("")]
public IQueryable<Thing> Get()
{
    return _thingsRepository.Query();
}

OData $filter 参数等非常有用。我只希望它们能像实际 OData 控制器一样出现在 Swagger 中。

我正在使用 Swashbuckle.OData ...但我真的不确定在这种情况下它是否能给我带来任何好处。

事实证明,使用 IOperationFilter 使用 SwashBuckle 向 Swagger 添加您想要的任何参数非常容易。您可以使用 c.OperationFilter<ODataParametersSwaggerDefinition>(); 在您的 Swagger 配置中添加它。我创建了一个向 API:

中的所有 IQueryable 端点添加一些 OData 参数的方法
/// <summary>
///     Add the supported odata parameters for IQueryable endpoints.
/// </summary>
public class ODataParametersSwaggerDefinition : IOperationFilter
{
    private static readonly Type QueryableType = typeof(IQueryable);

    /// <summary>
    ///     Apply the filter to the operation.
    /// </summary>
    /// <param name="operation">The API operation to check.</param>
    /// <param name="schemaRegistry">The swagger schema registry.</param>
    /// <param name="apiDescription">The description of the api method.</param>
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var responseType = apiDescription.ResponseType();

        if (responseType.GetInterfaces().Any(i => i == QueryableType))
        {
            operation.parameters.Add(new Parameter
            {
                name = "$filter",
                description = "Filter the results using OData syntax.",
                required = false,
                type = "string",
                @in = "query"
            });

            operation.parameters.Add(new Parameter
            {
                name = "$orderby",
                description = "Order the results using OData syntax.",
                required = false,
                type = "string",
                @in = "query"
            });

            operation.parameters.Add(new Parameter
            {
                name = "$skip",
                description = "The number of results to skip.",
                required = false,
                type = "integer",
                @in = "query"
            });

            operation.parameters.Add(new Parameter
            {
                name = "$top",
                description = "The number of results to return.",
                required = false,
                type = "integer",
                @in = "query"
            });

            operation.parameters.Add(new Parameter
            {
                name = "$count",
                description = "Return the total count.",
                required = false,
                type = "boolean",
                @in = "query"
            });
        }
    }
}

直接窃取已接受的答案,但要进行额外的空值检查。

这不会触及您现有的 OData 可查询端点,这些端点可能已经定义了 parameters,但只会增强您的可查询 ApiController 端点。

已更新 AspNetCore / Net 5、Net 6 答案

using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.Linq;

namespace My.Corporation.Api
{
    /// <summary>
    /// Help your swagger show OData query options with example pre-fills
    /// </summary>
    public class ODataParametersSwaggerDefinition : IOperationFilter
    {
        private static readonly Type QueryableType = typeof(IQueryable);
        private static readonly OpenApiSchema stringSchema = new OpenApiSchema { Type = "string" };
        private static readonly OpenApiSchema intSchema = new OpenApiSchema { Type = "integer" };

        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            var hasNoParams = (operation.Parameters == null || operation.Parameters.Count == 0);
            var isQueryable = context.MethodInfo.ReturnType.GetInterfaces().Any(i => i == QueryableType);

            if (hasNoParams && isQueryable)
            {

                operation.Parameters = new List<OpenApiParameter>();
                operation.Parameters.Add(new OpenApiParameter
                {
                    Name = "$filter",
                    Description = "Filter the results using OData syntax.",
                    Example = OpenApiAnyFactory.CreateFor(stringSchema,"ProductName eq 'YOGURT'"),
                    Required = false,
                    In = ParameterLocation.Query,
                    Schema = stringSchema

                });
                operation.Parameters.Add(new OpenApiParameter
                {
                    Name = "$select",
                    Description = "Trim the fields returned using OData syntax",
                    Example = OpenApiAnyFactory.CreateFor(stringSchema, "Id,ProductName"),
                    Required = false,
                    In = ParameterLocation.Query,
                    Schema = new OpenApiSchema { Type = "string" }
                });
                operation.Parameters.Add(new OpenApiParameter
                {
                    Name = "$orderby",
                    Description = "Order the results using OData syntax.",
                    Example = OpenApiAnyFactory.CreateFor(stringSchema, "Price,ProductName ASC"),
                    Required = false,
                    In = ParameterLocation.Query,
                    Schema = new OpenApiSchema { Type = "string" }
                });
                operation.Parameters.Add(new OpenApiParameter
                {
                    Name = "$skip",
                    Description = "The number of results to skip.",
                    Example = OpenApiAnyFactory.CreateFor(intSchema, 100),
                    Required = false,
                    In = ParameterLocation.Query,
                    Schema = intSchema
                });
                operation.Parameters.Add(new OpenApiParameter
                {
                    Name = "$top",
                    Description = "The number of results to return.",
                    Example = OpenApiAnyFactory.CreateFor(intSchema, 50),
                    Required = false,
                    In = ParameterLocation.Query,
                    Schema = intSchema
                });
            }
        }
    }
}

原始 WebAPI 2 答案

 /// <summary>
 /// Adds the supported odata parameters for IQueryable endpoints 
 /// ONLY if no parameters are defined already.
 /// </summary>
 public class ODataParametersSwaggerDefinition : IOperationFilter
 {
    private static readonly Type QueryableType = typeof(IQueryable);

    /// <summary>
    /// Apply the filter to the operation.
    /// </summary>
    /// <param name="operation">The API operation to check.</param>
    /// <param name="schemaRegistry">The swagger schema registry.</param>
    /// <param name="apiDescription">The description of the api method.</param>
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var responseType = apiDescription.ResponseType();

        if (responseType.GetInterfaces().Any(i => i == QueryableType))
        {
            if (operation.parameters == null)
            {
                operation.parameters = new List<Parameter>();

                operation.parameters.Add(new Parameter
                {
                    name = "$filter",
                    description = "Filter the results using OData syntax.",
                    required = false,
                    type = "string",
                    @in = "query"
                });

                operation.parameters.Add(new Parameter
                {
                    name = "$orderby",
                    description = "Order the results using OData syntax.",
                    required = false,
                    type = "string",
                    @in = "query"
                });

                operation.parameters.Add(new Parameter
                {
                    name = "$skip",
                    description = "The number of results to skip.",
                    required = false,
                    type = "integer",
                    @in = "query"
                });

                operation.parameters.Add(new Parameter
                {
                    name = "$top",
                    description = "The number of results to return.",
                    required = false,
                    type = "integer",
                    @in = "query"
                });

                operation.parameters.Add(new Parameter
                {
                    name = "$count",
                    description = "Return the total count.",
                    required = false,
                    type = "boolean",
                    @in = "query"
                });
            }
            
        }
    }
}

使用 Swashbuckle.AspNetCore v. 6.2.3 看起来像这样:

public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
    if (context.MethodInfo.ReturnType.GetInterfaces().Any(i => i == typeof(IQueryable)))
    {
        operation.Parameters.Add(new OpenApiParameter()
        {
            Name = "$filter",
            Description = "My $filter filter",
            Required = false,
            In = ParameterLocation.Query,                
        });

        operation.Parameters.Add(new OpenApiParameter()
        {
            Name = "$top",
            Description = "My $top filter",
            Required = false,
            In = ParameterLocation.Query,           
        });

        operation.Parameters.Add(new OpenApiParameter()
        {
            Name = "$expand",
            Description = "My $top filter",
            Required = false,
            In = ParameterLocation.Query,                
        });
    }
}