Git Product home page Git Product logo

Comments (22)

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024 5

The first iteration of packages are available. I'm leaving a little burn-in time with a beta release for issues and/or feedback before the official release. I'll be updating the wiki soon.

You can use the links to the sample projects above to get started. Thanks to everyone that helped drive this forward.

from aspnet-api-versioning.

jlopresti avatar jlopresti commented on May 25, 2024 1

Hi guyz !

Just play with versioning and swagger few days ago and here is what I found :
We have to declare :

  • 1 Swagger Document per version : Technically we have to configure SwaggerGen Service
  • We all version available for a given action : Use Swagger method DocInclusionPredicate inside SwaggerGen config service
  • Enable SwaggerUI middleware and add Endpoint for each version

For DocInclusionPredicate we already have all the api to configure this method because ApiVersionModel has been built by ApiVersionConvention :

 c.DocInclusionPredicate((docName, apiDesc) =>
                {
                    var model = apiDesc.ActionDescriptor.GetProperty<ApiVersionModel>();
                    var version = docName;
                    if (model.IsApiVersionNeutral)
                    {
                        return true;
                    }
                    else if (model.DeclaredApiVersions.Any())
                    {
                        return model.DeclaredApiVersions.Any(_ => _.ToString() == version);
                    }                    
                    else if (model.ImplementedApiVersions.Any())
                    {
                        return model.ImplementedApiVersions.Any(_ => _.ToString() == version);
                    }

                    return false;
                });

For other points, we can't use that because ApplicationModel hasn't been executed yet but to build SwaggerDoc and SwaggerEndpoint we just have to know all version available into our application not necessarily which action is available in a specific version

Example of code to configure swagger doc and endpoint :

            app.UseSwaggerUI(c =>
            {
                foreach (var version in versions)
                {
                    c.SwaggerEndpoint($"/swagger/v{version}/swagger.json", $"V{version} Docs");
                }
            }); 
// Register the Swagger generator, defining one or more Swagger documents
            services.AddSwaggerGen(c =>
            {
                foreach (var version in versions)
                {
                    c.SwaggerDoc($"v{version}", new Info { Title = $"Sample service v{version}", Version = $"v{version}" });
                }

                c.DocInclusionPredicate((docName, apiDesc) =>
                {
                      //implem is above
                       ...
                });
            });

from aspnet-api-versioning.

xperiandri avatar xperiandri commented on May 25, 2024 1

Or like this

options.DocInclusionPredicate((docName, apiDesc) =>
{
    var model = apiDesc.ActionDescriptor.GetProperty<ApiVersionModel>();
    switch (model)
    {
        case ApiVersionModel _ when model.IsApiVersionNeutral: return true;
        case ApiVersionModel _ when model.DeclaredApiVersions.Any():
            return model.DeclaredApiVersions
                        .Any(apiVersion => apiVersion.ToString() == docName);
        case ApiVersionModel _ when model.ImplementedApiVersions.Any():
            return model.ImplementedApiVersions
                        .Any(apiVersion => apiVersion.ToString() == docName);
        default: return false;
    }
});

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024 1

For those following the thread, there is now an end-to-end working Web API Swagger Sample. Now that there are working examples for ASP.NET Core and Web API, I'm convinced the work here is all in the API explorer. There are a few conveniences that could be provided API versioning, but I don't think I should own them.

In my conversations with the ASP.NET team, they still looking to take the core abstractions from here and roll them into the platform. The work has been delayed so I can't say when that will happen. When it does finally happen, it will be easier for Swagger/Swashbuckle libraries to provide some of these capabilities directly out-of-the-box.

The current build is alpha quality. The majority of the remaining work will be testing. I won't release anything official until the quality bar is high. However, since so many of you have been clamoring for this feature and have patiently waited for months, I'm willing to put out an alpha package for you to play with. No guarantee that nothing else will change, but I'm pretty confident little will change to the public API surface. Let me know your reaction.

Thanks

from aspnet-api-versioning.

RehanSaeed avatar RehanSaeed commented on May 25, 2024 1

I've added full versioning support to the ASP.NET API Boilerplate project template. You can now use dotnet new to create a project and optionally enable or disable Swagger and/or ASP.NET API Versioning (Both options are turned on by default) as well as a dozen other settings.

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024 1

Awesome! I'm sure this will help folks stitch things together very quickly. Thanks for putting in the work and sharing. :)

