Custom ApiExplorer with Namespace based ApiControllers
Asked Answered
P

3

7

I'm trying to add API documentation at my backend system. Default ApiExplorer and Help page worked absolutely great until the moment I introduced versions to my API Controllers.

In order to add versions I created sub folders under the Controllers folder:

  • v1
  • v2
  • v3

and have version based API Controllers there. In order to have my API discoverable I have to rewrite DefaultHttpControllerSelector to take into account namespaces provided by any client and map them to right controllers:

This have broken my default ApiExplorer and the following property returns ZERO api descriptions

Configuration.Services.GetApiExplorer().ApiDescriptions

How can I customize existent ApiExplorer and help him to find my Api Controllers and not to rewrite whole ApiExplorer implementation. I really need just to show where to find my API Controllers.

Please advise.

Paraffinic answered 10/2, 2014 at 23:41 Comment(2)
I'm trying to do the exact same thing... Any chance you figured out how to implement this?Engorge
I answered your question belowParaffinic
P
6

Turned out that there is nothing to do with ApiExplorer. As instead you should modify your namespace based controller selector:

NamespaceHttpControllerSelector : DefaultHttpControllerSelector
{
//...
    public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping() 
    {
        var mapping = base.GetControllerMapping();
        mapping["User"] = new HttpControllerDescriptor
        {
            Configuration = _httpConfig,
            ControllerName = "User",
            ControllerType = typeof(UserController)
        };
        //...
        return mapping;
    }
    //...  }

That is. After that default ApiExplorer will find you controllers and fetch all the actions.

Paraffinic answered 24/2, 2014 at 16:50 Comment(0)
R
8

I will show you a way to do that. This code is just for learning. Here I not talking about design and best practices, so feel free to change anything you want.

Well, You must follow the next steps:

1) Create a custom ApiExplorer:

public class MyApiExplorer: ApiExplorer
{
    private readonly string _version;

    public MyApiExplorer(string version) : base(GlobalConfiguration.Configuration)
    {
        _version = version != null ? version.ToUpperInvariant() : "V1";

        foreach(var apiDescription in ApiDescriptions)
        {
            apiDescription.RelativePath = apiDescription.RelativePath.Replace("{version}", _version);
        }

    }

    public override bool ShouldExploreController(string controllerVariableValue, HttpControllerDescriptor controllerDescriptor,
        IHttpRoute route)
    {
        return controllerDescriptor.ControllerType.FullName.Contains(_version);
    }

}

a) In the constructor _version will be converted to upperCase (just in case it will be passed as lowerCase) but if it is null then it will take V1 as default. Then change relative path to show specific version instead of {version}.

b) ShouldExploreController (in short words) decide if specific controller is taken to show in documentation. In this case we will only show controllers that its type full name contains choosed version.

2) Go to HelpController class and change Index method like this:

public ActionResult Index(string version)
{
    //...

    Configuration.Services.Replace(typeof(IApiExplorer), new MyApiExplorer(version));

    return View(Configuration.Services.GetApiExplorer().ApiDescriptions);
}

We are replacing current ApiExplorer by our own in order to be returned when call to Configuration.Services.GetApiExplorer()

Now you can use this .../help?version=v1 or .../help?version=v2 or .../help?version=v3 and you will get specific api controller documentation.

Rebound answered 7/4, 2015 at 23:18 Comment(1)
Wouldn't you be better off putting Configuration.Services.Replace in a static ctor for HelpController? Otherwise, this will be called every single time.Delate
P
6

Turned out that there is nothing to do with ApiExplorer. As instead you should modify your namespace based controller selector:

NamespaceHttpControllerSelector : DefaultHttpControllerSelector
{
//...
    public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping() 
    {
        var mapping = base.GetControllerMapping();
        mapping["User"] = new HttpControllerDescriptor
        {
            Configuration = _httpConfig,
            ControllerName = "User",
            ControllerType = typeof(UserController)
        };
        //...
        return mapping;
    }
    //...  }

That is. After that default ApiExplorer will find you controllers and fetch all the actions.

Paraffinic answered 24/2, 2014 at 16:50 Comment(0)
C
1

I faced a similar problem recently, and solved mine with this: 2 LOC:

public class VersionControllerSelector : IHttpControllerSelector

to

public class VersionControllerSelector : DefaultHttpControllerSelector

...and...

public VersionControllerSelector(HttpConfiguration config)

to

public VersionControllerSelector(HttpConfiguration config) : base(config)
Coextend answered 15/4, 2014 at 17:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.