2 minute read

Problem

When you are a library author of controllers and the user has the ability to use their desired controllers from your library, then your customer will somewhat wonder that not only the desired but also the unsdesired controllers are showing up in the API documentation of Swagger/OpenAPI provided by Swashbuckle or NSwag. So, what’s the reason?

The above conclusion is drawn based on the fact that many people are adding controllers from a library by simply adding an assembly via ApplicationPart to the ApplicationPartManager:

1
2
3
4
5
6
public void ConfigureServices(IServiceCollection services)
{
    services
        .AddControllers()
        .AddApplicationPart(typeof(LibraryAuthoredController).Assembly);
}

This will announce all controllers as available what we don’t want. These controllers are also used for the “API exploration” in ApiExplorer. The result is unwanted controllers in the API documentation of Swagger/OpenAPI.

Info: In the scope of this post I won’t elaborate how ApplicationParts works. Therefore I can refer to an excellent post from Andrew Lock which you can read here: When ASP.NET Core can’t find your controller: debugging application parts.

Solution

The solution sounds trivial but effective:

  1. Make the controller generic
  2. Register the controller manually via ApplicationPart

Let’s do it step by step. Let’s assume we have a BearerSignInController that is normally not generic.

1
2
3
[ApiController]
public class BearerSignInController : Controller
{ }

Make the controller now generic. In this case I use a very restrictive constraint where TNotUseful has to implement ISingleton and it’s implementation Singleton is sealed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// This interface has no purpose. It just serves
/// as helper interface being usable as constraint.
/// </summary>
public interface ISingleton
{ }

public sealed class Singleton : ISingleton
{
    public static readonly Singleton Default = new Singleton();

    private Singleton() { }
}

[ApiController]
public class BearerSignInController<TNotUseful> : Controller
        where TNotUseful : ISingleton
{ }

Volià, our controller is now generic and no one should miss its intention.

Now we need to register the controller manually. Therefore I will use my class TypesProvidingApplicationPart. It is part of the NuGet package Teronis.AspNetCore.Mvc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// <summary>
/// Adds <see cref="BearerSignInController"/> to <see cref="IMvcBuilder"/>.
/// </summary>
/// <param name="mvcBuilder"></param>
/// <param name="applicationPartName">Sets <see cref="ApplicationPart.Name". If null the name is <see cref="TypesProvidingApplicationPart"/>.</param>
/// <returns></returns>
public static IMvcBuilder AddBearerSignInControllers(this IMvcBuilder mvcBuilder, string? applicationPartName)
{
    mvcBuilder.ConfigureApplicationPartManager(setup => {
        var controllerType = typeof(BearerSignInController<Singleton>).GetTypeInfo();
        var typesProvider = TypesProvidingApplicationPart.Create(applicationPartName, controllerType);
        setup.ApplicationParts.Add(typesProvider);
    });

    return mvcBuilder;
}

In the above code sequence you can see that we are creating an application part that will announce our controller as available. No more magic.

This is my first post; thanks for reading it. 🙂