按控制器名称排序(Swagger),NET核心

sirbozc5  于 2023-04-30  发布在  其他
关注(0)|答案(3)|浏览(257)

我正试图弄清楚如何以及是否可能在中显示按控制器名称排序的API组(控制器)。网络核心 Swagger 。有很多关于如何对动作进行排序的信息,但我正在努力弄清楚如何按组(控制器名称)进行排序,有没有人对此有任何想法?

juzqafwq

juzqafwq1#

这是可能的,看这里:
http://swagger-net-test.azurewebsites.net/swagger/docs/V1
到最后,你应该看到:

"tags": [
    {
      "name": "ActionFilter"
    },
    {
      "name": "ApiExplorer"
    },

这就是控制UI上的最终顺序的内容。
希望你有最新的swagger-ui版本的swashbuckle你。..
但是顺序应该不重要,最新的有一个非常方便的过滤器,可以快速搜索一些东西:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=Image

tp5buhyn

tp5buhyn2#

试试这个:

services.AddSwaggerGen(c =>
{
   c.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.HttpMethod}");
   ....
}
nx7onnlm

nx7onnlm3#

这是一篇讨论排序控制器"Swashbuckle Custom Ordering of Controllers"的文章(文章如下),一个GitHub存储库,其中包含解决方案/tgolla/TGolla.Swashbuckle.AspNetCore的示例和实现解决方案TGolla.Swashbuckle.AspNetCore的NuGet包。

Swashbuckle控制器自定义排序

本文改编自Rob Janssen(RobIII)在2018年撰写的一篇关于自定义控制器在Swagger UI by Swashbuckle中显示的顺序的文章。它解决了OrderActionGroupsBy方法的贬值问题,当在ASP中使用AddSwaggerGenUseSwaggerUseSwaggerUI时,该方法不再可用。NET API项目通过在配置中使用AddSwaggerGen方法OrderActionsBy来实现。
默认情况下,使用Swashbuckle生成Swagger文档时,控制器按字母顺序排序。但是,在某些情况下,按字母顺序排列并不适合您的文档。例如,考虑一个API项目,其中每个控制器代表一种类型的剧院(竞技场,Black Box,Prescenium,Thrust)。与其按字母顺序排序,不如按戏剧教学时通常的排序方式(舞台、推力、竞技场、黑匣子)排序。
在本文中,我们将研究如何使用自定义属性来影响控制器在Swagger文档中的显示顺序。同样,大部分代码都改编自Rob Janssen的文章Swashbuckle Custom Ordering of Controllers。完整的示例可以在GitHub存储库TGolla.Swashbuckle.AspNetCore中找到,您可以通过安装NuGet包TGolla.Swashbuckle.AspNetCore开始在项目中使用SwaggerControllerOrder属性。

工作原理

当使用Swashbuckle工具将Swagger添加到您的项目时,您可以使用OrderActionsBy更改控制器的排序顺序,甚至可以更改AddSwaggerGen方法的配置信息中的各个API。在OrderActionsBy中,您可以使用lambda表达式来更改用于对API调用进行排序(分组)的排序键字符串。
默认情况下,排序键设置为第一个标签,默认情况下,设置为控制器的名称,因此控制器按字母顺序排序,之后API调用通常按相对路径排序。下面的代码表示与使用OrderActionsBy定义的预定义默认值等效的代码。

OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}");

在我们的示例中,我们将使用order属性注解每个控制器类,创建一个使用反射收集所有控制器列表沿着order属性值的类,并在类中公开一个方法,该方法在排序键字符串中返回order和控制器名称。这个方法可以在OrderActionsBy lambda表达式中使用。

OrderActionsBy((apiDesc) => $"{swaggerControllerOrder.SortKey(apiDesc.ActionDescriptor.RouteValues["controller"])}")

