Join Jeff Fritz for an in-depth discussion in this video In-the-box middleware, part of ASP.NET Core: Middleware.
- [Instructor] That's enough about the theory. Let's actually server some code and see how middleware works inside of our applications. To start off our samples here, I've built a brand-new project that is an empty ASP.NET Core application. There's nothing else added to it except or the default content, and on line 38, you can see I have a simple Hello World! statement being returned. I can run my application from the command line by running dotnet run.
These are actually files that are only included in our application by this line of middleware. So you can see inside my solution, I have an index.html page. And my index.html page is actually quite simple, Hello from my index page. Because my startup is configured to include static files already, I can browse to index.html, and that content will be returned. And I get Hello from my index page.
But if this line is not here, and I restart my application, return to my browser, and refresh the request for index.html, I only get Hello World! ASP.NET Core doesn't know how to reach outside of its pipeline to find that HTML file and return it. This is very important to understand, that these things are only added, these are features that are only added if you instruct ASP.NET to include these.
I'm going to go back to my command line and kill the process, and come back over here to my source code. I'm going to put static files back into the mix here, and then next piece that I'm going to look at it is this DeveloperExceptionPage on line 30. So we see, again, there's this Use keyword at the beginning of my method after the app reference. Use is, by convention, a definition of some middleware to use and pass control onto the next thing in the pipeline that's available.
So UseDeveloperExceptionPage is going to use the developer exception page and pass control along and listen in case there's an exception. If there is an exception on the way back up the stack from the bottom, it will appropriately handle that exception and do something instead. In this case, it'll show us an exception handler page that we may want to see only when we're in development mode as the test on line 28 is providing.
So I'm going to go back over to my command line and set this to developer mode by defining the ASPNETCORE_ENVIRONMENT=Development. And now let's throw an exception down here inside of my application. Let's go back over to the command line, and I will run again. It's picking up that there's unreachable code detected.
It can't reach my context, write Hello World! When I go over to my browser and I request index.html, it returns the index.html because that's a terminating piece of middleware. It came down and saw the static file that terminated and returned, and there was no exception thrown. So if I browse to somewhere else, and it can be as simple as somewhere else, it's going to handle and show me this exception page.
This is actually a pretty good exception page for a developer to see. This is something where you're able to see exactly what's going on under the covers of my application because I'm seeing the stack, where the exception was thrown. I can see any query information that was submitted, any cookies that came in, and of course the headers that were submitted to my application so that folks can interact with it. So this is information about the browser that came through.
Well, that's interesting. That's nice. And it's something that we only want to show to a developer. So it's a good thing that we have that if statement around this. Now, you notice that this only executed when it came down into this piece of middleware on line 39. Remember, this configure method is executed only once at startup, and these delegates, like the content of my app.Run method are executed every time a request comes through.
Consequently, if I move this exception here, that exception will be thrown at the time that the configure method is executed, and my application won't actually start. There, you can see in the log on the console the error that was thrown on my initial request when the exception was being thrown inside my app.Run. I'll clear that, and let me dotnet run again, this time, with the exception being thrown after the UseStaticFile statement.
And here you see the web server doesn't even start. The exception happens, and that developer exception page doesn't even have a change to be handled, because our dotnet process failed while trying to configure the application. Because configure failed to complete, our application did not start, and we're greeted with this error. So make sure your configure method completes properly so that the process can be handed off to the Kestrel web server, and you can start to receive and interact with your web visitors.
So, I'm going to remove this exception. And we can start talking about some other ways that we can write middleware. The first way that we can write middleware is actually demonstrated here on line 35 by providing an interaction with the Run command. The Run command is a terminating piece of middleware that receives the context, the HTTP context, that was submitted as part of the request and then performs some sort of a task.
Anything that occurs after that is not actually processed by the Kestrel web server. So if I put down here app.Run, and all the elements of our pipeline are asynchronous. This way, threats can be released to other capabilities inside of our web host so that we can balance our resources more effectively. This second statement will never be executed.
I'll rerun. Go back over to my web browser. Refresh this, and I only get Hello World! There is nothing that can happen after a Run statement. So line 35 never passes the context, never passes the control of the pipeline, onto what happens at line 41 or later. So, these Run statements should be the last element on our pipeline.
I'll remove that section. But the Use statement allows us to receive a delegate called Next that we can pass along to the next element in the pipeline.