2018-08-16

Swashbuckle custom ordering of controllers

By default, Swagger (or rather: Swashbuckle) orders the documentation of controllers by controllername. So if you have a FooController, BarController and BazController they will be ordered as Bar, Baz, Foo in the swagger UI. Let's change that.

Swashbuckle offers an option to order the "ActionGroups" as these are called using the OrderActionGroupsBy method. However, this method expects an IComparer<string> which doesn't offer much flexibility by itself other than ordering alphabetically in ascending or descending order. There's not much more creative we can get with this rather simple interface. Or... can we?

I was hoping to be able to add some attributes to my controllers to set the order manually but couldn't find any possibilities to do so, so I implemented it. The idea is simple: we'd like to have an attribute we can slap on a controller to influence the order the controller should be documented in. That's about it.

So, first we will need to implement our attribute:

Now we can annotate our controllers with this attribute:

Now, we need a class that implements IComparer<string> but somehow, magically, knows how to get to the attributes on the controllers. The plan is as follows: we create a dictionary with controllersnames and the desired order as key/value pairs. We keep a reference to this dictionary around. When the Compare(string x, string y) method is called we first check our dictionary to see if we have a specific order for controller X and/or controller Y and compare them. If the orders are equal we fall back to comparing the names; in all other cases we simply return the result of the comparison of the order values. The question is: how do we populate this dictionary?

Well, there are 2 options actually. First, in our SwaggerControllerOrderComparer, as we'll call it, we could use some reflection to figure out all controllers in the assembly. That works quite nicely and is the simplest to use. However, another option would be that the calling code provides the controllers we want to sort using this method so we have better control of how the assembly is scanned for controllers. This resulted in a SwaggerControllerOrderComparer with two constructors; one which accepts the assembly to scan for controllers and one which accepts an IEnumerable<Type> that can be used to pass in specific controllers.

With that out of the way, all we need to do is tell Swashbuckle to use our comparer:

And that's it! Enjoy your custom ordered controllers!

8 comments:

  1. I have followed the same but not working

    ReplyDelete
    Replies
    1. At least define "Not working". I can't help you with this bare information.

      Delete
    2. I am using DocumentFilter showinswaggerattribute to show the required API controllers in swagger.
      After that i am using the "c.OrderActionGroupsBy(new SwaggerControllerOrderComparer(Assembly.GetExecutingAssembly()))"
      Will it be causing any problem?

      Delete
    3. With Latest Swagger code, Change the Controller File Name as per order of controller you want. And Swagger UI will list Controller as per File Name. ( only exception i saw Health check Controller. It always display at bottom in Swagger UI )

      Delete
  2. Hi Rob,
    If i use the DocumentFilter option this order is not working.
    If i dont use the documentFilter then c.OrderActionGroupsBy working fine and getting cutom order.
    Please help me how to use c.OrderActionGroupsBy along with c.DocumentFilter

    ReplyDelete
  3. This not working seems like it is a bug in swagger-ui. The documentation file is generated in the correct order but is always displayed alphabetically in swagger-ui. I wrote a reversing comparer and it had the same result. Or should I configure something in swagger-ui that I'm not aware of?

    ReplyDelete
  4. I'm having this error on the WebApiConfig file:

    'HttpConfiguration' does not contain a definition for 'EnableSwagger' and no accessible extension method 'EnableSwagger' accepting a first argument of type 'HttpConfiguration' could be found (are you missing a using directive or an assembly reference?).

    I'm creating all the files on the root of the project.

    ReplyDelete
    Replies
    1. This a very old post; if you're using the latest Swashbuckle it's likely this won't work anymore without some changes.

      Delete