from aspnet-api-versioning.

CumpsD avatar CumpsD commented on May 25, 2024

If it helps, when I currently use this and Swagger, Swagger outputs these things:

image

from aspnet-api-versioning.

scoleman81 avatar scoleman81 commented on May 25, 2024

I am not sure what you are after here but in the last day or so I have been adding swagger and api versioning to a new core api site I am working on. I wanted my version and a functional area split out in my swagger docs. I am using swashbuckle to implement swagger (Swashbuckle.AspNetCore). The way I implemented this was all on the swagger generation side not in versioning. I decided to generate separate documents for each API version (and namespace in my case) I was going to have. Then it is just a mater of selecting the document for the api I want to see. You can also insert a default query or header parameter and it will just work. I have attached some of my code if it helps.

In the end I get a list of swagger documents for my api that is split by a functional area (portion of the namespace) and api version ie

DoSomeWorkGroup1 - v1.0
DoSomeWorkGroup1 - v2.0
DoSomeWorkGroup2 - v2.0
etc

Generate a list of Namespaces and Versions for Swagger:

public static class SwaggerDocuments
{
    public static List<SwaggerDocument> GetSwaggerDocumentList()
    {
        var APIGroups = new List<SwaggerDocument>();
        Assembly asm = Assembly.GetExecutingAssembly();
        var controllers = asm.GetTypes()
            .Where(type => typeof(Controller).IsAssignableFrom(type))
            .SelectMany(type => type.GetMethods())
            .Where(method => method.IsPublic && !method.IsDefined(typeof(NonActionAttribute)));

        foreach (var controller in controllers)
        {
            Type t = controller.GetType();
            var Namespace = controller.DeclaringType.Namespace;

            var attributes = controller.DeclaringType.CustomAttributes;

            foreach (var item in attributes)
            {
                if (item.AttributeType == typeof(ApiVersionAttribute))
                {
                    foreach (var version in item.ConstructorArguments)
                    {
                        APIGroups.Add(new SwaggerDocument() { Namespace = Namespace.Replace("<Namespace Prefix I want Removed>.", "").Split('.').First(), Version = version.Value.ToString() });
                    }
                }
            }
        }

        return APIGroups.GroupBy(x => new { x.Namespace, x.Version }).Select(x => new SwaggerDocument { Namespace = x.Key.Namespace, Version = x.Key.Version }).ToList();
    }

    public class SwaggerDocument
    {
        public string Namespace { get; set; }
        public string Version { get; set; }
    }
}

Swagger generation in ConfigureServices of Startup Class

services.AddMvc(c =>
    c.Conventions.Add(new ApiExplorerGroupPerVersionConvention())
);
services.AddSwaggerGen(c =>
{
    var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "BG.Connect.API.xml");
    c.IncludeXmlComments(filePath);

    foreach (var item in Apilist)
    {
        c.SwaggerDoc($"{item.Namespace}{item.Version}", new Info { Title = $"{item.Namespace} API", Version = item.Version });
    }
    c.OperationFilter<AddRequiredHeaderParameter>();
    c.DocInclusionPredicate((docName, apiDesc) =>
    {
        var restrictedVersion = apiDesc.ActionAttributes()
            .OfType<MapToApiVersionAttribute>()
            .SelectMany(attr => attr.Versions);

        var versions = apiDesc.ControllerAttributes()
            .OfType<ApiVersionAttribute>()
            .SelectMany(attr => attr.Versions);
        if (restrictedVersion.Count() > 0)
        {
            return restrictedVersion.Any(v => $"{apiDesc.GroupName}{v.ToString()}" == docName);
        }
        else
        {
            return versions.Any(v => $"{apiDesc.GroupName}{v.ToString()}" == docName);
        }
    });
});

Swagger UI Settings in Configure of Startup Class

app.UseSwaggerUi(c =>
{
     foreach (var item in Apilist)
    {
        c.SwaggerEndpoint($"/swagger/{item.Namespace}{item.Version}/swagger.json", $"{item.Namespace} API - v{item.Version}");
    }
});

Helper Class Used to Get the Portion of the namespace I want to split the documents by.