我们还将通过添加额外的信息(如HTTP方法(GET、POST、PUT、DELETE、...)来影响各个API调用的顺序。

代码

开始我们需要定义一个属性类(SwaggerControllerOrderAttribute.cs)。

namespace TGolla.Swashbuckle.AspNetCore.SwaggerGen
{
    /// <summary>
    /// Annotates a controller with a Swagger sorting order that is used when generating 
    /// the Swagger documentation to order the controllers in a specific desired order.
    /// </summary>
    /// <remarks>
    /// Ref: http://blog.robiii.nl/2018/08/swashbuckle-custom-ordering-of.html modified for AddSwaggerGen() extension OrderActionsBy().
    /// https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md#change-operation-sort-order-eg-for-ui-sorting
    /// </remarks>
    public class SwaggerControllerOrderAttribute : Attribute
    {
        /// <summary>
        /// Gets the sorting order of the controller.
        /// </summary>
        public uint Order { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="SwaggerControllerOrderAttribute"/> class.
        /// </summary>
        /// <param name="order">Sets the sorting order of the controller.</param>
        public SwaggerControllerOrderAttribute(uint order)
        {
            Order = order;
        }
    }
}

这个类允许我们用属性[SwaggerControllerOrder(x)]注解控制器,其中 x 是一个整数值,表示按升序排列的顺序。默认情况下,未使用该属性注解的控制器将被指定顺序值4294967295。默认情况下或使用属性分配的具有相同顺序的控制器然后按字母顺序排序,但是当设置OrderActionBy方法时可以更改。在我们的例子中,我们将希望注解我们的每个控制器如下。

[ApiController]
    [Route("theater/[controller]")]
    [SwaggerControllerOrder(1)]
    public class ProsceniumController : ControllerBase
    {
        // ...
    }

    [ApiController]
    [Route("theater/[controller]")]
    [SwaggerControllerOrder(2)]
    public class ThrustController: ControllerBase
    {
        // ...
    }

    [ApiController]
    [Route("theater/[controller]")]
    [SwaggerControllerOrder(3)]
    public class ArenaController: ControllerBase
    {
        // ...
    }

    [ApiController]
    [Route("theater/[controller]")]
    [SwaggerControllerOrder(4)]
    public class BlackBoxController: ControllerBase
    {
        // ...
    }

接下来,我们需要创建一个类,它将收集具有SwaggerControllerOrder属性值的控制器列表。

using System.Reflection;

namespace TGolla.Swashbuckle.AspNetCore.SwaggerGen
{
    /// <summary>
    /// Class for determining controller sort keys based on the SwaggerControllerOrder attribute.
    /// </summary>
    /// <typeparam name="T">The type controllers should implement.  By default this would normally be ControllerBase or Controller
    /// unless you have derived your own specific api controller class.</typeparam>
    /// <remarks>
    /// Ref: http://blog.robiii.nl/2018/08/swashbuckle-custom-ordering-of.html modified for AddSwaggerGen() extension OrderActionsBy().
    /// https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md#change-operation-sort-order-eg-for-ui-sorting
    /// </remarks>
    public class SwaggerControllerOrder<T>
    {
        private readonly Dictionary<string, uint> orders;   // Our lookup table which contains controllername -> sortorder pairs.

        /// <summary>
        /// Initializes a new instance of the <see cref="SwaggerControllerOrder&lt;TargetException&gt;"/> class.
        /// </summary>
        /// <param name="assembly">The assembly to scan for for classes implementing <typeparamref name="T"/>.</param>
        public SwaggerControllerOrder(Assembly assembly)
            : this(GetFromAssembly<T>(assembly)) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="SwaggerControllerOrder&lt;TargetException&gt;"/> class.
        /// </summary>
        /// <param name="controllers">
        /// The controllers to scan for a <see cref="SwaggerControllerOrderAttribute"/> to determine the sortorder.
        /// </param>
        public SwaggerControllerOrder(IEnumerable<Type> controllers)
        {
            // Initialize our dictionary; scan the given controllers for our custom attribute, read the Order property
            // from the attribute and store it as controllername -> sorderorder pair in the (case-insensitive) dicationary.
            orders = new Dictionary<string, uint>(
                controllers.Where(c => c.GetCustomAttributes<SwaggerControllerOrderAttribute>().Any())
                .Select(c => new { Name = ResolveControllerName(c.Name), c.GetCustomAttribute<SwaggerControllerOrderAttribute>().Order })
                .ToDictionary(v => v.Name, v => v.Order), StringComparer.OrdinalIgnoreCase);
        }

        /// <summary>
        /// Returns all <typeparamref name="TController"/>'s from the given assembly.
        /// </summary>
        /// <typeparam name="TController">The type classes must implement to be regarded a controller.</typeparam>
        /// <param name="assembly">The assembly to scan for given <typeparamref name="TController"/>s.</param>
        /// <returns>Returns all types implementing <typeparamref name="TController"/>.</returns>
        public static IEnumerable<Type> GetFromAssembly<TController>(Assembly assembly)
        {
            return assembly.GetTypes().Where(c => typeof(TController).IsAssignableFrom(c));
        }

        /// <summary>
        /// Determines the 'friendly' name of the controller by stripping the (by convention) "Controller" suffix
        /// from the name. If there's a built-in way to do this in .Net then I'd love to hear about it!
        /// </summary>
        /// <param name="name">The name of the controller.</param>
        /// <returns>The friendly name of the controller.</returns>
        private static string ResolveControllerName(string name)
        {
            const string suffix = "Controller"; // We want to strip "Controller" from "FooController"

            // Ensure name ends with suffix (case-insensitive)
            if (name.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
                // Return name with suffix stripped
                return name.Substring(0, name.Length - suffix.Length);
            // Suffix not found, return name as-is
            return name;
        }

        /// <summary>
        /// Returns the unsigned integer sort order value.  
        /// </summary>
        /// <param name="controller">The controller name.</param>
        /// <returns>The unsigned integer sort order value.</returns>
        private uint Order(string controller)
        {
            // Try to get the sort order value from our lookup; if none is found, assume uint.MaxValue.
            if (!orders.TryGetValue(controller, out uint order))
                order = uint.MaxValue;

            return order;
        }

        /// <summary>
        /// Returns an order key based on a the SwaggerControllerOrderAttribute for use with OrderActionsBy.
        /// </summary>
        /// <param name="controller">The controller name.</param>
        /// <returns>A zero padded 32-bit unsigned integer.</returns>
        public string OrderKey(string controller)
        {
            return Order(controller).ToString("D10");
        }

        /// <summary>
        /// Returns a sort key based on a the SwaggerControllerOrderAttribute for use with OrderActionsBy.
        /// </summary>
        /// <param name="controller">The controller name.</param>
        /// <returns>A zero padded 32-bit unsigned integer combined with the controller's name.</returns>
        public string SortKey(string controller)
        {
            return $"{OrderKey(controller)}_{controller}";
        }
    }
}

要使用该类,我们在Program.cs文件的ConfigureServices方法中定义一个示例。

SwaggerControllerOrder<ControllerBase> swaggerControllerOrder = new SwaggerControllerOrder<ControllerBase>(Assembly.GetEntryAssembly());

示例化后,SwaggerControllerOrder类在程序集中搜索ControllerBase类型的控制器,并构建控制器名称及其关联的SwaggerControllerOrder属性值的字典列表。那些没有SwaggerControllerOrder属性的不放在列表中,并将被分配默认的最大值4294967295。
通过示例化类,我们现在可以在添加Swagger生成服务时使用它,方法是在Program.cs文件中的AddSwaggerGen配置中添加OrderActionsBy方法调用。

builder.Services.AddSwaggerGen(c =>
{
    c.OrderActionsBy((apiDesc) => $"{swaggerControllerOrder.SortKey(apiDesc.ActionDescriptor.RouteValues["controller"])}");
});

更进一步

如前所述,您还可以使用OrderActionsBy来影响单个API调用的顺序。
例如,如果要添加组名称(即例如[ApiExplorerSettings(GroupName = "Hidden")])对某些API调用隐藏这些API调用,但您不希望对某些用户单独分组(显示),您可以将相对路径添加到排序关键字字符串。

c.OrderActionsBy((apiDesc) => $"{swaggerControllerOrder.SortKey(apiDesc.ActionDescriptor.RouteValues["controller"])}_{apiDesc.RelativePath}");

如果你想看到你的API调用按HTTP方法的字母顺序排列,你可以使用下面的lambda表达式。

c.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.HttpMethod}");

如果你想看到API调用按HTTP方法排序,你可以按照特定的顺序添加下面的排序顺序数组。..

string[] methodsOrder = new string[5] { "get", "post", "put", "patch", "delete", "options", "trace" };

使用下面的lambda表达式。

c.OrderActionsBy(apiDesc => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{Array.IndexOf(methodsOrder, apiDesc.HttpMethod.ToLower())}");

可能性是无限的,甚至可以通过添加一个属性来注解IActionResult(API)调用,来更进一步。
参考: www.example.com

相关问题