34

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;
    }
}
Bill Sambrone
  • 4,334
  • 4
  • 48
  • 70

11 Answers11

30

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.

Sumit Maingi
  • 2,173
  • 3
  • 24
  • 44
  • 10
    Thanks! This worked for me but in addition I also had to remove the line at the very top of the SwaggerConfig.cs file ([assembly: PreApplicationStartMethod...). Before I removed it I was getting a "Parameter count mismatch" error – HitLikeAHammer Apr 30 '18 at 22:16
  • @HitLikeAHammer After removing above line, I'm getting 404. – Prashant Yadav Sep 12 '18 at 05:04
  • 1
    Thank you! I also had to remove the PreApplicationStartMethod attribute. – inliner49er Feb 03 '19 at 17:17
23

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.

Bill Sambrone
  • 4,334
  • 4
  • 48
  • 70
5

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);
    }
}
chris31389
  • 8,414
  • 7
  • 55
  • 66
4

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!

Alex C
  • 16,624
  • 18
  • 66
  • 98
  • I just spent an hour debugging the heck out of my controllers trying to get them to show up in Swagger. It turns out that it was pulling route from System.Web.Mvc instead of System.Web.Http. As soon as I switched it, it worked like a charm. Thank you! – Rob W. May 02 '18 at 21:37
3

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

  1. Glimpse adds castle proxies on top of HttpWebRoute. So HostedHttpRouteCollection is collection of RouteProxy and not HttpWebRoute.
  2. APIExplorer class has FlattenRoutes method which does a foreach loop over HostedHttpRouteCollection.
  3. 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.

Marek Dorda
  • 1,255
  • 17
  • 19
1

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

gh9
  • 10,169
  • 10
  • 63
  • 96
  • Is there an API documenting package that does support this scenario? Also, I created a dummy controller that inherits from ApiController like a normal controller would, and that also does not appear in the swagger ui. – Bill Sambrone Aug 05 '15 at 19:33
  • I dont know if there is an API package that does this out of the box.if you could post the code for the dummy controller or just upload your project somewhere that i can look at we could get this resolved – gh9 Aug 05 '15 at 19:59
  • dummy controller added – Bill Sambrone Aug 05 '15 at 20:17
  • sorry, i dont know why the dummy controller isn't coming up – gh9 Aug 05 '15 at 20:29
  • I'm not sure that this assessment is entirely correct. I'm able to see controllers that do not directly implement APIController when using Swashbuckle/swagger on a local webapi project. I also tested using a derived controller in a separate class and it was still found in a sample project. I am seeing the same issue as the OP on my real implementation. – Jim Wooley Aug 05 '15 at 21:15
  • @JimWooley fair enough, i have updated my answer with why it should only work with things inheriting from ApiController. – gh9 Aug 06 '15 at 03:51
1

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

Crimson7
  • 61
  • 3
1

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);
Nae
  • 14,209
  • 7
  • 52
  • 79
0

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

Rod Hartzell
  • 420
  • 3
  • 9
0

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.

enter image description here

You can have it expanded by default with the following:

.EnableSwaggerUi(c => {
    c.DocExpansion(DocExpansion.List);
});
JohnOpincar
  • 5,620
  • 3
  • 35
  • 38
0

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.

SendETHToThisAddress
  • 2,756
  • 7
  • 29
  • 54