public class ApiExplorerGroupPerVersionConvention : IControllerModelConvention
{
    public void Apply(ControllerModel controller)
    {
        var controllerNamespace = controller.ControllerType.Namespace; // e.g. "Controllers.V1"
        var apiVersion = controllerNamespace.Replace("<Namespace Prefix I want Removed>", "").Split('.').First();

        controller.ApiExplorer.GroupName = apiVersion;
    }
}

Helper Function to add the needed query or header parameters to all Item:

public class AddRequiredHeaderParameter : IOperationFilter
    {
        public void Apply(Operation operation, OperationFilterContext context)
        {
            string Version = "";
            var controllerActionDescriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
            if (controllerActionDescriptor != null)
            {
                var controllerAttributes = controllerActionDescriptor.ControllerTypeInfo.CustomAttributes;

                foreach (var item in controllerAttributes)
                {
                    if (item.AttributeType == typeof(ApiVersionAttribute))
                    {
                        foreach (var version in item.ConstructorArguments)
                        {
                            Version = version.Value.ToString();
                        }
                    }
                }
                
                var methodAttributes = controllerActionDescriptor.MethodInfo.CustomAttributes;

                foreach (var item in methodAttributes)
                {
                    if (item.AttributeType == typeof(MapToApiVersionAttribute))
                    {
                        foreach (var version in item.ConstructorArguments)
                        {
                            Version = version.Value.ToString();
                        }
                    }
                }
            }

            var header = new NonBodyParameter
            {
                Name = "api-version",
                In = "query",
                Required = true,
                Type = "string",
                Default = Version

            };

            if (operation.Parameters == null)
            {
                operation.Parameters = new List<IParameter>();
            }

            operation.Parameters.Add(header);
        }
    }

Hope this helps.

from aspnet-api-versioning.

xperiandri avatar xperiandri commented on May 25, 2024

Do you mean like releasing a package related to API Versioning with a solution from here packaged into a single library with easy setup?

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024

Exactly. An interesting challenge is how to aggregate the controller versions for documentation. In the versioning libraries this happens by letting the routing infrastructure find all candidates for a route and then aggregating from there. In addition, the client knows the route their looking for. For documentation, all of the routes need to be collated and then aggregated. I know this is possible, but the implementation has yet to be hashed out.

Another aspect is making sure that the documentation is built using the right abstractions. A lot of the early variants that I'm seeing people build are based on attributes. This not actually what the API versioning libraries use. The libraries only care about IApiVersionProvider and IApiVersionNeutral. One of the ways these interfaces are realized just happen to be attributes.

It's important for the right abstractions are used so that the solution is generic and can be used for various API versioning scenarios. Regardless, there has been some great progress in a number of attempts to create a library and I'll certainly be reviewing the implementations I know about for input to the out-of-the-box solution.

Now that I've nearly burned down all of the other issues, I should have some time (soon) to dedicate to this feature. Thanks for your patience.

from aspnet-api-versioning.

xperiandri avatar xperiandri commented on May 25, 2024

So what help do you need?
Do you want a pull request that adds a separate project with SwashBuckle filters that I referenced?
RemoveVersionParameters : IOperationFilter
abstract SetVersionInPaths : IDocumentFilter that requires to implement version replacement logic.

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024

Someone looking to help ... yeah! Coincidentally, I've finally burned down just about every other outstanding issue, so I can now give this topic the full attention it deserves. Admittedly, I haven't spent a bunch of hands-on time with Swagger and Swashbuckle, so I'll be looking for feedback there for sure. I'll be catching myself up as quickly as possible. Here's the 10,000ft overview:

Objective

Provide an aggregated API version meta-model that groups all services (e.g. controllers) by route and their associated API versions. This is the part that is difficult and provides the value proposition to service authors. The result should be something analogous to, if not an implementation of, an API Explorer that is API version aware. This will provide all the heavy lifting for service authors and can potentially live directly within the API versioning libraries. I'm up in the air as to whether a separate library should be required for these capabilities. Opinions are welcome.

Requirements

  • Controllers must be collated by route (this is probably the most challenging part)
  • API version information must be based on IApiVersionProvider and IApiVersionNeutral (not solely attributes)
    • Once you can get down to the controller or action descriptors, this information can be collected using the built-in GetApiVersionModel extension methods
  • API version information should be aggregated into a single ApiVersionModel per route
    • Built-in extension methods exist to aggregate ApiVersionModel instances
  • Implement IApiExplorer for ASP.NET Web API, if possible/feasible
  • Implement IApiDescriptionProvider for ASP.NET Core, if possible/feasible

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024

