What’s in the ASP.NET Core MVC Template?

Espresso

ASP.NET Core MVC Template

When starting a new project, templates can be beneficial to use as a starting point. The standard boilerplate code gets generated, and before you know it, the all too familiar “Hello World” text gets printed on the screen. It almost feels like magic. While new project templates are something we have become accustomed to, the code that gets created on our behalf shouldn’t be a mystery. After all, we are the ones who will ultimately support it!

Getting Started

In ASP.NET Core, we can create a new project in one of two ways.

  1. Visual Studio
  2. .NET CLI

Both approaches are very clearly documented (thanks Microsoft!) so, I won’t go into detail here.

The Host Builder

Like any .NET application, our story begins with the Main method. If you’re entirely new to .NET, this method resides in the Program class. As you can see below, a Host gets created and launched via the CreateHostBuilder method. The host is responsible for starting up our application and managing its lifetime. Just as the name implies, the Host host’s our application. When creating a web application, it also configures the webserver by establishing the HTTP request pipeline. We will go into this in more detail in the following sections.

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

public class Program

{

public static void Main(string[] args)

{

CreateHostBuilder(args).Build().Run();

}

public static IHostBuilder CreateHostBuilder(string[] args) =>

Host.CreateDefaultBuilder(args)

.ConfigureWebHostDefaults(webBuilder =>

{

webBuilder.UseStartup<Startup>();

});

}

public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

It is also worth noting the CreateHostBuilder method returns a Generic Host via the IHostBuilder interface. .NET Core 2.1 introduced the generic host concept, which makes standard components such as dependency injection, logging, and application configuration available to many other application types. Console applications and worker services can now take advantage of these same features with ease.

StartUp “ConfigureServices”

As shown in the code snippet above, the web host builder references a Startup class. This class is the next step in fully understanding the ASP.NET Core template. The Startup class contains two critical methods for configuring our application.

  1. ConfigureServices
  2. Configure

The ConfigureServices method is called first and for a good reason. It is here that all of the application’s services are registered. Sometimes framework level services are registered. Sometimes they are services of our own. Sometimes both. The beauty is, once they are registered, they are accessible to the rest of the application via dependency injection. If you are not familiar with how dependency injection works, I recommend familiarizing your self with it. Below are a couple of links to help you get started.

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

// This method gets called by the runtime. Use this method to add services to the container.

public void ConfigureServices(IServiceCollection services)

{

services.AddControllersWithViews();

}

// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
        }

Our project only contains a single line in the ConfigureServices method after being generated by the template. As the name implies, AddControllersWithViews registers all services required for commonly used features with ASP.NET Core MVC. If we have any additional services we want to add, this is where we add them.

Startup “Configure”

Next in the line up is the Configure method. The configure method establishes an application request pipeline and ultimately drives how the application responds to HTTP requests. In the following code, each method represents a middleware component that gets added to the pipeline.

Plain text

Copy to clipboard

Open code in new window

EnlighterJS 3 Syntax Highlighter

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

{

if (env.IsDevelopment())

{

app.UseDeveloperExceptionPage();

}

else

{

app.UseExceptionHandler(“/Home/Error”);

// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.

app.UseHsts();

}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>

{

endpoints.MapControllerRoute(

name: “default”,

pattern: “{controller=Home}/{action=Index}/{id?}”);

});

}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler(“/Home/Error”); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: “default”, pattern: “{controller=Home}/{action=Index}/{id?}”); }); }

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();

            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

Reviewing these two methods brings up a question. Why would we want to split Configure and ConfigureServices into separate methods? There are two reasons for this. First, this split provides us with a separation of concerns. We have one method to configure our services and one to configure the application request pipeline. Second, it gives us the ability to inject dependencies into the Configure method as parameters. In the template, we can see the IWebHostEnvironment interface as a parameter to the Configure method. In the same way, we can inject any other services we have registered. This strategy is not always required, but as you can see below, it can be helpful in certain circumstances.

We aren’t going to go into much more detail on the middleware component architecture; however, I’ve included some additional links below if you would like to explore further.

WWWRoot

If you noticed the app.UseStaticFiles(); code in the Configure method, then you have probably guessed that static files get served via the HTTP request pipeline as well. To create a clear separation between server-side .NET code and static content, all static files are placed in the wwwroot folder. This folder typically consists of JavaScript, CSS, images, etc. but, not always HTML, as most of the HTML will get generated by the views. We will discuss this more in the following section.

Models, Views, and Controllers

Everything we have discussed so far can be found in any ASP.NET Core application. This post, however, focuses on the Model-View-Controller (MVC) template. If you’ve happened to stumble across my article, you are most likely already familiar with the MVC pattern. It has been around for quite some time. ASP.NET MVC 1.0 was launched about 11 years ago at the time of this writing. In the technology world, this is an eternity. There have probably been approximately 10,324,183 different JavaScript libraries launched in the last 11 years.

For those who are not familiar with MVC (or those that need a refresher), I will explain the MVC pattern in the following section. It is essential to understand the MVC pattern to understand the MVC template. But what is the best way to describe MVC? Most people describe the Model, the View, and then the Controller. Makes sense, right? I mean, it is called Model-View-Controller pattern after all. On the other hand, I like to think of MVC in terms of the life cycle of a request.

In frameworks like ASP.NET Core MVC, a request gets routed to a Controller. The controller receives input from the request then parses and validates it. If valid, the controller does some “stuff” with the data. This “stuff” could be saving to a database, calling a web service, or sending a message to a message bus. Whatever logic your application needs to perform is facilitated by the controller. Of course, it is essential to follow good architectural patterns without putting this logic DIRECTLY in the controller. A “service” as described above would be much better suited but, we won’t go into that here. The goal of this article is to describe the template and stay high level.

When the processing is complete, the controller will create a Model. At a high-level, the model will contain the data that we want to present to the user. I prefer to think of this model as a ViewModel. It is a model that provides data to view so, the name fits. It is important, though, that we don’t get this confused with the Model-View-ViewModel pattern, which is entirely different.

Lastly, we have the View. Using the data from the model, the view generates the HTML that gets returned to the user. ASP.NET Core has a robust view engine, called Razor, to facilitate this for us. With Razor, we can directly introduce C# code into our HTML markup. Try not to get confused with Blazor, though, as this code is executed server-side.

Now that we conceptually understand the MVC components, it comes as no surprise that each of them gets physically separated into their own folders. Some of this is even required. MVC follows a “convention over configuration” paradigm. For example, the views need to be in a specific folder structure to be routed to appropriately.

IN Closing…

I hope you enjoyed this article. As I said, project templates are powerful, but it is essential to resist the urge to dive-in to quickly. Ultimately, we are responsible for the applications we produce. It is necessary to understand each of the bits and pieces before we call it a day. Next, we will be discussing the newer ASP.NET Core Razor Pages template for those who want a more modern alternative to MVC.


This blog post was written originally on espressocoder.com Jason Robert.

Leave a Comment