domaindrivendev / Swashbuckle.WebApi

Seamlessly adds a swagger to WebApi projects!
BSD 3-Clause "New" or "Revised" License
3.07k stars 678 forks source link

MultipleApiVersions not working in OWIN #1126

Open knowhoper opened 7 years ago

knowhoper commented 7 years ago

I am struggling to get Swagger to document multi-tenanted routes in WebApi.I have used this approach before but never in a self-hosted project. It seems MultipleApiVersions is never invoked - when i've added logging code.

StatupConfig.cs

  public class StartupConfig
        {
            private static ILog _logger = LogManager.GetLogger(nameof(StartupConfig));

            public void Configure(IAppBuilder appBuilder)
            {
                var config = new HttpConfiguration();
                config.MapHttpAttributeRoutes();

                config.Filters.Add(new ApiKeyAuthorizationFilter());
                config.Filters.Add(new ApiInvocationMetricsFilter());

                var assembly = Assembly.GetExecutingAssembly();
                var builder = new ContainerBuilder();
                builder.RegisterApiControllers(assembly);
                builder.RegisterWebApiFilterProvider(config);

                builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();

                var container = builder.Build();
                config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

                appBuilder.UseAutofacMiddleware(container);
                appBuilder.UseAutofacWebApi(config);
                appBuilder.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

                appBuilder.UseWebApi(config);

                config
                 .EnableSwagger(c =>
                    {
                       c.MultipleApiVersions(
                              ResolveVersionSupportByRouteConstraint,
                              (vc) =>
                              {
                                  vc.Version("v2", "API v2");
                                  vc.Version("v1", "API v1");
                              });
                        c.RootUrl((message) => ConfigurationManager.AppSettings["SwaggerRoot"]);
                        c.IncludeXmlComments($@"{System.AppDomain.CurrentDomain.BaseDirectory}\API.xml");
                        c.OperationFilter<AddRequiredApiKeyParameter>();
                        c.DescribeAllEnumsAsStrings(true);
                    })
                 .EnableSwaggerUi(ui =>
                    {
                        ui.EnableDiscoveryUrlSelector();
                    });
            }

            public static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
            {
                try
                {
                    var versionConstraint = (apiDesc.Route.Constraints.ContainsKey("apiVersion"))
                    ? apiDesc.Route.Constraints["apiVersion"] as ApiVersionConstraint
                    : null;

                    return versionConstraint?.AllowedVersion.Split('|').Select(x => x.ToLowerInvariant()).ToList().Contains(targetApiVersion.ToLowerInvariant()) ?? false;
                }
                catch (System.Exception excep)
                {
                    _logger.Error("An error occurred resolving version support", excep);
                    throw;
                }
            }
        }

Note: This predates WebApiVersioning so I am using a route constraint:

 ```
public class ApiVersion2RoutePrefixAttribute : RoutePrefixAttribute
  {
    private const string RouteBase = "api/{apiVersion:apiVersionConstraint(v2)}";
    private const string PrefixRouteBase = "api/{apiVersion:apiVersionConstraint(v2)}/";

    public ApiVersion2RoutePrefixAttribute(string routePrefix)
      : base(string.IsNullOrWhiteSpace(routePrefix) ? "api/{apiVersion:apiVersionConstraint(v2)}" : "api/{apiVersion:apiVersionConstraint(v2)}/" + routePrefix)
    {
    }
  }


Am I missing something here?

Thanks
knowhoper commented 7 years ago

This problem was solved by ensuring any Name parameters in the Route attribute are unique across both controller versions. I had an operation named Add, with a Route Name parameter of Add across both controllers and this was preventing Swagger from functioning.

i.e this

[Route("", Name = nameof(AddAdvertiser))]

changed to this

[Route("", Name = nameof(V1AddAdvertiser))]