Update: Swashbuckle.AspNetCore
is no longer a pre-release and now reached 1.0.0 status.
Let’s create a versioned and semi-automatically documented Web API, this could be done both for public API, and quite useful for, internal-use API’s as well.
The ASP.NET Core Web API Project
Create a new project Visual C# > .NET Core > ASP.NET Core Web Application and give a descriptive name to your API.
We’re going to add three NuGet packages:
Install-Package Microsoft.AspNetCore.Mvc.Versioning Install-Package Swashbuckle.AspNetCore Install-Package SwashbuckleAspNetVersioningShim -Pre
Two of these packages are prerelease packages but they are fully functional
You can now remove the automatically created ValuesController.cs and let’s add a new HelloController.cs
using Microsoft.AspNetCore.Mvc; namespace VersionedWebApi.Controllers { /// <summary> /// HelloController, just saying Hello World! /// </summary> [ApiVersion("1.0", Deprecated = true)] [Route("api/v{version:apiVersion}/[controller]")] public class HelloController : Controller { /// <summary> /// Default Get call returning Hello World! /// </summary> /// <returns></returns> [HttpGet] public string Get() { return "Hello World!"; } } }
I’ve decorated the class with an ApiVersion attribute stating that this is our version 1.0 HelloController and that it is already deprecated, and also note the modified Route attribute with a v{version:apiVersion} part.
Let’s create a new folder in the Controllers folder called v2 and create a second HelloController.cs in there.
using Microsoft.AspNetCore.Mvc; using System; namespace VersionedWebApi.Controllers.v2 { /// <summary> /// The modern HelloController, all up to date responses /// </summary> [ApiVersion("2.0")] [Route("api/v{version:apiVersion}/[controller]")] public class HelloController : Controller { /// <summary> /// Default Get call returning Hello current-year! /// </summary> /// <returns></returns> [HttpGet] public HelloWorldModel Get() { return new HelloWorldModel { Message = $"Hello {DateTime.Now.Year}!" }; } } /// <summary> /// HelloWorldModel class for HelloController /// </summary> public class HelloWorldModel { /// <summary> /// Message string /// </summary> public string Message { get; set; } } }
This class is decorated with a 2.0 ApiVersion attribute and features a HelloWorldModel return type with a DateTime.Now part.
Now open up the Startup.cs file and let’s add Versioning support to our Web API.
public void ConfigureServices(IServiceCollection services) { // Add framework services var mvcBuilder = services.AddMvc(); // Adds versioning capabilities, defaulting to version 1.0 calls if available services.AddApiVersioning(o => { o.AssumeDefaultVersionWhenUnspecified = true; o.DefaultApiVersion = new ApiVersion(1, 0); }); // Add generated documentation services.AddSwaggerGen(c => { SwaggerVersioner.ConfigureSwaggerGen(c, mvcBuilder.PartManager); }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ApplicationPartManager partManager) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); // Generate swagger.json app.UseSwagger(); // Let's enable SwaggerUI app.UseSwaggerUI(c => { SwaggerVersioner.ConfigureSwaggerUI(c, partManager); }); app.UseMvc(); }
Open the project’s Properties and go to Debug and change the Launch Url to api/v1/hello and start the application, see the result and change the url v1 to v2.
Now head over to /swagger and see your interactive documentation based on the /swagger/v1.0/swagger.json and /swagger/v2.0/swagger.json generated files.
Check out the finished project on github.com/PaddoSwam/AspNetCoreVersionedWebApi.
Thanks for the write up. Customising the shim to output better formatted docs has proven pretty simple (may raise any issue for allowing better configuration when I get a second).
Have you managed to get the above to work with Areas? For example, my api route template is
"api/v{version:apiVersion}/{area:exists}/{controller=Home}/{action=Get}";
I’m going to check that ApiExplorer is detecting them, but Swagger is not it seems.I ran into the same issue and currently working on a solution for that, it’s mostly the recognition of variable routing parts and handling them.
I’ll keep you posted!
I found the answer in case you’re interested. The ApiExplorer doesn’t detect controllers using conventional routing (they must use attribute routing).
So, remove the route template from your
UseMvc
call. In my case I’m using both versioning as you’ve defined, and also Areas. Therefore, typical attributes on my controllers will look as follows:You’re not quite there yet though. You’ll notice I haven’t supplied the
[ApiExplorerSettings]
attribute, which means the ApiExplorer will not populate the details required by Swagger. However, this can be done by convention. In startup under your AddMvc call, add a new convention:Now create the class:
That should be the barebones required for this to display on swagger, though I did go one step further. I wanted Swagger to group my api calls by area, so I switched the above code to assign the GroupName as follows:
Hopefully that helps?
Swashbuckle.AspNetCore is at 1.0.0 now and we no longer need to specify prerelease to install it.
Great article by the way!
I’ve updated the post, it was written sometime ago! 🙂
Edit: and the repository is up to date as well!