Coding Shorts: ASP.NET Core Middleware Explained

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome back to coding shorts my name is sean wildermuth today's coding short isn't that short in fact my wife talked me out of calling it coding almost short running a little long for what i wanted this series to be but i thought this was an important topic over the next few videos i'm going to be talking about asp.net core middleware this is an important topic really understanding how middleware works where you would use it and how the order of the middleware matters is something that's really important to me in today's episode i'm going to be talking about what middleware is how it works and how to write your own middleware let's get started [Music] so before we dig into how to write your own middleware let's talk a little bit about how the middleware actually works so if you've ever created a brand new asp.net core project you'll see that middleware is being registered you might be familiar with static files routing authorization maybe you're mapping controllers maybe you're mapping razer pages each one of these is actually a piece of middleware and when i talk about middleware i just mean it has some code to run so if we start out and we want to go to some page on our project what actually happens asp.net core starts the scope for the process and then starts to call the middleware by calling the first piece of middleware and in that case it's going to come in and it's going to look at whether this is a static file or not if it can't because it this is a an earl to an individual page it then calls the next piece of middleware it may be doing some things that are trying to fulfill the request or it's just adding some context to that request which is the way that routing and authorization work so they're always going to pass it to the next piece finally when we get to map controllers it's going to look at all the routes it has in this route table and it's not going to recognize this url so it's going to go i can't handle it either let me call the next and finally we're down here in razor pages and it does know about this but if it didn't know about it it would also say you know what no one could fulfill it therefore it's going to end up being a status code 404 right but in our case some page is an actual razor page that it can map to and so it fulfills the request by filling in the response and then it just returns which takes it back to map controllers authorization routing static files and finally after static files returns it goes ahead and sends that content to the user what's important to realize here is that each of these pieces of middleware doesn't know about each other it just knows that it can call the next person in the chain that's it and it has an opportunity before it calls the next piece of middleware to do some work and to do some work after the call to the next piece of middleware returns so we look at something like an api we have that same process the request comes in static file says nope not me add some contacts add some context map controllers goes oh i know this route i can call the controller to fulfill the request and it does and then it returns and notice because map controllers could handle it razer pages middleware was never called and that's intentional the same way if we do have a static file that static file request comes in static files says oh i know that file let me return it fills the request fills the response and the returns without any of the rest of the middleware being called and this is an important idea here because you can't guarantee that your piece of middleware is being called but also that the order matters one of the reasons that static files is first is that when you start to think about how a typical web page works there are more static files than any request into razer pages or controllers right you have images you have css you have javascript all these individual files that are being returned let's look at some code so i'm back in visual studio and i'm going to open up program.cs because i'm using.net 6 in this case but this should be similar if you're using an older version and what i'm going to talk about is the same across most versions of asp.net core so you can see those familiar pieces of middleware that we were sort of alluding at that are being called one after another and so every time one of these uses being called a piece of middleware is being registered for it so one way to add middleware is actually just to call use itself uses saying i want wanna inject myself into this chain and i have some very little work to do so we're gonna start here in our case we're gonna say it's asynchronous and we're gonna expect two pieces of data being passed to us context which is the http context and next which is a function that calls the next person in line and so this next becomes pretty important because this is how my middleware will know how to call the next one i'm not going to know who that is i'm not going to know if the programmer changed the order of this or any of that i'm just going to know when i'm done with the job i need to do i need to then call next invoke and pass in context because this is just a function underneath the covers you could just call next with parameters and that's the way it was done in older versions but it's recommended now that we pass the context in because it results in fewer allocations but we want to actually do some work here so i'm going to start by creating a start that just has a starting date value more importantly a time value and what i'm going to do with that information is i'm going to actually call at logger.log information and i'm going to tell it how long this request took to be fulfilled so again i'm able to do stuff before i call next and i'm able to do stuff after next executes so this next is going to call the next piece of middleware who are going to call the next piece of mirror all the way down the line until someone can fulfill the request i don't need to know any of that information so i'll say request duration in milliseconds and how am i going to get that i'm going to say date time utc now so what time is it right now and i'm going to subtract that start that's going to give us a time span so i can just call total milliseconds i have all the information i might want in that context so i could say request context.request.path so that i can see the request path as well as how long it took right and this is now a complete set of middleware now it's not packaged in the way we might want to but it is a complete successful piece of middleware let's run this real quick we can see our piece of middleware didn't interrupt any of the things that normally happen with this project but if we come over to the console we can see that a request for root did what it fulfilled it and it took them a second and a half and probably took a second and a half because some things were warming up in the startup of these pieces of middleware the second request which was what testing.styles.css a static file took less than a half a millisecond right a tiny amount of time because all it was doing was returning one piece of item and so we have middleware that's actually doing something let's close this back in visual studio let's talk about another kind of middleware and in this case we're not going to make it asynchronous because what we will actually want to do is say return test completed task and i'll use that app.logger again to log information saying terminating the request right where what's important here is instead of calling next i may have done some logic here where i can say you know what i'm not calling the next person so just return a completed task of course it's complaining here because the way we're using it as a lambda here it can't really figure out which overload we're trying to use so we have to give it a couple of hints so this is going to be http context and this is going to be actually a funk that returns a task now because we're doing it this way now it's perfectly happy and let's run that project again knowing that this piece of middleware is going to call this piece of middleware notice the webpage doesn't come up we look at the console again so i wish we could look i wish we could zoom that a little better for you it says the request for root took five seconds or five milliseconds and it took so little because all we did was terminate the request right we didn't do anything but return with a failure here so there are times when you want to be able to build middleware that is going to be what they call a terminating middleware where it just doesn't call the next or in most cases you're going to want to just call the next like we do here this isn't packaged very usefully or being able to reuse it and it doesn't really feel like they use authorization or map razor pages and more importantly it also doesn't have the services either so first let's go ahead and build a new piece of middleware in a more appropriate way so i'm just going to create a new file and i'll call it timing middleware it doesn't matter what you call it so in this public class i'm actually going to get rid of that one indention with the namespace i love dotnet 6 and c 10 because of that so i'm going to first create a public constructor that takes a couple of pieces and what i'm going to expect is timing middleware is going to have a logger just like you might be used to and because we're going to use this as middleware it actually also fulfills a request for a request delegate what is the request delegate it's actually the next call right it's where we're in the lambda we're using next when we actually called it we're using this when we create this piece of middleware to assign what the next piece is and so here i'll just make some fields to hold my data and then the real magic happens in a call that looks a lot like what we saw in the lambda but isn't in the lambda right and here we're going to call invoke and expect a context we're only expecting a context in this case because the next delegate was passed into the constructor for us and here we can come back to program.cs and just copy some of this code so first of all i'm going to going to comment out our terminating middleware because boo hiss right and here i'm just going to copy what we're doing here i'll leave it here so you can see what it looks like in case you want to get the example but we're going to do the same thing we did before except we're going to get this from the past in next delegate as well as the logger right we're not doing anything different here that we did before just we're doing it in this more formalized class and this is useful because this class may be reused because it's going to be injected into the service collection for us and so if this is transient we're going to get a new one every time if it's scoped you actually have to change this a little to deal with scope and that's important if you're using other types of dependencies that need to be scoped dbcontext is a pretty common one you'll see there but i'm not going to cover that part what i'm going to do here instead say app use middleware and i'm just going to point it at our timing middleware now we've commented out these so it really should be calling these and i'll just put timing instead of requests so you can believe that we're getting this from our new piece of middleware not the old one let's run it again same thing happen our app works fine we can see timing timing instead of request right same things we were looking at before but it's not coming from that piece of middleware we can also see that the description of where it comes from is also that timing middleware let's close this and if you're in an organization or you want to share this across projects this works like you don't need to do a whole lot of magic here it just works but if you want to look and feel like other pieces of middleware or you're sharing it in an open source project that sort of thing there's some niceties that you can make it a little better and so back in our middleware i'm going to actually create a second class i'll call timingextensions and i'll make this a static class because we're going to want these to be static and this is going to return an application builder move this up so it's a little easier for you guys to see and it's just going to say use timing and this is going to be extension of the i application builder so just an extension method here that takes the app and calls use middleware timing middleware right just like we did manually though there might be some other operations here that we're going to care about and let me just return that and so that allows you to take our use middleware and make it a little cleaner and also allow it to pass in data if it needs to by calling it use timing we now look and feel like the others now in our case our middleware is pretty thin in what it needs to do but you may need things let's imagine that we had something like like an i timing object that we were going to use to do the timings maybe we weren't going to write all the code here but we wanted people to be able to supply their own so we could do something like this of course i'm going to delete this because we don't need that that does indicate that we might want a second call here right public static iservice collection static void add timing and this is extension method and what am i going to extend i service collection so that i can do services that add transient i timing some timing right so this gives you that opportunity to register those services and in fact when we look at something like ad razor pages most of what it does in here is just add all the services that razer pages needs so that when we call map razor pages later it doesn't fail to find those services that's why there's always been a pair of those it's just middleware and so i'm going to comment this out because we're not going to actually use it but you can see that the pattern for creating most middleware is creating these two extension methods as well as implementing a piece of middleware and notice there is no interface for this middleware because it can be created in so many different ways so that you can decide how you want to integrate it it just has to have a constructor that accepts the next request delegate so that's a requirement of it as well as follows this pattern of invoke with the http context it's not doing this by making sure that we have that interface the interface could be useful for this part but there'd be no way to guarantee this part because constructors aren't part of interfaces right and so don't be thrown by that this is just conventional middleware not contracted middleware like you would have with an interface hope this has helped you understand some middleware this is going to be the first of a multi-part series where i talk about a bunch of the built-in middleware that a lot of people aren't using to really show you that you can get some other benefits by using the middleware this was a way for us to show how you can write your own but then we're going to follow it up with a few more of these coding short videos talking about that middleware again if you've gotten this far hopefully you enjoyed it go ahead and like and subscribe i guess that's what us youtube people are supposed to tell you to do either you do it or you don't but it always helps please feel free to you know share this with your friends and co-workers that always helps us as well and i'm shaun wildermuith and thanks for joining me for another coding short [Music] [Music] you
Info
Channel: Shawn Wildermuth
Views: 21,657
Rating: undefined out of 5
Keywords: C#, ASP.NET, ASP.NET Core, .NET 6
Id: TqCshF0o0nE
Channel Id: undefined
Length: 17min 36sec (1056 seconds)
Published: Sun Jul 17 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.