 So what I'm going to talk about today is a few things about versioning ASP.NET Core. We're going to start by talking about just some versioning strategies in my opinions about them, then we'll dive into some code. So first of all, when you're publishing an API, versioning really comes down to how you want to deal with changes over time. As developers, we start to think about when we're building a new system, getting that system out. But sometimes we forget that we're going to have to support it for the next 10 years or the people coming after us will. So once you publish it, it's set in stone to have people writing code against it. So your users are relying on this API not changing. So having a strategy for dealing with that is really important. Because your requirements are going to continue to change, and you need a way to evolve these APIs without breaking existing clients. That's really the core of what API versioning is about. This isn't about product versioning. This is really about changing the way the API works. Now they may be coordinated at some time together, but don't necessarily tie them together until the API needs to change. You may have several versions of your project that don't require your API to change. You may have API changes that don't correlate with your product versioning. So the API versioning becomes an issue when, in typical projects, it's thought about later. So outside of APIs, let's say you're just doing regular.net and you want to deal with versioning. You can do this with different versions of the package. So normally this would be sort of the versioning at the assembly level. But APIs are harder because you need to have support for both versions at the same time. You may have older clients that are still using APIs from when you originally published and new clients that want to take advantage of new functionality or changes in functionality. And so side by side deployment isn't really feasible. You don't want to have a 1.0 version on a server and a 1.1 or a 2.0 version on the same server because you're going to have to support all those different versions at once. So we need a way to support both versions in the same code base. There are a lot of ways to do this and some of them I recommend and some of them I don't. But there isn't a one catch all that should be good for everyone. This is a question I get an awful lot about is, which one should I choose? And a lot of it depends on who is using your API, how your API is going to change and what sort of technical requirements you need. So let's talk about them. I don't recommend all of them. In fact, one you're going to hear me sort of say some negative things about. But I'm still, the library I'm going to be showing you still does support all the ways I'm going to be talking about. You want to find something that works for your use case, period. You have to think about this as, how am I going to want to deal with this? And that's really all that matters. Try to get away from the dogma of this is the one way to do it and this is the right way in every case. Because if you've been a programmer long enough, you know that that's never the case, it's always a compromise. Ultimately, you're serving your clients, not yourselves. So if it's harder for you but easier for your clients, that's usually the better strategy than the reverse. Especially if your clients are paying invoices that end up paying your salary. I really suggest you take this approach. So let's talk about the most common approach I see and the one I dislike the most, and that is versioning in the URI. So I often see this first version, this first example of using a version number inside of the URI itself. And while this looks really, this really makes sense if you're making broad changes every time you do a version. This does mean that every time you make a new version, you're gonna need to get all of your clients to change the URIs across their application. And so I find this to be the most fragile for clients. Another approach we'll talk about is the query string versioning, and that's where you use a query string to specify the version. And I like this because it implies that there's a default version and then allows customers, clients, to change the version as they need to. So they can either opt into an old version or opt into a new version. And without the query string, you should have an implicit default version in this case. Which this first URI path really doesn't allow for. Another approach I see quite a lot is versioning with headers. Where you have a header value that specifies what version they're looking for. And this can be useful in that it doesn't corrupt the URIs or anything, it just makes a change to the header. But it does require that you have clients that know how to deal with sending headers and depending on your user base. If you're expecting novice web developers to be able to call an API, this may be a little harder for them. It's not difficult, but there is a one level of sophistication further in order to deal with headers. There's also the accept header, which I quite like. And this is the idea that it's still a header implementation, but it's putting it inside the kind of data that it will accept. And this is interesting in that we start to get into the concept of content versioning, not just API versioning. Because remember, when we're talking about APIs, we're talking about URIs as sort of the beginning point. But we also have in many cases, the body of a request. And that request, whether it's a post or a put or a delete. You want to know that the structure of the data they're sending is a certain version, not just the URI has changed. Because it's often changes in just the way the bodies are returned, not the way the URIs have changed. In this same way to sort of extend the complexity scale, there's also being able to specify your own content types. And this is one when you start to get really down a rabbit hole of complexity, this is one of the more complex ones. And this is where you would specify a content type for the data that's being sent that includes a version. And then the client could tell you what version they're going to accept as well. So it allows you to understand what data is being sent with a version and what data is being received. Now this is usually only used in pretty complex scenarios. GitHub's 3.0 version uses this, for example. And the reason for this is you might get information from something like GitHub, hold on to it for a week and then try to make some update and send it back. And so the version, if there's been any granular change since then, they're going to want to know when you got that data. And so while this is probably the most bulletproof of the versioning strategies, it's also much more difficult to accomplish. And so finding sort of that middle range of what is acceptable is really what you're going to want to look at. So let's dive into some code. If you see me talk before, you know that I hate slides. And so let's dig into an example. So I have an example, and I'll have this out on my blog later on today so you can get a copy of this if you want. And it's a pretty standard ASP.NET 3.0 project. It's essentially just an API project that when we run it will allow us to get some data. And I'm going to do all the getting of data here in Postman. And we could see that I just went to an API, got customers, and are getting data. And I haven't done anything with versioning yet. But I've decided that I need to deal with versioning in some way. And so let's see how to do that. Back in the code, let me close all of these so we can look at this pretty simply, is I'm going to start in the startup.cs in a.NET project. And if we look at the way that Configure Services, I'm going to collapse this so you can see a little bit more, we want to in Configure Services actually add support here for versioning. We do that by just going to the Services and saying Add API Versioning. Now, of course, we get the little squiggle because we don't have the package reference yet. So we'll need to go over to NuGet. And what we're looking for is I usually just search for Microsoft Versioning, and that usually brings it up. To see here, this is a package developed by Microsoft, but it is a separate package. And it's on GitHub if you want to see how it all works. And we're in a preview right now, but this should be in a release pretty soon. Microsoft ASP.NET Core, FBC Versioning. And here you'll also see a couple of others that are interesting to note. Of course, it won't show me the full library. But there's one for versioning of ASP.NET for Before.NET Core, and there's also ones that will allow you to do something called an API Explorer, which is a tool that that same team has put together. The last thing I'll mention is if you're doing OData Versioning, they also do that as well. But we're going to focus on API Versioning in ASP.NET Core, so let me just install that. Whenever these license agreements come up, don't read them just to accept blindly, because I think that's the way that works. Now that it's installed, we should see that API Versioning is now no squiggle. We can build it and run it. When we go back to Postman, now that we've turned on API Versioning, we haven't done anything about specifying it. We've just turned it on. When we send it, you're going to see that there's an error code. In fact, we're getting a bad request. No API Version specified, right? And this assumes that the default behavior here for API Versioning is that we need to specify it in all cases. That's not really what we need yet. So let's talk about how to configure this. We can pass in a lambda, which if you've done enough .NET Core is pretty much the standard to configure anything these days. And I'm going to first do default API Version. I'm going to specify an API Version that is the default when no one specifies any information. And I do this by creating an API Version. Let me add that namespace. And I'm just going to say 1 1 is the default. We have some 1 0 things, but 1 1 is going to be the default. And I'm also going to specify that when it's unspecified, this is the default should be assumed. So this specifies what the default version of every API is. And this says if the client hasn't specified one, should I use this as the default when somebody calls us? So if we go ahead and look back at Postman, we'll see that it works again. And it works again mainly because I haven't specified any versioning here. So it's assuming I'm asking for 1 1 and that the API I'm calling is 1 1 because I haven't told it about it. The last thing I'll put in here, which I normally put in just so we can see it, is Report API Versions. Report API Version says on every call, tell the user what API versions are supported. And it does this by using a header. So let me execute it again with the new code. We'll see that there is a new header called API Supported Versions. And because it only knows about 1 1 so far, the default one, it's reporting that that's the supported. Now because it's in a header, it doesn't have to be exposed to a lot, but you can have users read that header and be able to figure out what versions are supported. I like that mostly so that while I'm debugging, I can see what versions are supported for a particular API. Some of you are going to want to use something called Conventions, which I won't really get into. We don't have time to get into it, but it's a way to specify your APIs using a Fluent Syntax. So you can say this controller has this API and this method has this API version. And that is certainly a way to do it. The other way that we're going to do is actually use attributes. So I'm going to open up my customer's controller. It's a pretty standard .NET 3.0 API. And we can see that we have no versioning information. So I'm going to first do API version as a new attribute. And this says what API versions are supported by this controller? Hasn't said what we haven't said yet how to differentiate between them. We're just advertising that this controller has these API versions. And in fact, we come back and send this again. Look at the headers. We'll see that both versions are now reported because we've advertised them on the controller. But how do we differentiate between these two? Let's come down to where I'm getting an individual ID. So we're getting something like just a single body here. And let's make two versions. So I'm going to use editor inheritance. I'm not sure if you're familiar with that term. Some people call it copy paste. I like the term editor inheritance. And we're going to create a second version. So here I'm going to say map to version. And I'm going to tell it that this is the 1.0 version. This is the one we've always had. This is 1.0. And then down here, I'm going to say map to version 1.1. And of course, it's the same method. It's the same signature. So I'm going to need to use a different name. More because it's C sharp than versioning. You can't have the same methods, same name, same number of attributes. If your 1.1 had a different URI, you might not even need a version of this. But you wouldn't need to rename it if the parameter numbers have changed. And so in our new version, I'm just going to include the order information. So the default for 1.1 is going to be get the orders. Default for 1.0, which was to eliminate, was to not include the orders. So let's see what happens here. I'm going to go ahead and send this. Go clockwise to make it speed up. We're going to see down here we have the order number. And that's because by default, we're getting that 1.1 version. How do we specify a different version? We specify it by default by using a query parameter called version, API version. And I'll talk about how to change this to the different schemes in a minute. But all of what we talked about is not really tied to any one scheme. But by asking for the 1.0 version, we can now see the orders are now null. So we're calling two different versions of the API simply by changing whether we're running without versioning requests or we're specifying or opting into a specific version. So now that we have very simple versioning, we can see how in the same controller. And you can also do this at the controller level. You could have a 1.1 version of the controller that had the 1.1. But this allows you to piecemeal and decide at what level do you want to do it. There's also a scheme in here to allow you to version the APIs based on folder names. So you could have the same classes and different namespaces. And those namespaces would include information about how to map it to a particular version. But I won't get into that now. The docs explain that stuff really well. I just want to kind of whet your appetite. One of the interesting things here is I don't know if you're like me, but question mark API version sort of makes me sick. I don't like this long verbose. And as a default, it's fine. But I want to change the way we do this. I want to actually use a header because I like that it's a little more disconnected. And so we can do this back in the configuring of the API versioning. But just saying configuring API version reader and assign it a new reader. So this version reader is a class that knows how to look at the request and figure out what the version is. So the version reader does this in different ways. And by default, it's actually using one called a query string API version reader. And I'm going to bring in the namespace of sp.net core MVC versioning. And this is by default the reader it's using. That's why it's reading it from the URI. But instead, I'm going to use header API versioning reader. And I'm going to actually specify what header names. And notice the I don't know how well you can see it. But you can actually specify more than one header name if you had some different header names. But I'm just going to specify one as X version. There's no magic there. You could really use any string you want as long as it's not going to collide with other headers. And by simply changing the reader, if we go ahead and send this, we'll actually see because we're getting orders that we're getting the one one version even though we had the API because we're no longer using that query string. It's just being ignored. But over here in headers, we can go ahead and add version equals, whoops, 1.0, which adds that header for us. The request header more importantly. And then we can go ahead and get that 1.0 version versus the 1.1 version. Last thing I'll show you because it comes up quite a lot is what if I want to support both? Like, what if I want to have more than one scheme supported? You can do API version reader.combine. This is a static method on it that allows you to add more than one reader. So in this case, I'm going to add a header. But I'm also going to add that query string version reader. And I'm going to use v as the parameter of the query string version reader instead of that API dash version because I like just a very short. It being so short means we might collide with some existing functionality, but should be good enough for now. And by making that change, this version 1.1 will certainly work getting our orders. But we can also use a query string and just say v equals 1.0. And now we're getting the version with the orders being null. And if we have both, we're actually going to get an error that says ambiguous API version. And this is important in that this is not an order of which one wins. This isn't an or. So if you're specifying both, they must match. But most people, you should never specify both. Just make sure that if you do specify both, that they're the same. But it's really so that users can use one or the other. See how we're doing on time? Oh, we're just about out of time. Let's head back to the slides and the magic Q&A questions. So you guys have any questions for me? Yeah, you got a great presentation. Go ahead, Sean. Well, it's Shane. Well, it's Shane. I'm sorry, Eric. I've only known you for a few years. I got Shane and I got Sean. All right, Sean. Actually, we do have a couple of questions. I can't hear you guys at all just so you know. Oh, let me see. Hey, can you minimize your Skype? Let's see. There we go. And can you hear us now? OK. Let's try it again. How about now, sir? Sorry about that. The button to unmute or Skype on our end didn't cause a plan. So can you hear us now? I can. Perfect. Thanks, Sean. We do have a couple of questions. Is there a concept of versioning the open API docs? So I'm assuming like this is like Swagger, right? So we can add Swagger. And is this reflected like in the Swagger docs when we add that to our APIs? I haven't done it, but I've been assured it is. So this works pretty well with the Swagger stuff. But I haven't done it personally, so I don't want to over-promise. Sure, no problem. And then one other one here is I don't know why you would, but could you configure versioning for both headers and query programs? Yeah, and in fact, I just showed that in the demo and that'll be in the source version. So they're mutually exclusive? So absolutely. They're not mutually exclusive. You can use both. In fact, you can use as many as you want. As long as they, if someone specifies both, they have to match the same version number. But usually, you're going to do this so that people can specify them in different ways, not at the same time. Perfect. Sweet. Well, is that any more questions? I haven't seen any more in the chat. Awesome. Either it's clear, everyone's still asleep. Yeah, no, it's great. No, we had like 700 people when you were speaking. So we got plenty of folks on Twitter. Awesome. I like to tell the speakers after the fact because it's easier that way, right? It is. It is. All right, well, thanks so much for having me on the show. Yeah, thank you so much, Sean. This was really, really great. Up next, we got Kerry Payette talking about Asher Sphere. So we're going to switch things here around. We're going to the slate, and we'll be right back.