Middleware is the heart of an ASP.NET Core application. You can think of middleware as small connectors which makes a pipeline to accept requests and send responses. Anything which your ASP.NET core application does is performed by middleware. Middleware are small application components that can be incorporated into an HTTP request pipeline like HttpHandlers and HttpModules. But middleware are different from HttpModules. In this post, we take a look at various ASP.NET Core Diagnostics middleware examples which are used for error logging and handling, exception handling and runtime environment information.
03.03.2017: Updated to ASP.NET Core 1.0 version
ASP.NET Core Diagnostics Middleware
ASP.NET Core comes with many built-in and ready to use middleware, and you add them to your application in the Startup.Configure
method. These built-in middleware can be classified in the following categories.
Built-in Middleware
Authentication | Provides authentication support. |
CORS | Configures Cross-Origin Resource Sharing. |
Routing | Define and constrain request routes. |
Session | Provides support for managing user sessions. |
Routing | Provides support for serving static files, and directory browsing. |
Diagnostics | Includes support for error pages and runtime information. |
In this post, we are interested in Diagnostics middleware. And some of the built-in diagnostics middleware are,
- UseDeveloperExceptionPage()
- UseDatabaseErrorPage()
- UseExceptionHandler()
- UseStatusCodePages()
UseRuntimeInfoPage()Removed in 1.0 release. Check here.- UseWelcomePage()
- UseElmPage() and UseElmCapture()
In any ASP.NET Core 1.0 RTM web application, you will find many built-in middleware are already added to the HTTP request pipeline. You can verify this in Startup.cs file -> configure()
method. Highlighted lines in below code, are built-in Middleware with ASP.NET Core 1.0.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseApplicationInsightsRequestTelemetry(); if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // For more details on creating database during deployment see http://go.microsoft.com/fwlink/?LinkID=615859 try { using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>() .CreateScope()) { serviceScope.ServiceProvider.GetService<ApplicationDbContext>() .Database.Migrate(); } } catch { } } app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear()); app.UseStaticFiles(); app.UseIdentity(); // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715 app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
Let’s take a look at diagnostics middleware.
app.UseDeveloperExceptionPage() Back to List
As you can see in the configure
method, this middleware is already added and it will come into picture the hosting environment is development. And by default, hosting environment is development. I shall show you later how you can change it. So as the name UseDeveloperExceptionPage()
says all that it’s going to execute on when an exception occurs and since it’s in a development mode it should show you the culprit line of code. You can say that it’s a replacement for yellow screen of death.
So let’s go ahead and add code which throws an exception. And the most common exception I could think of is “Object reference not set to an instance of an object”. So let’s add the following code in Index action method of home controller so that when we run the app, an exception occurs.
public IActionResult Index() { string sMessage = null; if (sMessage.Length > 0) { var sResult = sMessage.Substring(0); } return View(); }
Self explanatory code. And now when you run the application you should see following.
As you can see there are 4 tabs (Stack, Query, Cookie and Headers). Stack tab has information for culprit code (highlighted) and provides complete stack trace. Query tab has query string information (if any). Cookie tab tells you a list of all the cookies created by your application and the Headers tab has HTTP request header information.
On some blog post, you may find app.UseErrorPage()
to handle errors, that was present in beta stage. In RC 1, it’s not available. With app.UseErrorPage
, you had the option to choose which tab to show and which to hide.
app.UseDatabaseErrorPage() Back to List
Till RC1, this middleware was available in Microsoft.AspNetCore.Diagnostics
package so it was available out of the box as this package is available by default for the ASP.NET Core project. With RTM release, it is now available with "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore"
nuget package.
This is similar to UseDeveloperExceptionPage()
but it is used to display database related error details. The default ASP.NET Core application uses EF Code first approach to create a database and it provides functionality like user registration and login. It creates various DB tables and one of them is AspNetUsers
. So for demo purpose, I renamed this table to AspNetUsers_
so that login button fails and display the error. You should see the following error on-screen with DB error “SqlException: Invalid object name ‘AspNetUsers’.”
If we don’t add UseDatabaseErrorPage()
middleware and use UseDeveloperExceptionPage()
to handle even DB errors, then you should see the following screen.
app.UseExceptionHandler() Back to List
This middleware is also added by default to HTTP request pipeline in configure
method and it will be executed only when the environment is not development. Which makes sense as on production environment you don’t want to show the error stack trace or DB related errors which is meant only for development purpose.
As you can see in below code, it is configured to accept the error handling path. So, in case of any exception, go to Home Controller -> Error action method and executes Error()
action method to display the “Shared->error.cshtml” view.
if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Home/Error"); }
But let’s first change the environment to Production. To change it, right-click on your Project Files -> Select Properties -> go to the Debug tab. You should see “Environment Variables” section. You can change the value from here.
Now when any exception occurs, you should see following. You can test it with same code which we used in Home Controller to show “Object reference not set to an instance of an object” exception.
app.UseStatusCodePages() Back to List
This middleware can be used to handle the response status code is between 400 and 600. This middleware allows to return a generic error response or allows you to also redirect to any controller action or another middleware. To use this, add it to configure
method.
app.UseStatusCodePages();
This by default returns simple text-only handlers for common status codes. Below is the result of 404 error.
Please read How to handle 404 error in ASP.NET Core 1.0 to learn more about UseStatusCodePages
middleware.
app.UseRuntimeInfoPage() Removed in 1.0 release Back to List
This middleware adds an endpoint and provide details of the application’s environment. Again, it should be used in development environment otherwise you would be exposing your application’s production environment which you must not do. Use following code to add this,
if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); app.UseRuntimeInfoPage(); } else { app.UseExceptionHandler("/Home/Error"); }
And now run the application and add /runtimeinfo
to the URL and you should see your application environment. It will display your runtime architecture, .NET runtime version, list of all the packages and location from where they are loaded.
app.UseWelcomePage() Back to List
This middleware adds a welcome page when your application starts. Use “UseWelcomePage()
” to add this.
app.UseWelcomePage();
Once added, it will handle all the requests with a cool welcome page which uses embedded images and fonts to display a rich view.
There would be no links available on the page to go to other parts of the application. And even when you try to go to another page via URL, then also you would be redirected to this welcome page. Welcome page is a good option, when application’s work is in progress.
You can also configure this middleware to handle request for certain path.
app.UseWelcomePage("/welcome"));
So when you hit the URL http://localhost:49883/welcome
, then only welcome page will come.
app.UseElmPage() and app.UseElmCapture() Back to List
Till RC1, this middleware was available in Microsoft.AspNetCore.Diagnostics
package so it was available out of the box as this package is available by default for the ASP.NET Core project. With RTM release, it is now available with "Microsoft.AspNetCore.Diagnostics.Elm"
nuget package.
ELMAH
(Error Logging Modules and Handlers) is an application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment. ELMAH does not work with ASP.NET Core 1.0 as ELMAH is currently compiled for .NET 1.x and 2.0; run on .NET 1.x, 2.0. 3.x and 4.0. There is a middleware in the ASP.NET Core called “ELM” (Error Logging Middleware) that has some features similar to ELMAH.
To use ELM, we need to add 2 middlewares. One which capture the errors and other adds an endpoint to see the error log.
app.UseElmPage(); app.UseElmCapture();
And with RTM changes, also need to add ElmLoggerProvider service in ConfigureServices
method.
public void ConfigureServices(IServiceCollection services) { services.AddApplicationInsightsTelemetry(Configuration); services.AddElm(); services.AddMvc(); }
Now when you add /elm
to URL, then you should see something similar.
Here you have the option to filter the logs based on error, warning, information and other parameters.
These 2 needs to be used together. If you use only UseElmPage()
, then only the endpoint is added but no information. And if you use only UseElmCapture()
, then you are capturing information, but no place to display it.
You may also find this useful app.Use vs app.Run in ASP.NET Core middleware while adding middleware.
Summary
Middleware are great as it allows to have complete control on the request pipeline. In this post, you saw various diagnostics middleware examples to handle errors/exceptions, database errors, how to do error logging, handling of HTTP status codes and see run-time information. These are quite useful and save time at the time of development.
Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section.
I work on the Exceptionless project (http://github.com/exceptionless/exceptionless), we have support for capturing a bunch of information from asp.net core apps like 404’s and exceptions. It’s elmah on steroids :).