~/codewithstu

How To Version ASP.NET Core MVC & Minimal APIs

Transcript

If your API is a key part of your product, you're going to want to version your APIs. One of the major reasons that we consider versioning our APIs is so that we provide a consistent experience for our users. If we constantly change our APIs, then consumers will break, they will get annoyed and eventually leave our product. There are three main ways that you can version your APIs: headers, query string, or URLs. I don't believe there is a right way to do versioning, so I'm going to show you all three approaches in this video. I'll let you decide which one is the best for your scenario. We will see this in action for both a traditional MVC API and a minimal API. Depending on whether we are working with a minimal API or an MVC based API depends on which NuGet package we need to add. For MVC APIs, we need to add the Asp.Versioning.Mvc.ApiExplorer package and for minimal APIs we need to add the Asp.Versioning.Http package. It's worth noting that the name of the package has changed from the previous Microsoft.

Asp.NET Core.Mvc.Versioning.ApiExplorer package because the main contributor to the repository has now left Microsoft and can't reuse the prefix. There's a link for this announcement in the description below. Once we've added the correct NuGet package for our API type, we can head over to our service collection and call AddApiVersioning. We want the options later on, so I'm going to set that up in the lambda function. Now, one additional step is needed for MVC based APIs, and that is to call AddMvc at the end of the AddApiVersioning call. One minor thing that you need to consider before you start is which style of versioning numbers you're going to have. For example, I use a major only versioning strategy, which is where a single number is used to describe the version, e.g. v1. Another common versioning strategy is to use the major.minor setup, e.g. 1.0. You'll also need to consider how much a version covers for an API. For me, I have versioning set up on a per endpoint basis, but I have seen all endpoints on an API being versioned as one. There is no right strategy for you to pick here. It all depends on your requirements and what you think is easiest for you to manage, thus the experience for your consumers.

So let's take a look at how we can add a version to an MVC controller. With a controller, we can add the version information either at the class level, meaning that the API version will apply to all of the actions in that controller, as well as adding it to a specific action. To version an entire controller, add the ApiVersion attribute, parsing in the version you want, at the top of the class. To version a single endpoint, add the same ApiVersion attribute and the version number to the endpoint that you want. The setup for a minimal API is a little bit different. First, we need to create a version set which tells the API versioning subsystem which versions are available to be used. Here we can set some common options like whether to report API versions or not. From there, we are versioning each endpoint by first calling WithApiVersionSet, passing in our version set, then we map it to a specific version with a call to MapToApiVersion, or we can make it version agnostic with IsApiVersionNeutral. Once this is done, the versioning setup for minimal APIs and MVC controllers are pretty much the exact same. I'll call out whenever they're different.

The first way that we can read the version of the request is to use headers. When using a header, we have two approaches that we can use. We can set up an extension to the accept header, or we can use a custom header. To use it with the accept header, we go to our options and we add in options.

ApiVersionReader equals new MediaTypeApiVersionReader and then pass in the parameter that we're going to use. If we then switch across to Postman, you'll be able to see that our request fails when we don't add any versioning information. So we can then go to the header, add in the version equals one, submit the request, and our request starts working as we would expect. If we wanted to use a custom header, we need to change the type that's used to new HeaderApiVersionReader and then pass in the name of the header from which to read. Again, if we run the setup in Postman without any versioning passed in, you can see that the request fails. We add in our custom header, pass in the version number, and everything will start to work as we would expect. For a header-based versioning strategy, there is no difference between MVC and minimal APIs.

The next way of versioning our URLs is to use a query string parameter. Here we will change the type of API version reader to QueryStringApiVersionReader and pass in the name of the parameter that will use in the query string to provide the version information. Just to quickly see this in action, we try and execute our endpoint without any versioning information. Then as expected, our endpoint fails. But when we amend the query string to add in the version information, our API works as we expect. For a query string based versioning strategy, there is no difference between MVC and minimal APIs.

The last major way of adding in versioning information is to use the URL. There is a slight difference between the way this works with minimal APIs and MVC due to the way that routing works, so I'm going to start off by looking at an MVC application. To add the version information to the URL, we first need to modify the route itself. Depending on your setup, this might be via the Route attribute or by one of the HTTP attributes such as HttpGet. We add a new segment to our URL called version, limited to the type of ApiVersion. This is a special validation that's created by the versioning package to ensure that correct values are passed in and the correct actions are called. For a minimal API, we work in a very similar way by needing to change a route. Instead of changing an attribute, we just need to edit the route information that's passed into one of the map methods such as MapGet or MapPut. We put in the new path segment in the exact same way as we did for the MVC route segments, i.

e. adding version and limiting it to the type ApiVersion. Once this is set up for your application, then the last bit we need to do is change the API version reader in the versioning options. We need to change it to use the type UrlSegmentApiVersionReader, and this will make it read from the route data instead. If we switch over to Postman for the last time, we can test out our latest versioning strategy. Without any version information added to the route, we cannot access our endpoints. The same occurs if we don't pass in a valid version such as 99. But when we send a correct version in our URL, then we get the information back that we would expect from our API.

For any of the versioning strategies that we've just been through, we may come across scenarios where we need to access the version that's been requested by the user. The easiest way to access this information is to go through the HttpContext. On each HttpContext instance, there is an extension method that we can invoke called GetRequestedApiVersion. If there is a version requested by the user, the method returns this as an ApiVersion instance, otherwise it returns null. This can be handy for when you're using the same action or endpoint for multiple versions of your API.

There are other things that we can do with this package, such as specifying the default version when one is not specified, and reporting the supported or deprecated versions in our responses. To set the default version of our API, we would first need to set the option AssumeDefaultVersionWhenUnspecified to true. Following this, we would then need to set the DefaultApiVersion option to the default version that you wish to be assumed. Please note that this will only work if you're using the header or query string versioning strategies.

We can also specify which versions are deprecated and which ones are not. There are slightly different ways of doing this in MVC and minimal APIs. In MVC APIs, we would set the Deprecated flag on the ApiVersion attribute. For minimal APIs, we would call HasDeprecatedApiVersion with the relevant version number that we are using. Once this is done, for both MVC and minimal APIs, we set the option ReportApiVersions equal to true in the options, and then we will start receiving two new headers. These are api-deprecated-versions and api-supported-versions respectively. These headers contain a comma separated list of supported versions or deprecated versions depending on the header.

Now you've got a fully versioned API, you're going to want to add logs, metrics and traces to it. Luckily I've got a full playlist right here that shows you how to do all of this.

If you enjoyed this video, consider subscribing to the YouTube channel for more content like this.

// share_this