ApiVersionModel usage is pretty low-level, so let me elaborate a little about how it's used. I'll limit the specifics to ASP.NET Core, but things work similarly in Web API. Ultimately, the logic should be very close to the way that the action selector works.

If API versioning is enabled, then you can grab the ApiVersionModel as you've shown; however, there's a chance that it's not and the model will be null so you have to account for that. In addition, if the action is implicitly versioned, then you'll have to fallback to getting the API version information from the controller model. I thought I had an extension method for this, but I guess in ASP.NET Core I don't. I think it's worth adding, so I'll open an issue for that. The implementation should look almost the same as the IsImplicitlyMappedTo extension method.

Once you've retrieved a model, that should be all that's necessary based on the example I'm seeing. The only time any of the properties on the model are empty is when it's version-neutral. Since the both version-neutral and any version are matches, these conditions seem unnecessary.

The DeclaredApiVersions are probably not useful for documentation. This collection is used by the controller and action selectors to find the matching implementation. The ImplementedApiVersions is an aggregation of the SupportedApiVersions and DeprecatedApiVersions for a single service. For creating a simple list, ImplementedApiVersions is probably sufficient; however, I suspect there would be a desire to know and document which API versions are deprecated. The ApiVersion class intentionally does not track that information. In order to bucketize these two groups, you'll want to use the SupportedApiVersions and DeprecatedApiVersions instead (though probably not in this code snippet).

I hope that provides some additional context.

from aspnet-api-versioning.

jlopresti avatar jlopresti commented on May 25, 2024

To complete answer, i think that actually retrieving ApiVersionModel is dependant to have an ApplicationModel, ControllerModel or ActionModel.
To be able to configure SwaggerUi middleware we need to juste have a class that allow to retrieve all api version from multiple provider (Implicit, Attrbiute, whatever)

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024

Sort of. The ControllerModel and ActionModel contain the information used to discover and populate the version information from attributes. This information is merged with any version information defined by conventions. The final set of version information is aggregated into a single ApiVersionModel and is attached as a property to the corresponding model. The ApiVersionModel defines a constructor that accepts the models, but this is purely a convenience as a shortcut to populate the information from attributes. Beyond that, there is no use or coupling to the models. The models and ApiVersionModel can still be built-up independent of this behavior.

Unless I'm mistaken, this behavior doesn't add any additional dependency coupling that didn't already exist. If it does, then we should consider changing it now before the 1.1 milestone is officially released. These constructors were previously internal, but changed recently due an extensibility bug.

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024

A quick update. I have first set of changes out for review in PR #103 which introduces support for the IApiExplorer in Web API. I know a lot of you are waiting for the ASP.NET Core flavor, but I wanted to get - what I believe will be - the hard one out of the way. I'm expecting the ASP.NET Core version to come sometime during the next week.

As a sanity check, if there is formal support for an API explorer that groups and enumerates all the controllers and actions by API version, is there really any other support needed to light up Swagger and/or Swashbuckle? It seems to me that the main challenge is collating all the metadata about API-versioned services. After that, the integration to Swagger and Swashbuckle should be business as usual, no?

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024

Alright folks, an alpha version of the ASP.NET Core functionality is now in PR #103. You can see how it comes together in the new Swagger Sample in the working branch.

Free feel to share your thoughts on the PR.

from aspnet-api-versioning.

xperiandri avatar xperiandri commented on May 25, 2024

I'll be updating the wiki soon.

Yes, please. Because I have no idea which things are now unnecessary in my previous Swagger usage implementation

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024

The first round of documentation is up. See the API Documentation topic. The sample projects should also help point you in the right direction. Feel free to ask questions if anything is unclear.

from aspnet-api-versioning.

xperiandri avatar xperiandri commented on May 25, 2024

Looks cool! I will try it tomorrow

from aspnet-api-versioning.

xperiandri avatar xperiandri commented on May 25, 2024

You can simplify sample by moving provider resolution to method parameter like this

public void Configure( ...loggerFactory, IApiVersionDescriptionProvider provider )
{
    ...
    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() );
            }
        } );
}

from aspnet-api-versioning.

commonsensesoftware avatar commonsensesoftware commented on May 25, 2024

Reasonable. I'll update it.

from aspnet-api-versioning.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.