I'd like to prepare my .NET Core Web API project so that multiple versions of the API can be managed and documented, according to the REST services standards.
I'm using .NET Core 2.1 with NSwag (v11.18.2). I also installed the Microsoft.AspNetCore.Mvc.Versioning NuGet package.
I already searched with Google for some configuration examples, but the only useful link I found is this.
I'm now able to get Swagger pages for both API versions but with some problems:
Please note that none of the last config settings (Title, Description, etc.) takes effect on any of the 2 routes. It only works if I add them on each of the individual configuration. So I'd also like to know if it possible to avoid that, since the general configuration of the API can be version indipendent (title, description and so on...).
Since the issue with NSwag and Microsoft API Versioning package discussed in the above link, was opened 2-3 months (and NSwag versions too) ago, I'd like to know if it is now truly fixed and in this case, which is the right configuration to set.
Although the version is explicit in the configuration of the controllers, it is still required as a mandatory input parameter of the controller methods and of course I don't want that! See image:
So, my actual configuration, by following that example, is looking like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddApiVersioning(options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ReportApiVersions = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSwaggerWithApiExplorer(config =>
{
config.GeneratorSettings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "1.0" };
config.SwaggerRoute = "v1.0.json";
});
app.UseSwaggerWithApiExplorer(config =>
{
config.GeneratorSettings.OperationProcessors.TryGet<ApiVersionProcessor>().IncludedVersions = new[] { "2.0" };
config.SwaggerRoute = "v2.0.json";
});
app.UseSwaggerUi3(typeof(Startup).GetTypeInfo().Assembly, config =>
{
config.SwaggerRoutes.Add(new SwaggerUi3Route("v1.0", "/v1.0.json"));
config.SwaggerRoutes.Add(new SwaggerUi3Route("v2.0", "/v2.0.json"));
config.GeneratorSettings.Title = "My API";
config.GeneratorSettings.Description = "API functionalities.";
config.GeneratorSettings.DefaultUrlTemplate = "{v:apiVersion}/{controller}/{action}/{id?}";
config.GeneratorSettings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase
});
}
And these are my actual controllers:
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]/[action]")]
[SwaggerTag("Test1", Description = "Core operations on machines (v1.0).")]
public class MachinesController : Controller
{
[HttpGet("{id}")]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<ActionResult<Machine>> Get(int id)
{
return await ...
}
}
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]/[action]")]
[SwaggerTag("Test2", Description = "Core operations on machines (v2.0).")]
public class MachinesController : Controller
{
[HttpGet("{id}")]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<ActionResult<Machine>> Get(int id)
{
return await ...
}
}
They are ignored in the middleware because they are inferred from the settings or do not apply for api explorer (template). However title and description should work...
Please create an issue with the specific issue and a repro, also check out the existing tests in the repo
Fixed with v11.18.3
I believe starting in NSwag 12.0.0, there is significantly improved support for the API Explorer. It's important that the complementary API Explorer package for API versioning is also referenced so that the proper information is provided to NSwag.
The Swagger sample application provided by API Versioning uses Swashbuckle, but the setup will be very similar to NSwag. You can use the IApiVersionDescriptionProvider service to enumerate all of the API versions defined in your application. That should significantly simplify your NSwag configuration.
You're versioning by URL segment; therefore, to address Problem 3 you simply need to configure the API Explorer a la:
services.AddVersionedApiExplorer( options => options.SubstituteApiVersionInUrl = true );
This will replace the {version} route parameter in the route template with the corresponding API version value and remove the API version parameter from the API description.
Related
Why does adding versioning to a webApi project removes number from the controller path name?
Replication steps :
Create a fresh .net6 project. And rename WeatherForecastController to WeatherForecast2Controller.
Run app and call https://localhost:x/WeatherForecast2 (where x is your port)
Observe valid/expected results
Add the below code in program.cs/startup
services.AddApiVersioning(config =>
{
config.AssumeDefaultVersionWhenUnspecified = true;
config.DefaultApiVersion = new ApiVersion(1, 0);
config.ReportApiVersions = true;
config.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader("version"),
new HeaderApiVersionReader("x-version"));
config.UseApiBehavior = false;
});
Run app and call https://localhost:x/WeatherForecast2 (where x is your port).
Step 5 will return a 404 not found error.
However if you call https://localhost:x/WeatherForecast. It will work.
So why does adding versioning, change the url path?
It's not entirely clear if you are using <= 5.0 or 6.0+.
History
The reason this behavior happens is because the only logical way to group controllers together is by their names. A controller name, therefore, becomes very important. This is problematic in code because two or more controllers in the same namespace cannot have the same type name. The assumption and long-defined convention has been to allow ASP.NET to remove the Controller suffix and then remove any remaining numbers. This allows ValuesController, Values2Controller and Values3Controller to all map to the logical controller name Values by default. In most cases, that's probably want someone wants. If API Versioning doesn't do this, then there is no way to collate all API versions together for an API.
Contrary to popular belief, route templates are not considered for grouping controllers (e.g. APIs). There is too much ambiguity as to how a template can map to code. Take the simplest example of two different versions of the same API with different route templates: V1 = values/{id}, V2 = values/{id:int}. These are semantically equivalent, but not the same. API Versioning does not try to understand what the route template means nor compare their equivalence. It can easily get a lot more complicated; especially, for overlapping route templates. For example, should order/{oid}/customer/{cid} be part of the Orders API or the Customer API? Only the service author knows for sure.
Regression
In the 5.0 release, a regression was accidentally introduced due to an over-optimization. The controller name is used in two places: the actual name of the controller and the name used to group controllers. It seems reasonable they'd be the same and why normalize (e.g. trim suffixes) more than necessary? It seemed like a good idea, but it caused unexpected behavior - such as this one. There are also legitimate reasons to have a number in the name of a controller; for example, S3Controller.
Fix
In library versions <= 5.0, developers had no control over the behavior of how names were normalized. In 5.1 and 6.0+, this is now exposed via the IControllerNameConvention service, which has two methods: one for normalizing the controller name and one for normalizing the group name. The following implementations are provided out of the box as properties on ControllerNameConvention:
Default: The default, out-of-the-box conventions
Original: The original names without any normalization (could result in the wrong behavior)
Grouped: The group name is normalized, but the controller name is unmodified
If none of those work for you, then you can create your own custom convention. In 5.1 this is wired up via ApiVersioningOptions.ControllerNameConvention, while in 6.0+ IControllerNameConvention is a transient service in the DI container.
Workaround
There are two ways you can workaround the problem using the current version you are leveraging:
Explicit Route Template
If you omit using the [controller] token, the routing problem will be resolved; for example, api/weatherforecast. You appear to have already discovered this.
Explicit Controller Name
The controller name is derived from a convention, even without API Versioning. It was understood this behavior could be a problem so API Versioning provides a way to explicit set it with the ControllerNameAttribute.
[ControllerName("WeatherForecast")]
[Route("api/[controller]")] // ← expands to 'api/WeatherForecast'
public class WeatherForecast2Controller : ControllerBase { }
Edge Case
This will solve the routing issues, but it will not fix the controller name issue. That should only matter if you are planning on documenting your API with OpenAPI (formerly Swagger). For example, S3Controller will simply show up as S, even though the route might be api/s3.
I think that your controller may have issues, as I used your AddApiVersioning code and it works for me. To avoid the conflicting action names, you can two different controllers.
ApiController
namespace WebApiVersioningApp.Controllers;
[ApiVersion("1.0")]
[ApiVersion("2.0")]
[ApiController]
[Route("api/Version2")]
public class Version2Controller : ControllerBase
{
[MapToApiVersion("1.0")]
[HttpGet(Name = "GetWeatherForecastV1")]
public string GetV1()
{
return "Version 1";
}
[MapToApiVersion("2.0")]
[HttpGet(Name = "GetWeatherForecastV2")]
public string GetV2()
{
return "Version 2";
}
}
Program:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First())
);
// Versioning setup
builder.Services.AddApiVersioning(o =>
{
o.ReportApiVersions = true;
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
o.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader("version"),
new HeaderApiVersionReader("x-version"));
o.UseApiBehavior = false;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Hope this works for you.
I am trying to get versioning to work via the request header in .NET 5 using Microsoft.AspNetCore.OData v8.0.1 and versioning to work with SwaggerUI.
Pre-v8, you used to be able to use
services.AddODataApiExplorer(...);
which would enable DependencyInjection for the Startup's Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider) {
...
app.UseSwagger();
app.UseSwaggerUI(options => {
// build a swagger endpoint for each discovered API version
foreach (var description in provider.ApiVersionDescriptions) {
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
options.ShowExtensions();
});
...
}
OData8 appears to not handle this out of the box and the best documentation I can find on versioning only handles URL Segment and hints at Query String versioning. I'd prefer to save characters in the URL if I can which is why I want to go with Request Header versioning.
Any guidance is appreciated.
So, instead of using
services.AddODataApiExplorer(...);
you can use
services.AddApiVersioning(...);
It won't (currently) get picked up properly by the SwaggerUI but it will generate the client code properly and respond to versioning by routing to the target versioned controller.
I was able to find the most clear and concise documentation on versioning in the article, 'REST API versioning with ASP.NET Core'
//WHEN VERSIONING BY: query string, header, or media type
endpoints.MapVersionedODataRoute( "odata", "api", modelBuilder );
//WHEN VERSIONING BY: url segment
endpoints.MapVersionedODataRoute( "odata-bypath", "api/v{version:apiVersion}", modelBuilder );
We are using Web API 2 on our project with Swagger. My problem is that when Microsoft.AspNet.WebApi.Versioning is applied as following:
the Swagger UI is ignoring the fact that now I have version in my API which needs to be provided.
I looked at several examples but none seem to address this issue in a satisfying manner.
How do I force Swagger to let me add the API version or just add the version number automatically to the URL?
Swagger configuration so far:
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "MoovShack.ServerApi");
// If your API has multiple versions, use "MultipleApiVersions" instead of "SingleApiVersion".
// In this case, you must provide a lambda that tells Swashbuckle which actions should be
// included in the docs for a given API version. Like "SingleApiVersion", each call to "Version"
// returns an "Info" builder so you can provide additional metadata per API version.
//
//c.MultipleApiVersions(
// (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
// (vc) =>
// {
// vc.Version("v2", "Swashbuckle Dummy API V2");
// vc.Version("v1", "Swashbuckle Dummy API V1");
// });
c.OperationFilter<MoovShackTokenHeaderParameter>();
})
.EnableSwaggerUi(c =>
{
// If your API has multiple versions and you've applied the MultipleApiVersions setting
// as described above, you can also enable a select box in the swagger-ui, that displays
// a discovery URL for each version. This provides a convenient way for users to browse documentation
// for different API versions.
//
//c.EnableDiscoveryUrlSelector();
});
You can see that so far MultipleApiVersions are disabled - from one good reason as it doesn't produce any results. Especially since I am not sure what "ResolveVersionSupportByRouteConstraint" should do.
I also read that "EnableDiscoveryUrlSelector" has some kind of impact but I am also not sure if that applies to my case. When I enabled it, nothing happened.
We use it like this in our project and swagger recognizes it and it looks fine
[ApiVersion( "1.0" )]
[Route("api/v{version:apiVersion}/[controller]")]
public class SomeControlelr: Controller{
[HttpGet("", Name = "Someaction"), MapToApiVersion("1.0")]
public async Task<IActionResult> SomeAction(string someParameter)
I have a problem to integrate swagger ui with my web api, and i don't have any idee what is the problem.
When i call in the browser the swagger, the page http://localhost:56381/swagger/ui/index is like in this screenshot
In the SwaggerConfig.cs file i have this code:
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace dummyNamespace
{
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c => c.SingleApiVersion("v1", "Test API"))
.EnableSwaggerUi();
}
}
}
I follow this tutorial: http://www.wmpratt.com/swagger-and-asp-net-web-api-part-1/ . But not working.
I don't now what is wrong with my configuratio.
I use .net framework 4.5.2, Web api 2.0 and Swashbuckle.5.5.3 version
Update:
When I call this url
http://localhost:56381/swagger/docs/v1
Return this image:
Update1:
After i put this code in my WebApiConfig.cs:
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
Now the http://localhost:56381/swagger/ui/index return this json:
{
"statusCode": 200
}
Any idee how to make make http://localhost:56381/swagger/ui/index return this page:
The page is from a test project.
After reading the tutorial you posted, it looks like somehow your root URL may be different than what swagger expects. From the swagger config file:
// By default, the service root url is inferred from the request used to access the docs.
// However, there may be situations (e.g. proxy and load-balanced environments) where this does not
// resolve correctly. You can workaround this by providing your own code to determine the root URL.
//
//c.RootUrl(req => GetRootUrlFromAppConfig());
If that is not the case, it could be that the swashbuckle nuget install is somehow corrupted. Try removing the nuget package and reinstalling it.
I'm at a point where I really need API documentation for my WebAPI 2 project, and I used the Swashbuckle 5 NuGet package. Out of the box, I can hit {myrooturl}/swagger and a UI pops up, but there are no controllers, methods, or anything in there. Just my title: [ base url: /EM.Services , api version: v1 ]
I took a look at the Swashbuckle docs, and since I'm using OWIN which is hosted by IIS, I modified the SwaggerConfig with:
c.RootUrl(req => req.RequestUri.GetLeftPart(UriPartial.Authority) + req.GetRequestContext().VirtualPathRoot.TrimEnd('/'));
as per this doc: https://github.com/domaindrivendev/Swashbuckle/blob/1326e753ce9b3a823b3c156b0b601134692ffc58/README.md#transitioning-to-swashbuckle-50
I also setup the build of the project to generate the XML docs and pointed my SwaggerConfig to it with:
private static string GetXmlCommentsPath()
{
// tried with an without the \bin
return String.Format(#"{0}\bin\EM.Services.XML", AppDomain.CurrentDomain.BaseDirectory);
}
I'm not sure if the XML docs working/not-working has anything to do with it though, as I get absolutely no controllers on the swagger-ui page.
For what it's worth, all of my controller inherit from a BaseController, which in turn inherits from ApiController.
Is there something screwy with my WebApiConfig?
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
config.Filters.Add(new ValidateModelAttribute());
config.Filters.Add(new BaseAuthenticationAttribute());
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
jsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
}
My concrete controllers all look like this (I've tried subbing out BaseController for ApiController and there is no change):
[RoutePrefix("api/whatever")]
public class FooController : BaseController
and my Base controller doesn't do much (yet), just has an attribute:
[BuildClaims]
public abstract class BaseController : ApiController
The empty page persists across using IIS Express or full blown IIS.
Update:
Example of a contrived controller I made that is really basic. It also does not show up, as I still have the boiler plate swagger ui with nothing in it.
/// <summary>
/// I am a test
/// </summary>
[RoutePrefix("api/dummy")]
public class DummyController : ApiController
{
[HttpGet]
[Route("foo")]
public int Foo()
{
return 42;
}
}
I got stuck.. and these answers didn't help me fully... although they led me there. Just to save other people some time:
You have to pass the http config from OWIN and then register on that instead of using the GlobalConfiguration class like so:
//starup.cs
public void Configuration(IAppBuilder app)
{
Config = new HttpConfiguration();
WebApiConfig.Register(Config);
app
.UseResponseLogging()
.UseRequestLogging()
.UseHttpErrors()
.UseExceptionLogging()
.UseWebApi(Config);
HandlerConfig.Register(Config);
SwaggerConfig.Register(Config);
}
and in the swagger config file, change the register method to:
public static void Register(HttpConfiguration config)
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
config
.EnableSwagger(c =>
{...
Hope this helps.
I found the problem. After creating an empty test project, I noticed that the WebApiConfiguration was being registered from the global.asax app start and not the OWIN startup class (like I did).
Since Swagger/Swashbuckle is hooking into the GlobalConfiguration and also given that OWIN startup and Global.asax live in different contexts (I think), the fix is to wire up your WebAPI stuff to register from Global.asax and to have OWIN's app object use WebAPI.
Relevant bits:
// global asax
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(WebApiConfig.Register);
// ... more stuff
}
//startup.cs
public void Configuration(IAppBuilder app)
{
// This must happen FIRST otherwise CORS will not work.
app.UseCors(CorsOptions.AllowAll);
HttpConfiguration config = new HttpConfiguration();
ConfigureAuth(app);
// webapi is registered in the global.asax
app.UseWebApi(config);
}
After rewiring as above, I can now see controllers & actions in swagger UI.
I found that I had the same issue. I created an extension method to help
using Swashbuckle.Application;
using System.Web.Http;
public static class SwaggerExtensions
{
public static HttpConfiguration EnableSwagger(this HttpConfiguration httpConfiguration)
{
httpConfiguration
.EnableSwagger(c => c.SingleApiVersion("v1", "A title for your API"))
.EnableSwaggerUi();
return httpConfiguration;
}
}
Then in my Startup.cs
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration
.EnableSwagger() // <==== EXTENSION METHOD <==== //
.MapHttpAttributeRoutes();
httpConfiguration.Routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new {id = RouteParameter.Optional});
appBuilder
.UseWebApi(httpConfiguration);
}
}
I just had the same issue myself and none of these helped me.
After some messing around I figured out that the routes that I'd labeled as [System.Web.Mvc.Route("visit")] were not being discovered by swagger.
[HttpGet]
// ROUTE ATTRIBUTE NOT FOUND BY SWAGGER
[System.Web.Mvc.Route("visit")]
public string Visit()
{
but [System.Web.Http.Route("visit")] is
[HttpGet]
// ROUTE ATTRIBUTE *IS* FOUND BY SWAGGER
[System.Web.Http.Route("visit")]
public string Visit()
{
I'm not 100% sure, but if it matters, I also switched from
public class MyAPIController : Controller
to:
public class MyAPIController : System.Web.Http.ApiController
More accurately I removed the "using" statement for System.Web.Mvc, but the code is listed for illustrative purposes.
Hope this helps someone else in future :) Good luck!
All these solutions works for me, but all of them are just nasty hacks for my issue. After few hours of investigation I found out, that the problem is I also use Glimpse (or other packages which change route table).
Here is a great summary: https://github.com/domaindrivendev/Swashbuckle/issues/468#issuecomment-139246748
Glimpse adds castle proxies on top of HttpWebRoute. So HostedHttpRouteCollection is collection of RouteProxy and not
HttpWebRoute.
APIExplorer class has FlattenRoutes method which does a foreach loop over HostedHttpRouteCollection.
GetEnumerator implementation of HostedHttpRouteCollection specifically look for HttpWebRoute. See the code below. Since glimpse
has added proxies, enumerator always returns 0 routes!!
public override IEnumerator GetEnumerator()
{
// Here we only care about Web API routes.
return _routeCollection
.OfType()
.Select(httpWebRoute => httpWebRoute.HttpRoute)
.GetEnumerator();
}
I am affraid there is no solution, you can choose what you want to use: Swashbuckle or Glimpse, but not both together.
Of course you can try to run on one of these workarounds, but there is a risk of unexpected behaviour and tricky bugs.
Swashbuckle sits on top of WebApi's metadata layer ApiExplorer. It takes the operation descriptions from ApiExplorer and then maps them to Swagger descriptions.
Since your controller inherits from BASECONTROLLER and not APICONTROLLER it will not work
Per JimWolleys comment
private IEnumerable<ApiDescription> GetApiDescriptionsFor(string apiVersion)
{
return (_options.VersionSupportResolver == null)
? _apiExplorer.ApiDescriptions
: _apiExplorer.ApiDescriptions.Where(apiDesc => _options.VersionSupportResolver(apiDesc, apiVersion));
}
this is the method that powers Swashbuckle to get all of the api calls. It takes an IApiExplorer. Which if it wasnt modified to take something different it takes the default ApiExplorer provided. Which only has information about things which inherit from ApiController
Swashbuckle git repo. just search for GetApiDescriptionsFor and it will take you straight to the method
I had tons of issues with Owin + Swashbuckle integration and none of these answers fixed everything for me. Long story short, I managed to solve everything and created an open source repo to be used as a template for anyone who needs it.
Please check: ASPSwaggerOwinTemplate
I had this issue as well using OWIN. The issue was resolved by installing only Swashbuckler Core as suggested in here and by editing the Startup.cs as below:
// Startup.cs
// ...
HttpConfiguration config = new HttpConfiguration();
// ...
config
.EnableSwagger(c =>
{
////add if there's custom root path
//c.RootUrl(req =>
// req.RequestUri.GetLeftPart(UriPartial.Authority) +
// req.GetRequestContext().VirtualPathRoot.TrimEnd('/'));
c.SingleApiVersion("v1", "A title for your API");
})
.EnableSwaggerUi();
// ...
appBuilder.UseWebApi(config);
I found this link to be very helpful. This particular solution is specific to a Microsoft.Azure.Mobile.Server API but it solved the problem for me.
Azure Mobile Apps Server and Swagger
I was familiar with the .NET core version of Swashbuckle which auto-expanded the controllers. When I was working on a framework (non-core) API, when I finally managed to get something showing, I was confused because I didn't know to click show/hide and still thought it wasn't working.
You can have it expanded by default with the following:
.EnableSwaggerUi(c => {
c.DocExpansion(DocExpansion.List);
});
In my case I had a similar issue to Alex C. I had to do 2 things to fix it:
The 1st things was I had an import statement about using MVC, something like this:
using System.Web.Mvc;
I removed that import statement and that solved half the problem. The other thing I noticed is that in one of the controllers that was showing up in Swashbucke had an annotation like this
[RoutePrefix("v1/Awesome")]
Where Awesome is the name of the controller AwesomeController. So I put that route prefix annotation right before my class declaration and now it shows up in the Swagger interface
[RoutePrefix("v1/Amazing")]
public class AmazingController : ApiController
So if anyone else is having this issue you might check if you need to add a route prefix like I did.