
Displaying Azure DevOps build number in Swagger UI for ASP.NET Core
How to add and show Azure DevOps build number in ASP.NET Core project and Swagger UI
Swagger is a great way to document you REST API endpoints and reduce the need for communication, item spent and the cost for supporting the consumers of you APIs. Apart from providing the info about the endpoints, models, validation it can also display the data of the product. This data can be either stored outside of your application, but most of the time good old AppInfo is just good enough for storing the information about product name, product description or product version.
Unlike in .NET Freamwork where you had AppInfo.cs class right out of the box once you create your project, in .NET Core you do not get AppInfo.cs generated in the project. Instead you can set Assembly attributes in your project *.csproj file and they will be translated into AppInfo.cs class and compiled into an Assembly during you project compilation time.
All the assembly attribute values you set in your project *.csproj file will be generated in the AppInfo class on the compile time and you can access them through reflection by accessing the Assembly class instance.
Before we start integrating with Azure DevOps Build pipeline, let's make a small test for fetching Assembly information. For start let's add some Assembly related information to our project file.
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildThisFileName).xml</DocumentationFile> <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel> <Product>Sample REST Service</Product> <Description>Sample ASP.NET Core WebApi REST Service</Description> </PropertyGroup> </Project>
Since we have stored our data let's try to retrieve it first in the Startup class constructor first before we dive into the Swagger and add these things to its configuration. I simply added debug output lines in the Startup constructor and the output I got matched the values we declared in the .csproj file
We figured out how to add and retrieve values from the Assembly, now let's display the in the Swagger auto-generated API documentation. If you are not that familiar with Swagger and you are not sure how to do the setup, I suggest you to check few articles I wrote previously for using Swagger in ASP.NET Core DTO comments from external assembly in Swagger documentation in ASP.NET Core and Setting up Swagger to support versioned API endpoints in ASP.NET Core
services.AddSwaggerGen( options => { // Resolve the temprary IApiVersionDescriptionProvider service var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>(); // Add a swagger document for each discovered API version foreach (var description in provider.ApiVersionDescriptions) { options.SwaggerDoc(description.GroupName, new Swashbuckle.AspNetCore.Swagger.Info() { Title = $"{this.GetType().Assembly.GetCustomAttribute<AssemblyProductAttribute>().Product} {description.ApiVersion}", Version = description.ApiVersion.ToString(), Description = this.GetType().Assembly.GetCustomAttribute<AssemblyDescriptionAttribute>().Description }); } });
This sample code snippet considers that your API supports versioning. To read more about API versioning and Swagger, consider visiting this page http://bit.ly/2OnbQTF
Let's run our ASP.NET project and see what is the output in Swagger UI.
You can see that we have our Assembly info display as the part of Swagger UI.
Since we want to incorporate our Azure DevOps Build number into our Swagger, from the previous example we saw that Assembly info is the great place to store this value as it will be part of the built artifact itself and we do not need to store it anywhere externally. It is related to the build and that is where it should be stored as well.
We only used AssemblyProductAttribute and AssemblyDescriptioAttribute, we can try to use pretty much any other Assembly info attribute for keeping our Build number and as this is sort of a version of the build artifact, it is somehow logical to use attribute which is related to Assembly version. What first come to mind is AssemblyVersionAttribute, but beware, not all the attributes have loose value format. To demonstrate that, let's try to set AssemblyVersionAttribute in the .csproj file and try to run the project. To have it close as possible to the real scenario, I used one of my Azure Build numbers 20190412.8
Our project file will now look something like this
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildThisFileName).xml</DocumentationFile> <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel> <Product>Sample REST Service</Product> <Description>Sample ASP.NET Core WebApi REST Service</Description> <AssemblyVersion>20190412.8</AssemblyVersion> </PropertyGroup> </Project>
Right upon the debug start, you will get the compilation error. If we check the source of an error, you will see that it comes from auto-generated AppInfo class which was assembled from our project .csproj file.
As the compilation error shows, AssemblyVersionAttribute value, although it is a string, it has to follow specific format. You can find more details about assembly version format from official Microsoft documentation page related to AssemblyVersionAttribute Class. To cut the story short, we simply cannot use AssemblyVersionAttribute, so we need to pick some other assembly info attribute.
Assembly version is fortunately not the only version attribute we have to choose for storing free-form version values. One of them is AssemblyInformationalVersionAttribute which unlike AssemblyVersionAttribute can use free form text which makes it perfect for storing our build artifact version (build number).
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildThisFileName).xml</DocumentationFile> <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel> <Product>Sample REST Service</Product> <Description>Sample ASP.NET Core WebApi REST Service</Description> <InformationalVersion>20190412.8</InformationalVersion> </PropertyGroup> </Project>
We do not have the compilation error anymore, so we can now add this value to our Swagger configuration and make the test run to see how our Build number will be displayed in the Swagger UI.
services.AddSwaggerGen( options => { // Resolve the temprary IApiVersionDescriptionProvider service var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>(); // Add a swagger document for each discovered API version foreach (var description in provider.ApiVersionDescriptions) { options.SwaggerDoc(description.GroupName, new Swashbuckle.AspNetCore.Swagger.Info() { Title = $"{this.GetType().Assembly.GetCustomAttribute<AssemblyProductAttribute>().Product} {description.ApiVersion}", Version = description.ApiVersion.ToString(), Description = this.GetType().Assembly.GetCustomAttribute<AssemblyDescriptionAttribute>().Description + $"<p><strong>Build: </strong>{this.GetType().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion}</p>" }); } });
We are ready now to jump to Azure DevOps Build pipeline to setup AssemblyInfo update step. Assembly information update task does not come out of the box with Azure DevOps Build pipeline and you have to add it from the Visual Studio Marketplace as an extension to your VisualStudio online account. The extension I used and which works quite well is called Assembly Info, works fine with both .NET Framework, .NET Standard and .NET Core and it is free.
This task needs to be added to the pipeline before any other build task as it will basically make changes to the source before it restores packages and performs the actual code builds.
Build number is acquired from the build variables with $(Build.BuildNumber) macro and replaced when build pipeline is initiated.
Having your build number in your Swagger documentation, might be quite useful especially when you are facing issues with your latest build/release and you need to roll back to the previous stable build/release. Unfortunately, you cannot incorporate release number into your project since it is using already compiled and prepared deployment package, but still you can use build number to track down the release using it.
References
- Assembly Info - Visual Studio Marketplace
- AssemblyProductAttribute Class
- AssemblyInformationalVersionAttribute Class
- Swagger
Disclaimer
Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.
Comments for this article