4.35 out of 5
4.35
36 reviews on Udemy

ASP.NET Core 2.0 Introduction

Learn how things fit together under the hood as you build your first MVC applicaiton
Instructor:
Jonas Fagerberg
214 students enrolled
English [Auto-generated]
You will be able to crate a simple MVC web application using ASP NET Core 2.0.
You will know how to create a simple database using Entity Framework.
You will be able to use Dependency Injection to make functionality avaliable throughout yuour application.
You will be able so add new Middleware to the HTTP Pipeline.
You will be able to careate and add a custom service to the Services collection.

I would like to welcome you to this course that will guide you through creating your very first MVC application using an empty template. To get the most from this course, you should have a basic understanding of HTML and be familiar Object Oriented Programming in the C# language.

ASP.NET Core is a relatively new framework from Microsoft. It has been designed from the ground up to be fast and flexible and to work across multiple platforms. ASP.NET Core 2.0 is the framework to use for your future applications.

You will add the necessary pieces one at a time to get a good under­standing of how things fit together. The focus is on installing and configuring middleware, services, and other frameworks. Styling with CSS is not a priority in this course.

You will start by installing middleware to create a processing pipeline, and then look at the MVC framework.

Model classes are still used as data carriers between the controller and its views. There are, however, many new features that you will learn, such as Tag Helpers and View Components. You will also work with Entity Framework to store and retrieve data, and implement authentication with ASP.NET Identity framework. Note that dependency injection now is a first-class design pattern, which means that you need to learn it.

I sincerely hope you will enjoy this course.

Jonas Fagerberg

Introduction

1
Welcome to the course

Hi,

I would like to welcome you to this ASP.NET Core 2.0 course, where you will create your very first web application using an empty template.

2
Expectations

It's important that we set the expectations for this course from the get go.

Please watch this video to the end.

3
Setup

This video tells you more about the course and what version of Visual Studio you will be using throughout the course.

01 - Your First ASP.NET Core 2.0 Application and Service

1
01-01 Introduction

Your First ASP.NET Core Application

Welcome to the first module of this course: Building Your First ASP.NET Core 2.0 Application. I know that you are eager to get started, so let’s jump right in by creating your first solution and project.

If you haven’t already installed Visual Studio 2017 version 15.3.5 version or later, you can download a free copy here: www.visualstudio.com/downloads.

  1. Open Visual Studio 2017 and select File-New-Project in the main menu to create a new solution.
  2. Click on the Web tab and then select ASP.NET Core Web Application in the template list.
    1. Name the project AspNetCoreVideoCourse in the Name field.
    2.  Select a folder for the solution in the Location field.
    3. Name the solution AspNetCoreVideoCourse in the Solution name field.
    4. Make sure that the Create directory for solution checkbox is checked.
    5. Learning how to use GitHub is not part of this course, so if you are unfamiliar with GitHub, you should make sure that the Create new Git repository checkbox is unchecked.
    6. Click the OK button.
  3. In the project template dialog:
    1. Select .NET Core and ASP.NET Core 2.0 in the two drop-downs.
    2. Select Empty in the template list.
    3. Click the OK button in the wizard dialog.
  4. When the solution has been created in the folder you selected, it will contain all the files in the AspNetCoreVideoCourse project.
  5. Press Ctrl+F5 on the keyboard, or select Debug-Start Without Debugging in the main menu, to run the application in the browser.
  6. Note that the application only can do one thing right now, and that is to display the text Hello World! Later in this, and the next, module you will learn why that is, and how you can change that behavior.
  7. For now, just note that the application is running on localhost:55554 (the port number might be different on your machine).
  8. If you right click on the IIS icon in the system tray, you can see that ISS is hosting the AspNetCoreVideoCourse application.

 

2
01-02 Project Layout and Important Files

The Project Layout and the File System

There is a direct correlation between the files in the solution folder and what is displayed in the Solution Explorer in Visual Studio. To demonstrate this, you will add a file to the file structure in the File Explorer and see it show up in the Solution Explorer in real-time.

  1. Right click on the AspNetCoreVideoCourse solution node in the Solution Explorer and select Open Folder in File Explorer.
  2. When the File Explorer has opened, you can see that the solution file AspNetCoreVideoCourse.sln is in that folder, along with the project folder with the same name.
  3. Double click on the project folder in the File Explorer to open it.
  4. Right click in the File Explorer window and select New-Text Document and press Enter on the keyboard.
  5. A new file with the name New Text File should have been created in the folder.
  6. Now look in the Solution Explorer in Visual Studio; the same file should be available there.
  7. Double click the icon for the New Text File document in the Solution Explorer in Visual Studio, to open it.
  8. Write the text Some text from Visual Studio in the document and save it.
  9. Now switch back to the File Explorer and open the file. It should contain the text you added.
  10. Change the text to Some text from Notepad using the text editor (not in Visual Studio) and save the file.
  11. Switch back to Visual Studio and click the Yes button in the dialog. The altered text should now be displayed in Visual Studio. 
  12. Close the text document in Visual Studio and in the text editor.
  13. Right click on the file in the Solution Explorer in Visual Studio and select Delete to remove the file permanently.
  14. Go to the File Explorer and verify that the file was deleted from the folder structure.

As you can see, the files in the project are in sync with the files in the file system, in real-time.

Important Files

There are a couple of files that you need to be aware of in ASP.NET Core 2.0, and these have changed from the 1.0 version.

The Properties folder in the Solution Explorer contains a file called launchSettings.json, which contains all the settings needed to launch the application. It contains IIS settings, as well as project settings, such as environment variables and the application URL.

One major change from ASP.NET Core 1.0 is that the project.json file no longer exists; instead the installed NuGet packages are listed in the .csproj file. It can be opened and edited directly from Visual Studio (which is another change) or its content can be changed using the NuGet Package Manager.

To open the .csproj file, you simply right click on the project node in the Solution Explorer and select Edit AspNetCoreVideoCourse.csproj (substitute AspNetCoreVideoCourse with the name of the project you are in).

You can add NuGet packages by adding PackageReference nodes to the file .csproj, or by opening the NuGet Package Manager. Right click on the project node or the References node, and select Manage NuGet Packages to open the NuGet Manager.

One change from the ASP.NET Core 1.1 version is that there now only is one main NuGet package called Microsoft.AspNetCore.All that is installed when the project is created. It contains references to the most frequently used NuGet packages, the ones that you had to add separately in the 1.1 version.

Open the .csproj file and the NuGet manager side by side and compare them. As you can see, the same package is listed in the dialog and in the file.

 

 

You will be adding more NuGet packages (frameworks) as you build the projects.

Compiling the Solution

It is important to know that ASP.NET will monitor the file system and recompile the appli­cation when files are changed and saved. Because ASP.NET monitors the file system and recompiles the code, you can use any text editor you like, such as Visual Studio Code, when building your applications. You are no longer bound to Visual Studio; all you need to do is to get the application running in the web server (IIS). Let’s illustrate it with an example.

  1. Start the application without debugging (Ctrl+F5) to get it running in IIS, if it isn’t already open in a browser.
  2. Open the Startup.cs file with Notepad (or any text editor) outside of Visual Studio. This file is responsible for configuring your application when it starts.
  3. Locate the line of code with the string Hello World. This line of code is responsible for responding to every HTTP request in your application.
  4. Change the text to Hello, from My World! and save the file.
  5. Refresh the application in the browser. Do not build the solution in Visual Studio before refreshing the page.
  6. The text should change from Hello World! To Hello, from My World! The reason this works is because ASP.NET monitors the file system and recompiles the application when changes are made to a file.

You can create cross-platform applications using ASP.NET Core 2.0, but this requires the .NET Core template. As of this writing, this template has limitations compared with the .NET Framework template. This is because .NET Framework contains features that are relying on the Windows operating system. In a few years’ time, this gap will probably not be as significant, as the .NET Core platform evolves. So, if you don’t need the added features in .NET Framework, then use the .NET Core template, as it is much leaner and cross-platform ready.

The Startup.cs File

Gone are the days when the web.config file ruled the configuration universe. Now the Startup.cs file contains a Startup class, which ASP.NET will look for by convention. The application and its configuration sources are configured in that class.

The Configure and ConfigureServices methods in the Startup class handle most of the application configuration. The HTTP processing pipeline is created in the Configure method, located at the end of the class. The pipeline defines how the application responds to requests; by default, the only thing it can do is to print Hello World! to the browser.

If you want to change this behavior, you will have to add additional code to the pipeline in this method. If you for instance want to handle route requests in an ASP.NET MVC appli­cation, you have to modify the pipeline.

In the Configure method, you set up the HTTP request pipeline (the middleware) that is called when the application starts.

The ConfigureServices method is where you set up the services, such as MVC. You can also register your own services and make them ready for Dependency Injection (DI); for instance, the service that you implement using the IMessageService interface at the beginning of the course.

You will learn more about how to configure your application in the next module.

For now, all you need to know about dependency injection is that, instead of creating instances of a class explicitly, they can be handed to a component when asked for. This makes your application loosely coupled and flexible.

3
01-03 Creating a Configuration Service

Adding a Configuration Service

Let’s say that the hard-coded string Hello, from My World is a string that shouldn’t be hard-coded, and you want to read it from a configuration source. The source is irrelevant; it could be a JSON file, a database, a web service, or some other source. To solve this, you could implement a configuration service that fetches the value when asked.

Let’s implement this scenario in your application

  1. Right click on the project folder and select Add-New Item.
  2. Search for JSON in the dialog’s search field.
  3. Select the ASP.NET Configuration File template.
  4. Make sure the name of the file is appsettings.json. The file could be named anything, but appsettings is convention for this type of configuration file.
  5. Click the Add button.
  6. As you can see, a default connection string is already in the file. Remove the connection string property and add the following key-value pair: “Message”:”Hello, from configuration”. This is the file content after you have changed it.
  7. To read configuration information from the appsettings.json file, you have to add a constructor to the Startup class. You can do that by typing ctor and hitting the Tab key twice in the class.
  8. You need to create an instance of the ConfigurationBuilder class called builder in the constructor, and chain on the SetBasePath method with the application’s current directory as an argument. Without specifying the base path, the application will not know where to search for files.
  9. To get access to the classes used in the previous step, you have to resolve the following two namespaces by adding using statements for them. using Microsoft.Extensions.Configuration; using System.IO;
  10. To read the JSON appsettings.json file you need to chain on the AddJsonFile method, with appsettings.json as an argument, to the builder object. If you need to include more files, you can chain on the method multiple times.
  11. Add a property called Configuration, of type IConfiguration, to the Startup class.
  12. Now, you need to build the configuration structure from the ConfigurationBuilder object, and store it in the Configuration property. You do this by calling the Build method on the builder variable in the constructor.
  13. To replace the hard-coded text Hello, from My World! with the value stored in the Message property in the appsettings.json file, you have to index into the Configuration property. Store the value in a variable in the Configure method above the WriteAsync method call. var message = Configuration["Message"];
  14. Now, replace the hard-coded text with the variable. await context.Response.WriteAsync(message);
  15. Save all the files and go to the browser. Refresh the application to see the new message.
Creating a Service

Instead of using one specific source to fetch data, you can use services to fetch data from different sources, depending on the circumstance. This mean that you, through the use of configuration, can use different data sources according to the need at hand.

You might want to fetch data from a JSON file when building the service, and later switch to another implementation of that service, to fetch real data.

To achieve this, you create an interface that the service classes implement, and then use that interface when serving up the instances. Because the service classes implement the same interface, instances from them are interchangeable.

To get access to the services from the Configure method in the Startup class, or any other constructor, model, Razor Page, or view, you must use dependency injection. That is, pass in the interface as a parameter to the method.

You must register the service interface, and the desired service class, with the services collection in the ConfigureServices method, in the Startup class. This determines which class will be used to create the instance, when dependency injection is used to pass in an instance of a class implementing the interface.

In the upcoming example, you will inject a service class into the Configure method, but it works just as well with regular classes that you want to inject into a constructor, model, Razor Page, or view, using dependency injection. The same type of registration that you did in the ConfigureServices method could be applied to this scenario, but you wouldn’t have to implement it as a service.

You might ask how the IApplicationBuilder parameter gets populated in the Configure method, when no configuration has been added for it in the ConfigureServices method. The answer is that certain service objects will be served up for interfaces automatically by ASP.NET; one of those interfaces is the IApplicationBuilder. Another is the IHostingEnvironment service, which handles different environments, such as development, staging, and production.

When adding a service to the service collection, you can choose between several Add methods. Here’s a rundown of the most commonly used.

Singleton creates a single instance that is used throughout the application. It creates the instance when the first dependency-injected object is created.

Scoped services are lifetime services, created once per request within the scope. It is equivalent to Singleton in the current scope. In other words, the same instance is reused within the same HTTP request.

Transient services are created each time they are requested and won’t be reused. This lifetime works best for lightweight, stateless services.

Example

Let’s implement an example where you create two service classes that retrieve data in two different ways. The first will simply return a hard-coded string (you can pretend that the data is fetched from a database or a web service if you like), and the second class will return the value from the Message property that you added to the appsettings.json file.

You will begin by adding an interface called IMessageService, which will define a method called GetMessage, which returns a string.

Then you will implement that interface in a service class called HardcodedMessage­Service, which will return a hard-coded string. After implementing the class, you will add configuration for it and the interface in the ConfigureServices method and test the functionality.

Then you will implement another class called ConfigurationMessageService, which reads from the application.json file and returns the value from its Message property. To use the new service class, you must change the configuration. Then you will refresh the appli­cation in the browser to make sure that the configuration value is returned.

Adding the IMessageService Interface
  1. Right click on the project node in the Solution Explorer and select Add-New Folder.
  2. Name the folder Services.
  3. Right click on the Services folder and select Add-New Item.
  4. Select the Interface template, name the interface IMessageService, and click the Add button.
  5. Add the public access modifier to the interface (make it public).
  6. Add a method called GetMessage, which returns a string to the interface. It should not take any parameters.
  7. Save the file.
Adding the HardcodedMessageService Class
  1. Right click on the Services folder and select Add-Class.
  2. Name the class HardcodedMessageService and click the Add button.
  3. Implement the IMessageService interface in the class by clicking on the light bulb icon that appears when you hover over the interface name when you have added it to the class. Select Implement Interface in the menu that appears.
  4. Remove the code line with the throw statement and return the string Hardcoded message from a service.
  5. Save all files by pressing Ctrl+Shift+S on the keyboard.
Configure and Use the HardcodedMessageService Class
  1. Open the Startup.cs file.
  2. Locate the ConfigureServices method.
  3. To create instances that can be swapped for the IMessageService interface when dependency injection is used, you must add a definition for it to the services collection. In this example, you want ASP.NET to swap out the interface with an instance of the HardcodedMessageService class. Add the definition by calling the AddSingleton method on the services object, specifying the interface as the first type and the class as the second type. services.AddSingleton<IMessageService, HardcodedMessageService>();
  4. You need to add a using statement to the Services folder.
  5. Now you can use dependency injection to access the IMessageService from the Configure method.
  6. Remove the line that declares the message variable from the Run block.
  7. Replace the message variable name in the WriteAsync method with a call to the GetMessage method on the msg object, which will contain an instance of the HardcodedMessageService class. await context.Response.WriteAsync(msg.GetMessage());
  8. Save all files, switch to the browser, and refresh the application. The message Hardcoded message from a service should appear.
Add and Use the ConfigurationMessageService Class
  1. Right click on the Services folder and select Add-Class.
  2. Name the class ConfigurationMessageService and click the Add button.
  3. Implement the IMessageService interface in the class.
  4. Add a constructor to the class (you can use the ctor code snippet).
  5. Inject the IConfiguration interface into the constructor and name it configuration.
  6. Save the configuration object in a private class-level variable called _configuration. You can let Visual Studio create the variable for you by writing the variable name in the method, clicking the light bulb icon, and selecting Generate field…
  7. You need to add a using statement to the Microsoft.Extensions.Configuration namespace.
  8. Remove the throw statement from the GetMessage method and return the string from the Message property stored in the appsettings.json file. You achieve this by indexing into the Configuration object. return _configuration["Message"];
  9. Open the Startup.cs file and locate the ConfigureServices method.
  10. Change the HardcodedMessageService type to the ConfigurationMessageService type in the AddSingleton method call. services.AddSingleton<IMessageService, ConfigurationMessageService>();
  11. Add another call to the AddSingleton method above the previous AddSingleton method call. This time use the existing Configuration object and pass it to the method’s provider using a Lambda expression. This is another way to use the Add methods when you already have an object. You must add this line of code to prepare the configuration object for dependency injection. services.AddSingleton(provider => Configuration);
  12. Save all files by pressing Ctrl+Shift+S on the keyboard.
  13. Switch to the browser and refresh the application.
  14. You should now see the message Hello, from configuration, from the appsettings.json file.
4
01-04 Summary

In this module, you created your first ASP.NET application and added only the necessary pieces to get it up and running. Throughout this course you will add new functionality using services and middleware.

You also added a configuration file, and created and registered a service to make it avail­able through dependency injection in other parts of the application.

In the next module, you will learn about middleware.

02 - Middleware

1
02-01 Introduction to Middleware

Middleware

In this module, you will add middleware that handles HTTP requests, like how the applica­tion behaves if there is an error. One key aspect of the middleware is to perform user authentication and authorization.

By the end of this module you will have built a middleware pipeline for a MVC application.

2
02-02 How Does Middleware Work

How Does Middleware Work?

Let’s have a look at how middleware works and what it is used for.

When an HTTP request comes to the server, it is the middleware components that handle that request.

Each piece of middleware in ASP.NET is an object with a very limited, specific, and focused role. This means that you will have to add many middleware components for an applica­tion to work properly.

The following example illustrates what can happen when an HTTP POST request to a URL, ending with /reviews, reaches the server.

Logging is a separate middleware component that you might want to use to log informa­tion about every incoming HTTP request. It can see every piece of data, such as the head­ers, the query string, cookies, and access tokens. Not only can it read data from the re­quest, it can also change information about it, and/or stop processing the request.

The most likely scenario with a logger is that it will log information and pass the processing onto the next middleware component in the pipeline.

This mean that middleware is a series of components executed in order.

The next middleware component might be an authorizer that can look at access tokens or cookies to determine if the request will proceed. If the request doesn’t have the correct credentials, the authorizer middleware component can respond with an HTTP error code or redirect the user to a login page.

If the request is authorized, it will be passed to the next middleware component, which might be a routing component. The router will look at the URL to determine where to go next, by looking in the application for something that can respond. This could be a method on a class returning a JSON, XML, or HTML page for instance. If it can’t find anything that can respond, the component could throw a 404 Not Found error.

Let’s say that it found an HTML page to respond; then the pipeline starts to call all the middleware components in reverse order, passing along the HTML. When the response ultimately reaches the first component, which is the logger in our example, it might log the time the request took and then allow the response to go back over the network to the client’s browser.

This is what middleware is, a way to configure how the application should behave. A series of components that handle specific, narrow tasks, such as handle errors, serve up static files and send HTTP requests to the MVC framework. This will make it possible for you to build the example video application.

This course will not go into the nitty-gritty of middleware – only the basics that you need to build a MVC application.

3
02-03 IApplicationBuilder

IApplicationBuilder

The IApplicationBuilder interface injected into the Startup class’s Configure method is used when setting up the middleware pipeline.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMessageService msg) {     if (env.IsDevelopment()) app.UseDeveloperExceptionPage();     app.Run(async (context) =>     {         await context.Response.WriteAsync(msg.GetMessage());     }); }

To add middleware, you call extension methods on the app parameter, which contains the dependency-injected object for the IApplicationBuilder interface. Two middleware com­ponents are already defined in the Configure method.

The UseDeveloperExceptionPage middleware component will display a pretty error page to the developer, but not the user; you can see that it is encapsulated inside an if-block that checks if the environment variable is set to the development environment.

The UseDeveloperExceptionPage middleware component then calls the Run middleware component that is used to process every response. Run is not frequently used because it is a terminal piece of middleware, which means that it is the end of the pipeline. No mid­dleware component added after the Run component will execute, because Run doesn’t call into any other middleware components.

app.Run(async (context) => {     await context.Response.WriteAsync(msg.GetMessage()); });

By using the context object passed into the Run method, you can find out anything about the request through its Request object – the header information, for instance. It will also have access to a Response object, which currently is used to print out a string.

In the previous module, you called the GetMessage method on the message service in the Run method.

Most middleware components will be added by calling a method beginning with Use on the app object, such as app.UseDeveloperExceptionPage.

As you can see, there are several middleware components available out of the box using the app object. You can add more middleware components by installing NuGet packages containing middleware.

4
02-04 Handling Exceptions

Handling Exceptions

Let’s have a look at how exception messages are handled by the pipeline. As previously mentioned the app.UseDeveloperExceptionPage middleware is in place to help the devel­oper with any exceptions that might occur. To test this behavior, you can add a throw statement at the top of the Run-block and refresh the application in the browser.

  1. Open the Startup.cs file and locate the Run middleware in the Configure method.
  2. Add a throw statement that returns the string Fake Exception! to the Run-block.
  3. Add a using statement for the System namespace to access the Exception class.
  4. If you haven’t already started the application, press Ctrl+F5 to start it without debugging. Otherwise switch to the browser and refresh the application.
  5. A pretty error message will be displayed. Note that this message will be displayed only when in development mode. On this page, you can read detailed information about the error, query strings, cookie information, and header content.

Now let’s see what happens if you change the environment variable to Production and refresh the page.

  1. Select Project-AspNetCoreVideoCourse Properties in the main menu.
  2. Click on the Debug tab on the left side of the dialog.
  3. Change the ASPNETCORE_ENVIRONMENT property to Production.
  4. Save all files (Ctrl+Shift+S).
  5. Refresh the application in the browser.
  6. Now you will get an HTTP 500 Error- This page isn’t working error, which is what a regular user would see. If you don’t see this message, then you have to manually build the project with Ctrl+F5.
  7. Switch back to Visual Studio and change back the ASPNETCORE_ENVIRONMENT property to Development.
  8. Save all files.
  9. Refresh the application in the browser; you should now be back to the pretty error page.

Now let’s see what happens if we comment out the app.UseDeveloperExceptionPage middleware.

  1. Open the Setup.cs file and locate the Configure method.
  2. Comment out the call to the app.UseDeveloperExceptionPage middleware.
  3. Save the file.
  4. Refresh the application in the browser.
  5. The plain HTTP 500 error should be displayed because you no longer are loading the middleware that produces the pretty error message.
  6. Uncomment the code again and save the file.
  7. Refresh the browser one last time to make sure that the pretty error message is displayed.
  8. Remove the throw statement from the Run-block and save the file.

You can use the IHostingEnvironment object, passed in through dependency injection, to find out information about the environment. You have already used an if statement to determine if the environment variable is set to Development, if it is, a pretty error page will be displayed. You can also use it to find out the absolute path to the wwwroot directory in the project with the WebRootPath property.

5
02-05 Serving Up Static Files

Serving Up Static Files

A feature that nearly all web applications need is the ability to serve up static files, such as JSON, CSS, and HTML files. To allow ASP.NET to serve up files, you must add a new middleware component that is called with the UseStaticFiles method, located in the Microsoft.AspNetCore.StaticFiles NuGet package, which is installed with the default Microsoft.AspNetCore.All NuGet package.

Without the UseStaticFiles middleware component, the application will display the mes­sage from the Run middleware.

Let’s add an HTML file to the wwwroot folder and see what happens, and why. Static files must be added to the wwwroot folder for ASP.NET to find them.

  1. Right click on the wwwroot folder and select Add-New Item.
  2. Search for the HTML Page template and select it.
  3. Name the file index.html and click the Add button.
  4. Add the text An HTML Page to the <title> tag, and Hello, from index.html in the <body> tag.
  5. Save all files and navigate to the /index.html page in the browser.
  6. The message Hello, from configuration is displayed, which probably isn’t what you expected.

The reason why the message Hello, from configuration is displayed is that there currently is no middleware that can serve up the static file. Instead the message in the Run middle­ware, which is accessible, will be displayed.

Let’s fix this by adding a new middleware located in the Microsoft.AspNetCore.StaticFiles NuGet package, which is installed by deafult.

When the UseStaticFiles method is called on the app object, ASP.NET will look in the wwwroot folder for the desired file. If a suitable file is found, the next piece of middleware will not be called.

  1. Open the Startup.cs file and locate the Configure method.
  2. Add a call to the UseStaticFiles method on the app object above the Run middleware.
  3. Save all the files and start the application with F5.
  4. Navigate to the index.html file. The message Hello, from index.html should be displayed.
6
02-06 Setting Up MVC

Setting Up ASP.NET MVC

The last thing you will do in this module is to set up the ASP.NET MVC middleware and add a simple controller to test that it works.

The NuGet Microsoft.AspNetCore.Mvc package, which is installed by default, contains the MVC middleware that you will add to the HTTP pipeline and the MVC service that you will add to the services collection.

You will add a controller class with an Index action method that can be requested from the browser. In ASP.NET MVC, static HTML files, such as index.html, are not used. Instead views are usually used to serve up the HTML, JavaScript, and CSS content to the user. You will learn more about MVC in the next module. For now, let’s look at a simple example.

  1. Add a controller that can respond to the HTTP requests coming in to the application pipeline. The convention is to add controller classes to a folder named Controllers. Right click on the project node and select Add-New Folder and name it Controllers.
  2. Right click on the Controllers folder and select Add-Class.
  3. Name the class HomeController (the convention for a default controller) and click the Add button. The class doesn’t have to inherit from any other class.
  4. Add a public method named Index that returns a string, to the HomeController class. Return the string Hello, from the controller! in the method.
  5. Save all files and run the application (F5). Navigate to the index.html page. Note that the index.html file still is being served up to the user, displaying the text Hello, from index.html. This is because you haven’t yet added the MVC service and middleware.
  6. Stop the application.
  7. Delete the index.html file you added to the wwwroot folder; you won’t be needing it anymore since you want the controller to respond instead. You can do this either from the Solution Explorer or a File Explorer window in Windows.
  8. Open the Startup.cs file and locate the Configure method.
  9. Add the MVC middleware after the UseFileServer middleware method call, by calling the UseMvcWithDefaultRoute method. Adding it before the UseFileServer middleware would give the application a different behavior.
  10. Save all files and run the application (F5). You will be greeted by an exception message telling you that the necessary MVC service hasn’t been added. 
  11. Open the Startup class and locate the ConfigureServices method.
  12. Add the MVC services to the services collection at the top of the method. This will give ASP.NET everything it needs to run a MVC application.
  13. Comment out or delete the UseStaticFiles method call.
  14. Save all files and run the application (F5). Now the message Hello, from the controller! will be displayed in the browser. This means that MVC is installed and working correctly. In the next module, you will implement a more sophisticated controller.
7
02-07 Summary

Summary

In this module, you learned how to configure middleware in the Configure method of the Startup class.

The application now has several middleware components, including a developer error page and MVC. The MVC middleware can forward a request to an action method in a con­troller class to serve up content.

In the next module, you will learn more about controllers, and that you can use many different controllers in the same application, and how to route the HTTP requests to the appropriate one.

You will also create controller actions that return HTML, instead of just a string, as in the previous example.

03 - MVC Controllers

1
03-01 Introduction

MVC Controllers

In this module, you will learn about MVC, which is a popular design pattern for the user interface layer in applications, where M stands for Model, V stands for View, and C stands for Controller. In larger applications, MVC is typically combined with other design patterns, like data access and messaging patterns, to create a full application stack. This course will focus on the MVC fundamentals.

The controller is responsible for handling any HTTP requests that come to the application. It could be a user browsing to the /videos URL of the application. The controller’s responsibility is then to gather and combine all the necessary data and package it in model objects, which act as data carriers to the views.

The model is sent to the view, which uses the data when it’s rendered into HTML. The HTML is then sent back to the client browser as an HTML response.

The MVC pattern creates a separation of concerns between the model, view, and con­troller. The sole responsibility of the controller is to handle the request and to build a model. The model’s responsibility is to transport data and logic between the controller and the view, and the view is responsible for transforming that data into HTML.

For this to work, there must be a way to send HTTP requests to the correct controller. That is the purpose of ASP.NET MVC routing.

 

  1. The user sends an HTTP request to the server by typing in a URL.
  2. The controller on the server handles the request by fetching data and creating a model object.
  3. The model object is sent to the view.
  4. The view uses the data to render HTML.
  5. The view is sent back to the user’s browser in an HTTP response.

Routing

The ASP.NET middleware you implemented in the previous module must be able to route incoming HTTP requests to a controller, since you are building an ASP.NET Core MVC appli­cation. The decision to send the request to a controller action is determined by the URL, and the configuration information you provide.

It is possible to define multiple routes. ASP.NET will evaluate them in the order they have been added. You can also combine convention-based routing with attribute routing if you need. Attribute routing is especially useful in edge cases where convention-based routing is hard to use.

One way to provide the routing configuration is to use convention-based routing in the Startup class. With this type of configuration, you tell ASP.NET how to find the controller’s name, action’s name, and possibly parameter values in the URL. The controller is a C# class, and an action is a public method in a controller class. A parameter can be any value that can be represented as a string, such as an integer or a GUID.

The configuration can be done with a Lambda expression, as an inline method:

app.UseMvc(routes => {     routes.MapRoute(         name: "default",         template: "{controller=Home}/{action=Index}/{id?}"); });

 ASP.NET looks at the route template to determine how to pull apart the URL. If the URL contains /Home, it will locate the HomeController class by convention, because the name begins with Home. If the URL contains /Home/Index, ASP.NET will look for a public action method called Index inside the HomeController class. If the URL contains /Home/Index/ 123, ASP.NET will look for a public action method called Index with an Id parameter inside the HomeController class. The Id is optional when defined with a question mark after its name. The controller and action names can also be omitted, because they have default values in the Route template.

Another way to implement routing is to use attribute routing, where you assign attributes to the controller class and its action methods. The metadata in those attributes tell ASP.NET when to call a specific controller and action.

Attribute routing requires a using statement to the Microsoft.AspNetCore.Mvc name­space.

[Route("[controller]/[action]")] public class HomeController { }

Convention-Based Routing

In the previous module, you created a C# controller class named HomeController. A controller doesn’t have to inherit from any other class when returning basic data such as strings. You also implemented routing using the UseMvcWithDefaultRoute method, which comes with built-in support for default routing for the HomeController. When building an application with multiple controllers, you want to use convention-based routing, or attribute routing to let ASP.NET know how to handle the incoming HTTP re­quests.

Let’s implement the default route explicitly, first with a method and then with a Lambda expression. To set this up you replace the UseMvcWithDefaultRoute method with the UseMvc method in the Startup class. In the UseMvc method, you then either call a method or add a Lambda expression for an inline method.

Implement Routing
  1. Open the Startup class and locate the Configure method.
  2. Replace the UseMvcWithDefaultRoute method with the UseMvc method and add a Lambda expression with the default route template. app.UseMvc(routes => {     routes.MapRoute(         name: "default",         template: "{controller=Home}/{action=Index}/{id?}"); });
  3. Save the file and refresh the application in the browser. As you can see, the Index action was reached with the explicit URL /home/index.
  4. Now change to a URL without the action’s name, and only the controller’s name (/Home). You should still get the message from the action method, because you specified the Index action method as the default action in the routing template.
  5. Now call the root URL. A root URL is a URL with only the localhost and the port specified (http://localhost:xxxxx). This should also call the Index action because both Home and Index are declared as default values for the controller and the action in the routing template.
2
03-02 Adding Another Controller

Adding Another Controller

Now that you have implemented default routing, it’s time to add another controller and see how you can reach that controller.

  1. Right click on the Controllers folder and select Add-Class.
  2. Name the controller EmployeeController and click the Add button.
  3. Add an action method called Name that returns a string to the controller. Return your name from the method.
  4. Add another action method called Country that also returns a string. Return your country of residence from the method.
  5. Save the file and switch to the browser. Try with the root URL first. This should take you to /Home/Index as defined in the default route.
  6. Change the URL to /Employee/Name; this should display your name in the browser. In my case Jonas.
  7. Change the URL to /Employee/Country; this should display your country of residence in the browser. In my case Sweden.
  8. Change the URL to /Employee. ASP.NET passes the request on to the Run middleware, which returns the string Hello from configuration, using the ConfigurationMessageService that you implemented earlier. The reason is that the EmployeeController class has no action method called Index, which is the name defined as the default action in the default route you added earlier to the Startup class.
  9. Add a new method called Index that returns the string Hello from Employee to the EmployeeController class.
  10. Save the file and refresh the application in the browser, or use the /Employee URL. Now the text Hello from Employee should be displayed.
3
03-03 Attribute Routing

Attribute Routing

Let’s implement an example of attribute routing, using the EmployeeController and its actions.

  1. Open the EmployeeController class.
  2. If you want the controller to respond to /Employee with attribute routing, you add the [Route] attribute above the controller class, specifying employee as its parameter value. You will have to bring in the Microsoft.AspNetCore.Mvc namespace for the [Route] attribute to be available.
  3. Save the file and navigate to the /Employee URL. An exception is displayed in the browser. The reason for this exception is that ASP.NET can’t determine which of the three actions is the default action.
  4. To solve this, you can specify the [Route] attribute for each of the action methods, and use an empty string for the default action. Let’s make the Index action the default action, and name the routes for the other action methods the same as the methods.
  5. Save the file and refresh the application in the browser. Make sure that the URL ends with /Employee. You should see the message Hello from Employee in the browser.
  6. Navigate to the other actions by tagging on the route name of the specific actions to the /Employee URL, for instance /Employee/Name. You should be able to navigate to them and see their information.
  7. Let’s clean up the controller and make its route more reusable. Instead of using a hardcoded value for the controller’s route, you can use the [controller] token that represents the name of the controller class (Employee in this case). This makes it easier if you need to rename the controller for some reason.
  8. You can do the same for the action methods, but use the [action] token instead. ASP.NET will then replace the token with the action’s name. Keep the empty [Route] attribute on the Index action and add the [action] token to a second [Route] attribute so that it has two routes; this will make it possible to use either the base route /Employees or the /Employees/Index route to reach the Index action.
  9. Save the file and refresh the application in the browser. Make sure that the URL ends with /Employee/Name. You should see your name in the browser. Test the other URLs as well, to make sure that they work properly.
  10. You can also use literals in the route. Let’s say that you want the route for the EmployeeController to be Company/Employee; you could then prepend the controller’s route with Company/.
  11. Save the file and refresh the application in the browser. Make sure that the URL ends with /Employee/Name. You will not see your name in the browser; instead ASP.NET displays the text from the Run middleware. The reason for this is that there isn’t a route to /Employee/Name anymore; it has changed to /Company/Employee/Name. Change the URL in the browser to /Company/Employee/Name. You should now see your name again.
  12. If you don’t want a default route in your controller, you can clean it up even more by removing all the action attributes and changing the controller route to include the [action] token. This means that you no longer can go to /Company/Employee and reach the Index action; you will have to give an explicit URL in the browser to reach each action.
  13. Remove all the [Route] attributes from the action methods and change the controller’s [Route] attribute to include the [action] token. Save the file and refresh the browser with the URL /Company/Employee/Name. You should now see your name.
  14. Now navigate to the /Company/Employee URL. You should see the message from the Run middleware because ASP.NET couldn’t find any default action in the EmployeeController. Remember, you must give a specific URL with an action specified.
4
03-04 Implementing ContentResult

IActionResult

The controller actions that you have seen so far have all returned strings. When working with actions, you rarely return strings. Most of the time you use the IActionResult return type, which can return many types of data, such as objects and views. To gain access to IActionResult or derivations thereof, the controller class must inherit the Controller class.

There are more specific implementations of that interface, for instance the ContentResult class, which can be used to return simple content such as strings. Using a more specific return type can be beneficial when unit testing, because you get a specific data type to test against.

Another return type is ObjectType, which often is used in Web API applications because it turns the result into an object that can be sent over HTTP. JSON is the default return type, making the result easy to use from JavaScript on the client. The data carrier can be config­ured to deliver the data in other formats, such as XML.

A specific data type helps the controller decide what to do with the data returned from an action. The controller itself does not do anything with the data, and does not write any­thing into the response. It is the framework that acts on that decision, and transforms the data into something that can be sent over HTTP. That separation of letting the con­troller decide what should be returned, and the framework doing the actual transformation, gives you flexibility and makes the controller easier to test.

Implementing ContentResult

Let’s change the Name action to return a ContentResult.

  1. Open the EmployeeController class.
  2. Have the EmployeeController class inherit the Controller class.
  3. Change the Name action’s return type to ContentResult.
  4. Change the return statement to return a content object by calling the Content method, and pass in the string to it.
  5. Save all files, open the browser, and navigate to the Company/Employees/Name URL.
  6. Your name should be returned to the browser, same as before.
5
03-05 Using a Model Class and ObjectResult

Using a Model Class and ObjectResult

Using a model class, you can send objects with data and logic to the browser. By conven­tion, model classes should be stored in a folder called Models, but in larger applications it’s not uncommon to store models in a separate project, which is referenced from the application. A model is a POCO (Plain Old CLR Object or Plain Old C# Object) class that can have attributes specifying how the browser should behave when using it, such as checking the length of a string or displaying data with a certain control.

Let’s add a Video model class that holds data about a video, such as a unique id and a title. Typically you don’t hardcode a model into a controller action; the objects are usually fetched from a data source such as a database (which you will do in another module).

  1. Right click on the project node in the Solution Explorer and select Add-New Folder.
  2. Name the folder Models.
  3. Right click on the Models folder and select Add-Class.
  4. Name the class Video and click the Add button.
  5. Add an int property called Id; You can use the prop code snippet to add a property template by typing prop and hit the Tab-key twice. This will be the unique id when it is used as an entity in the database later.
  6. Add a string property called Title. Let’s keep it simple for now; you will add more properties later.
  7. Open the HomeController class and inherit the Controller class.
  8. You need to add a using statement to the Mvc namespace to get access to the Controller class.
  9. Instead of returning a string from the Index action, you will change the return type to ObjectResult.
  10. You need to add a using statement to the Models namespace to get access to the Video class.
  11. Create an instance of the Video model class and store it in a variable called model. Assign values to its properties when you instantiate it.
  12. Return an instance of the ObjectResult class passing in the model object as its parameter.
  13. Save all the files.
  14. Browse to the root URL or /Home. As you can see, the object has been sent to the client as a JSON object.
6
03-06 Introduction to Views
7
03-07 A View with a Data Collection

A View with a Data Collection

Now that you know how to display one video, it’s time to display a collection of videos. To achieve this, you’ll first have to create the video collection and then pass it to the view displaying the data. In the view, you’ll use a Razor foreach loop to display the data as HTML.

  1. Open the HomeController class.
  2. Replace the single Video object with a list of Video objects.
  3. Add a using statement to the System.Collections.Generic namespace to gain access to the List collection.
  4. Switch to the browser and navigate to /Home/Index, or start the application without debugging (Ctrl+F5), if it’s not already started.
  5. An error message will appear, telling you that you are sending in a collection (list) of Video objects to the Index view, when it is designed for a single Video object.
  6.  To solve this, you will have to change the @model directive in the Index view. You can use the IEnumerable interface, which is a nice abstraction to many different collections.
  7. When you change the @model directive, the @Model object no longer is a single instance of the Video class; you therefore must implement a loop to display the data in the model. Remove the @Model.Title property and add a table by typing ###strong/strong### in the <body> element and press the Tab key.
  8. Add a foreach loop around the <tr> element with Razor to loop over the Model object. Using Razor makes it possible to mix C# and HTML. Note that you don’t add the @-sign when already inside Razor code, but you use it when in HTML. Use the loop variable to add the Id and Title properties to the table row. The video variable in the loop doesn’t have an @-sign because the foreach loop has one. When the video variable is used in HTML, however, the @-sign must be used.
  9. Save all files, switch to the browser, and refresh the application. The three films should now be displayed.
8
03-08 Adding a Data Service

Adding a Data Service

Hardcoding data in a controller is not good practice. Instead you want to take advantage of dependency injection to make data available in a constructor, using a service compo­nent, like the Message service you added earlier.

One big benefit of implementing a service is that its interface can be used to implement different components. In this course you will implement one for Mock data and one for a SQL Server database.

In this section, you will implement a MockVideoData component that implements an interface called IVideoData.

The data will be implemented as a List<Video>. Note that a List collection isn’t thread safe, and should be used with caution in web applications; but this code is for experimental purposes, and the component will only ever be accessed by one user at a time.

To begin with, the interface will only define one method, called GetAll, which will return an IEnumerable<Video> collection.

  1. Right click on the Services folder and select Add-New Item.
  2. Select the Interface template, name it IVideoData, and click the Add button.
  3. Add the public access modifier to the interface to make it publicly available.
  4. Add a method called GetAll that returns an IEnumerable<Video> collection.
  5. Add a using statement to the Models namespace to get access to the Video class.
  6. Right click on the Services folder and select Add-Class.
  7. Name the class MockVideoData and click the Add button.
  8. Implement the IVideoData interface in the class.
  9. Add a using statement to the Models namespace to get access to the Video class.
  10. Add a private field called _videos of type IEnumerable<Video> to the class. This field will hold the video data, loaded from a constructor.
  11. Add a constructor below the _videos field in the class. You can use the ctor snippet and hit the Tab key.
  12. Open the HomeController class and copy the video list, then paste it into the MockVideoData constructor. Remove the var keyword and rename the model variable _videos to assign the list to the field you just added.
  13. Remove the throw statement in the GetAll method and return the _videos list.
  14. Now that the service is complete, you must add it to the services collection in the Startup class’s ConfigureServices method. Previously you registered the IMessageService interface with the services collection using the AddSingleton method; this would ensure that only one instance of the defined class would exist. Let’s use another method this time. Register the IVideoData interface using the AddScoped method; this will ensure that one object is created for each HTTP request. The HTTP request can then flow through many services that share the same instance of the MockVideoData class.
  15. Open the HomeController class and a using statement to the Services namespace.
  16. Add a private field of type IVideoData called _videos on class level. This field will hold the data fetched from the service.
  17. Add a constructor to the HomeController class and inject the IVideoData interface into it. Name the parameter videos. Assign the videos parameter to the _videos field, inside the constructor. This will make the video service available throughout the controller.
  18. Replace the hardcoded List<Video> collection assigned to the model variable in the Index action, with a call to the GetAll method on the service.
  19. Save all the files.
  20. Switch to the browser and refresh the application. You should now see the list of videos.
9
03-09 Summary

Summary

In this module, you learned about the MVC (Model-View-Controller) design pattern, and how the controller receives an HTTP request, gathers data from various sources, and creates a model, which is then processed into HTML by the view, along with its own markup.

You will continue to use MVC throughout the course and create Razor Views and more sophisticated views and models that can be used to view and edit data.

In the next module, you will learn more about model classes and how to use them with views.

04 - Models

1
04-01 Introduction to Models

Models

In this module, you will learn more about different types of model classes in the MVC framework and how to use them.

Up until now, you have used the Video class as a model for the Index view. In simple solutions that might be fine, but in more complex solutions, you need to use entity models and view models. Sometimes you even make a more granular distinction between the models, using Data Transfer Objects (DTOs) with the view models.

An entity model is typically used to define a table in a database. A view model is used to transport data from the controller to the view, but sometimes the view model needs to contain objects, and that’s where the DTOs come into play. Some might argue that DTOs are view models, and in some scenarios they are.

You will create a new folder called Entities and move the Video class to that folder. The reason for moving the file is that the Video class later will be used to define a table in a SQL Server database. A class used to define a database table is referred to as an entity. You will also add a new folder named ViewModels, which will hold the view models creat­ed throughout the first part of the course.

Important to note is that the view model typically contains other data than the entity model, although some properties are the same. One example is when a video is being added in a Create view. The view model needs some properties from the entity model, but could also need other information that is not stored in the Video entity and must be fetched from another database table, or an enum.

A view model is never used to directly update the database. To update the database the data from the view model is added to an entity model, which then in turn updates the database table.

Let’s look at an example where an enum is used to display the genre a video belongs to. For simplicity, a video can only belong to one genre.

2
04-02 Changing the Folder Structure

View Model Example

First you need to add an Entities folder and a ViewModels folder to the folder structure.

Changing the Folder Structure
  1. Create a new folder called Entities in the project.
  2. Move the Video.cs file from the Models folder to the Entities folder, using drag-and-drop.
  3. Open the Video class and change the Models namespace to Entities.
  4. Open the MockVideoData class and change the using statement from Models namespace to Entities.
  5. Open the IVideoData interface and change the using statement from Models namespace to Entities.
  6. Open the Index view and change the Models namespace to Entities for the @model directive.
  7. Open the HomeController class and remove any unused using statements.
3
04-03 Adding the View Model

Adding the View Model

In this video, you'll learn how to create a view model that sends and receives data from the view.

  1. Add a new folder called ViewModels to the project.
  2. Add a class called Genres to the Models folder.
  3. Replace the class keyword with the enum keyword and add some genres.
  4. Open the Video class and add an int property called GenreId to the class. This will hold the enum value for the video’s genre.
  5. Open the MockVideoData class and add a genre id for each video.
  6. Add a class called VideoViewModel to the ViewModel folder.
  7. The view model will contain the Id and Title properties, but you don’t want to display the genre id; it would be nicer to display the actual genre. To achieve this, you add a string property called Genre to the VideoViewModel class, but not to the Video class.
4
04-04 Using the View Model

Using the View Model

Now that the view model has been created, you need to send it to the view as its model. This requires some changes to the HomeController class and the Index view. You need to fetch the video from the _videos collection using its id, and then convert the genre id to the name for the corresponding value in the Genres enum.

When the view model has been assigned values from the entity object and the enum name, it is sent to the view with the View method.

  1. Open the HomeController class.
  2. Add a using statement to the System.Linq namespace to get access to the Select method and the System namespace to get access to the Enum class. Also, add a using statement to the ViewModels and Models namespaces to get access to the genre enum and the view model you added.
  3. Use the LINQ Select method in the Index action to convert each video into a VideoViewModel object, and store it in the model field. Use the Enum.GetName method to fetch the genre corresponding to the video’s genre id.
  4. Open the Index view and change the @model directive to an IEnumerable<VideoViewModel>.
  5. Add a new <td> for the genre.
  6. Switch to the browser and refresh the application. As you can see, the genres are now displayed beside each of the video titles.
5
04-05 Adding a Details View

Adding a Details View

Now that you know how to use the VideoViewModel to send data to the Index view, it is time to add a new view to the Views folder.

The Details view will display a single video in the browser, based on the id sent to the Details action you will add next.

  1. Add a new public action method called Details to the HomeController. It should have an int parameter named id, which will match a video id from the URL or the request data. The return type should be IActionResult, which makes it possible to return different types of data.
  2. To fetch the video matching the passed-in id, you must add a new method called Get to the IVideoData interface. The method should have an int parameter called id and return a video object.
  3. Now you need to implement that method in the MockVideoData class, and have it return the video matching the id parameter value. Use LINQ to fetch the video with the FirstOrDefault method.
  4. Add a using statement to the System.Linq namespace.
  5. Add a variable called model to the Details action in the HomeController class. Call the Get method to fetch the videos matching the passed-in id and assign them to the model variable.
  6. To test the Get method, return the model variable using an ObjectResult instance.
  7. Save all files and switch to the browser. Navigate to the /Home/Details/2 URL. The video matching the id 2 should be displayed.
  8. Change the return statement in the Details action to return the View method and pass in an instance of the VideoViewModel class filled with data from the model variable.
  9. Add a new MVC View Page file called Details.cshtml to the Views/Home folder in the Solution Explorer.
  10. Delete all content in the Details view.
  11. Type html and hit Tab to add the HTML skeleton to the view.
  12. Add the @model directive for a single VideoViewModel to the view. This enables the view to display information about one video.
  13. Add the text Video to the <title> element.
  14. Add a <div> element inside the <body> element for each property.
  15. Save all the files and switch to the browser and refresh. You should see the data for the video matching the id in the URL.
  16. Change the id in the URL and make sure that the correct film is displayed.
  17. Change to an id that doesn’t exist. An error should be displayed in the browser. The reason for the error is that the Get method will return null if the video doesn’t exist, and the view can’t handle null values for the model.
  18. One way to solve this is to redirect to another action; in this case the Index action is appropriate. Add an if-statement above the previous return statement, which checks if the model is null; if it is, redirect to the Index action. Implementing this check will ensure that the action won’t show the error message, and instead display the video list.
  19. Switch to the browser and refresh. The Index view should be displayed.
  20. Let’s add a link to the Index view in the Details view, for easy navigation back to the root. You can use a traditional HTML <a> tag, or you can use the Razor ActionLink HTML helper method. There is a new, third way to add a link, using Tag Helpers. You will explore Tag Helpers shortly.
  21. Switch to the browser and navigate to /Home/Details/2 URL. The view should have a link with the text Home. Click the link to get back to the Index view.
  22. Now open the Index view and add links for the video ids. To achieve this, you must pass in an anonymous object as an extra parameter, and add an id property to that object.
  23. Switch to the browser and go to the root (/). Click one of the links to view the details for that video.
6
04-06 Refactoring the Application

Adding a Create View

When creating a new record in the data source with a Create view, you have to implement two action methods. The first is a method using HTTP GET to render the Create view in the browser, filling select lists and other controls that need data. The second method is an HTTP POST method that receives data from the client through an HTTP POST request.

The post from the client can be done in several ways, for instance with JavaScript or a form post. In this example, you will use a form post to call back to the server when the user clicks a Submit button.

The HTTP POST action method can fetch data from several places in the posted data: the header, the query string, and the body of the request. The data is then matched against properties in a model object, which is a parameter of the action method. The action can also handle simple types such as int and string, without them being encapsulated in a model object.

There is a naming convention that you need to be aware of, to properly match posted form data with properties in model objects and other parameters. The rule states that the element names in the form data must match the property names to be matched.

The default behavior of a view using an enum is to display it as a text field. This is not the best way to display a selected item in a list of values. In this section, you will remove the Video class’s GenreId property, and add a new property of the enum type Genres called Genre. This makes it easier to work with enum data, especially when working with a SQL Server database entity model.

You will also add the enum as a property to a new view model called VideoEditView­Model, which can be used both when creating a new video and when editing one.

Refactoring the Application
  1. Open the Video class.
  2. Delete the GenreId property.
  3. Add a new property of type Genres and name it Genre. This property will hold the current genre for the video.
  4. Add a using statement to the Models namespace where the Genre enumeration is located.
  5. Open the MockVideoData class.
  6. Replace the GenreId property with the Genre property and assign its value from the enum directly.
  7. Open the HomeController class.
  8. Locate the Index action and change the assignment of the Genre string in the VideoViewModel object to use the value stored in the Genre property of the Video object. You can use the ToString method to fetch the name of the enum value.
  9. Repeat step 7 for the Details action method but use the model variable instead of the video parameter.
  10. Switch to the browser and refresh the application. It should look and work the same as before.
7
04-07 Adding the HTTP GET Create Action and the Create View

Adding the HTTP GET Create Action and the Create View

The HTTP GET Create action method renders the Create view to the browser, displaying the necessary controls to create a new video and to post the form to the server.

  1. Open the HomeController class.
  2. Add a new action method called Create, with the return type IActionResult. Return the View method.
  3. Add a MVC View Page to the Views/Home folder in the Solution Explorer and name it Create.cshtml.
  4. Delete all the content in the view.
  5. Add a @using statement to the Models folder, to get access to the enum definition for the select list.
  6. Add an @model directive with the Video class as the view model.
  7. To be able to use Tag Helpers, which is the new way to add ASP.NET specific markup to views, you have to add a @addTagHelper directive to the view, or a shared file. You will learn more about Tag Helpers later. @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
  8. Add an <h2> heading with the text Create Video.
  9. Add a <form> element and use the asp-action Tag Helper to specify the action to post to when the Submit button is clicked. Make the form post to the server by assigning post to the method attribute.
  10. Add a table with two rows to the form, one for the Title and one for the Genre enum.
  11. Use the asp-for Tag Helper to specify which property the controls should bind to. Add a <label> and an <input> element for the Title property.
  12. Use the same Tag Helper when you add the <label> and <select> elements for the Genre enum. To list the enum items, you must add the asp-items Tag Helper to the <select> element and call the GetEnumSelectList method on the Html class.
  13. Add a submit button with the text Create to the form.
  14. Add an anchor tag with the text Back to List below the form. Use the asp-action Tag Helper to specify that the link should navigate to the Index action.
  15. Save all files and switch to the browser. Navigate to the /Home/Create URL. You should see a form with a text field, a drop-down with all genres listed, a Submit button, and a link leading back to the Index view. The Submit button won’t work yet, because you haven’t added the required action method.
  16. Click the link to navigate to the Index view.

Adding the VideoEditViewModel Class

This view model will be used when the controller receives a post from a video’s Edit or Create view.

  1. Create a new class called VideoEditViewModel in the ViewModels folder.
  2. Add an int property named Id and a string property named Title.
  3. Add a property called Genre of type Genres. This property will contain the genre selected in the form when the submit button is clicked, and a post is made back to the controller on the server.
  4. Add a using statement to the Models namespace to get access to the Genre enumeration.
8
04-08 Adding the HTTP POST Create Action

Adding the HTTP POST Create Action

A <form> element is used when a user should enter data in a view. There are a few steps that are performed when a user posts data. The first you already know: the user sends an HTTP request to the HTTP GET action in the controller, which fetches the necessary data after which the view is rendered.

To handle the form’s post back to the server, an HTTP POST version of the action method is called with the form values. The names of the form controls are matched against the model properties or parameters available in the action’s parameter list.

The POST action then uses that data to create, update, or delete data in the data source.

When passing data from the view to the action, MVC will, by default, match all properties in the form with properties in the model. This can be risky, especially if you use an entity class as the model. In many scenarios, you don’t want to receive all data the form sends to the action. So how do you tell MVC to use only the values of interest? You create a separate view model, like you did in the previous section.

Let’s implement the HTTP POST Create action in the HomeController class.

  1. Open the HomeController class.
  2. Add a new action method called Create that takes the VideoEditViewModel as a parameter named model.
  3. Save all files and switch to the browser. Navigate to the /Home/Create URL. You should see an error message telling you that multiple actions were found with the same name. 
  4. To fix this, you need to decorate the GET action with the [HttpGet] attribute, and the POST action with the [HttpPost] attribute. This will tell ASP.NET which method to call when the view is rendered and which method to call by the client when posting data.
  5. Place a breakpoint on the return statement in the POST action.
  6. Save all files and start the application with debugging (F5). Navigate to the /Home/Create URL. The Create view should be displayed again.
  7. Fill out the form and click the Create button. The execution should halt at the breakpoint, proving that the Create button posts to the server. Inspect the content in the model object; it should contain the values you entered in the form. 
  8. Stop the application in Visual Studio.
  9. Add a using statement to the Entities namespace in the controller to get access to the Video entity class.
  10. Because the purpose of the Create method is to add a new video to the data source, you will have to create an instance of the Video class and assign values to it from the model object properties. Note that you don’t have to assign the Id property. The video doesn’t exist in the data source yet, and therefore doesn’t have an id.
  11. Because you have implemented the IVideoData Interface as a service that is injected into the constructor, you have to add an Add method to it, and implement it in the MockVideoData class. This will make it possible to call the Add method on the _videos variable to add a new video. Let’s implement it one step at a time. Begin by opening the IVideoData Interface.
  12. Add a new void method called Add that takes a Video parameter called newVideo.
  13. Add the method to the MockVideoData class. You can use the light bulb button if you hover over the interface name.
  14. Remove the throw statement from the method.
  15. Because the data source is a collection that is unable to generate new ids, you have to create a new id for the video object. You can fake an id by using LINQs Max method to fetch the highest id and add 1 to it. This id is only used for demo purposes and should never be used in a production application where the id is created automatically by the database.
  16. To add the new video to the _videos collection, you must change its data type to List. You can’t add values to an IEnumerable collection. To preserve the values between HTTP requests, you will later change the scope of the IVideoData service in the Startup class.
  17. Make a call to the Add method on the _videos collection, to add the new video in the Add method you created in the MockVideoData class.
  18. Open the HomeController class and call the Add method you just created, from the HTTP POST Create action, and pass in the video object to it.
  19. To prevent the user from submitting the Create form multiple times by refreshing the page, you must replace the View method with a call to the RedirectToAction method and redirect them to another view, like the Details view. Because the Details view has an id parameter you must pass in the name of the view, and the video id wrapped in an anonymous object.
  20. Open the Startup class and locate the ConfigureServices method. Change the scope of the IVideoData service to singleton by calling the AddSingleton method instead of the AddScoped that is currently used. You do this to preserve the data between HTTP requests.
  21. Save all the files and navigate to the /Home/Create URL. Fill out the form and click the Create button. Instead of remaining on the Create view, you are redirected to the Details view, which displays the added video. Note that the URL changed to /Home/Details/4 with the redirect.
9
04-09 Adding Validation to the Create View

Data Annotations

Data annotations are attributes you add to properties in a model, to enforce rules about them. You can specify that a field is required or must have a maximum number of charac­ters. The text displayed in a label is normally the property name, but that can be overrid­den with the [Display] attribute.

Many data annotations can be found in the System.ComponentModel.DataAnnotations namespace. You can specify one annotation per code line, or multiple annotations as a comma-separated list inside a set of square brackets.

[Required]

[MaxLength(80)]

Or

[Required, MaxLength(80)]

Below is a list of commonly used data annotations.

­­

Preparing the Create View for Validation

To validate the annotations in the browser, the view must be altered to display possible errors. You usually do this by adding a <span> or a <div> element decorated with the asp-validation-for Tag Helper, specifying which property it displays errors for. You can also add a validation summary that displays all errors as an unordered list inside a <div> element decorated with the asp-validation-summary Tag Helper.

Adding Validation to the Create View

Let’s add both types of validation to the Create view to see what it looks like.

  1. Open the Create view.
  2. Add a validation summary <div> at the top of the form.
  3. Add validation to the Title property. Add a <span> decorated with the asp-validation-for Tag Helper inside a <td> element below the Title <input>.
  4. Repeat step 3 for the Genre property.
10
04-10 Validating the Model on the Server

Validating the Model on the Server

Since no JavaScript validation libraries have been added to the application, you must vali­date the model on the server. To enforce model validation in the HTTP POST Create action, you must check if the model is valid before taking any action. If the model is valid, the video will be added to the data source, otherwise it will re-render the view so that the user can change the values and resubmit.

The ModelState object’s IsValid property can be used in the HTTP POST action to check if the model is valid. Surround the code that creates and adds the video to the data source with an if-statement that checks the IsValid property value. Return the view below the if-block if the model state is invalid.

  1. Open the HomeController class.
  2. Add an if-block that checks the model state; it should surround all the code inside the HTTP POST Create action.
  3. Return the view below the if-block.
11
04-11 Adding Data Annotations in the Video Entity and VideoEditViewModel

Adding Data Annotations in the Video Entity and VideoEditViewModel Class

Data annotations added to an entity class can affect both the controls in a view and the database table it represents.

In the project you are building, the Video entity is used as the view model for the Create view. To enforce some rules on that model, you add attributes to its properties that restrict or enhance them.

Let’s implement some annotations in the Video entity model that alter how the controls in the view are rendered, and later restrict the database columns.

  1. Open the Video entity model.
  2. Add the [Required] annotation to the Title property. This will restrict the value in the database table to non-null values, and force the user to enter a value in the control, for the model object to be valid.
  3. Add a using statement to the DataAnnotations namespace to get access to the data annotation attributes.
  4. Open the VideoEditViewModel and repeat step 2 and 3.
  5. Save all files and switch to the browser. Navigate to the /Home/Create URL.
  6. Click the Create button without entering a title. The validation message should appear beside the input field.
  7. Add the [MinLength] annotation, with a min length of 3, to the Title property in both the Video and VideoEditViewModel classes.
  8. Save all files and switch to the browser. Navigate to the /Home/Create URL.
  9. Enter 2 characters in the Title input field and click the Create button. The validation message should tell you that too few characters have been entered in the input field.
  10. Enter at least 3 characters in the Title input field and click the Create button. The video should be successfully added to the data source.
  11. Add the [MaxLength] annotation, with a max length of 80, to the Title property in both the Video and VideoEditViewModel classes. This will ensure that the Title property can have at most 80 characters when saved to the data source, and that the input control only will accept that many characters.
  12. You can use the [Display] annotation to change the label text for a property. Let’s change the text for the Genre property to Film Genre. Add the [Display] attribute to the Genre property in the Video class. Set its Name parameter to the text Film Genre. You only have to add the attribute to the Video model, since it only is applied to labels in a view.
  13. Save all files and switch to the browser. Navigate to the /Home/Create URL. The label for the Genre select list should display the text Film Genre.
  14. Let’s try the [DataType] annotation next. Add it to the Title property in the Video class and select the Password type. This should display the entered text as password characters, typically dots or asterisks. Specifying a data type in the model can change its control’s appearance on some devices, and can even change the layout of the keyboard displayed on the device screen, when the control has focus.
  15. Save all files and switch to the browser. Navigate to the /Home/Create URL. Enter text in the Title input field. It should be displayed as password characters.
  16. Remove the [DataType] annotation and save the file.
12
04-12 Summary

Summary

In this module, you learned about different models that can be used with MVC, and how data annotations can be used to influence the labels and input controls, created with HTML and Tag Helpers in the view.

You also implemented validation checks on the server and displayed validation messages on the client.

In the next module, you will learn about Entity Framework.

05 - Entity Framework

1
05-01 Introduction to Entity Framework

Entity Framework

In this module, you will set up Entity Framework (EF) and get familiar with how it works.

When the services have been installed and configured in the Startup class, you need to add a data context class that inherits from the DbContext class. This class will be the con­text that you use to interact with the database. To add a table to the database, the table’s entity class must be added as a DbSet property in the context class.

When the services are installed and configured in the Startup class, you create the first migration by using the Package Manager Console and the Add-Migration command. When the initial migration has been added, the database can be generated with the Update-Database command.

If you make any changes to the database, like adding or changing columns or tables, then you must execute the Add-Migration and Update-Database commands again for the ap­plication to work properly.

In previous versions you had to install Entity Framework NuGet packages to create and use a database. In ASP.NET Core 2.0 those NuGet packages are installed as part of the Microsoft.AspNetCore.All NuGet package.

The setup for User Secrets has been incorporated into the new BuildWebHost method in the Program.cs file, which means that you no longer have to add any configuration for it in the Startup class. You can use User Secrets to store sensitive data locally in a file called secrets.json, which is stored outside the solution folder structure and is never committed to the source code repository, if you use that. You will store the connection string to the database securely in the secrets.json file.

2
05-02 Adding the VideoDbContext Class

Adding the VideoDbContext Class

Now, you will add a class called VideoDb­Context that inherits form the DbContext class. This class will be your connection to the database. It defines the entity classes as DbSet properties, which are mirrored as tables in the database.

For the AddDbContext method to be able to add the context to the services collection, the VideoDbContext must have a constructor with a DbContextOptions<VideoDbCon­text> parameter, which passes the parameter object to its base constructor. The OnModelCreating method must be overridden to enable Entity Framework to build the entity model for the database.

  1. Add a new folder called Data to the project.
  2. Add a class called VideoDbContext to the Data folder in the Solution Explorer.
  3. Inherit the DbContext class in the VideoDbContext class. The DbContext class is in the Microsoft.EntityFrameworkCore namespace.
  4. Add a DbSet property for the Video class in the VideoDbContext class. the Video class is in the AspNetCoreVideoCourse.Entities namespace.
  5. Add the constructor with a DbContextOptions<VideoDbContext> parameter.
  6. Override the OnModelCreating method.
  7. Save all the files.
3
05-03 Configuration in the Startup Class

Configuration in the Startup Class

Before the initial migration can be applied, you have to configure Entity Framework to use the VideoDbContext, and read the connection string from the secrets.json file. Using the secrets.json file has two purposes: It stores the connection string in a safe place that is not checked into source control. It also renders the appsettings.json obso­lete for storing secret or sensitive data, which is a good thing, since it is checked into source control.

  1. Right click on the project node in the Solution Explorer and select Manage User Secrets.
  2. Add the following connection string property. Note that the database name is VideoCoreDb. The connection string should be on one row in the file. "ConnectionStrings": {     "DefaultConnection": "Server=(localdb)\mssqllocaldb;Database=VideoCoreDb;Trusted_Connection=True;MultipleActiveResultSets=true" }
  3. Open the Startup class and locate the constructor.
  4. Add the optional: true parameter value temporarily to the AddJsonFile method for the appsettings.json file if the file is missing from the project.
  5. To be able to check the environment the IHostingEnvironment interface must be injected into the constructor.
  6. Add an if-statement, checking if the development environment is active, and use the AddUserSecrets method to add it to the builder object. Add it above the Build method call.
  7. Locate the ConfigureServices method and fetch the connection string from the secrets.json file using the Configuration object. Store the connection string in a variable called conn.
  8. Use the AddDbContext method on the services collection to add the database context and the EF services at the beginning of the ConfigureServices method. Call the UseSqlServer method on the options action in its constructor to specify that you want to use a SQL Server database provider. The UseSqlServer method is in the Microsoft.EntityFrameworkCore namespace and the VideoDbContext class is in the AspNetCoreVideoCourse.Data namespace. Note that DefaultConnection is the name of the property you added to the secrets.json file.
4
05-04 Adding the Initial Migration and Creating the Database

To add the initial migration and create the database, you execute the Add-Migration and Update-Database commands in the Package Manager Console (View-Other Windows-Package Manager Console).

When the Add-Migration command has been successfully executed, a new folder called Migrations will appear in the project. The current and all future migrations will be stored in this folder.

If you encounter the error message No parameterless constructor was found on 'VideoDbContext': Either add a parameterless constructor to 'VideoDbContext' or add an implementation of 'IDbContextFactory<VideoDbContext>' in the same assembly as 'VideoDbContext', then check that your connection string in secrets.json is correct and that it is being loaded in the Startup class, before doing any other troubleshooting.

  1. Open the Package Manager Console.
  2. Type in the command add-migration Initial and press Enter. Note the Migrations folder, and the migration files in it.
  3. Execute the command update-database in the Package Manager Console to create the database.
  4. Open the SQL Server Object Explorer from the View menu.
  5. Expand the MSSQLLocalDb node, and then the Databases node. If the VideoCoreDb database isn’t visible, right click on the Databases node and select Refresh.
  6. Expand the VideoCoreDb node, and then the Tables node. You should now see the Videos table in the VideoCoreDb database that you just created.
  7. Expand the Videos table and then the Columns node. You should now see the columns in the table. Note that they match the properties in the Video entity class, and that they have the restrictions from the attributes you added to its properties.
  8. Right click on the Videos table and select View Data. This will open the table in edit mode. Add a genre id from the Genres enum (it is zero based) and a title. Press Enter to commit the value to the database. Add a few more videos if you like.
5
05-05 Adding the SqlVideoData Service Component

Adding the SqlVideoData Service Component

To use the database in the application, you can implement the IVideoData interface in a new service component class. Then, you change the service registration in the Configure­Services method in the Startup class to create instances of the new component.

Implementing the SqlVideoData Service Component Class

Let’s begin by implementing the SqlVideoData class that will communicate with the data­base through the VideoDbContext.

  1. Add a class called SqlVideoData to the Services folder.
  2. Add a private field called _db to the SqlVideoData class. This variable will hold the context needed to communicate with the database. Add a using statement to the VideoDbContext in the AspNetCoreVideoCourse.Data namespace.
  3. Add a constructor that is injected with an instance of the VideoDbContext class; name the parameter db. Assign the injected object in the db parameter to the _db variable.
  4. Implement the IVideoData interface. You can use the light bulb button when hovering over the interface name.
  5. Replace the throw statement in the Add method with a call to the Add method on the _db context and pass in the video object to the method. Then call the SaveChanges method on the _db context to persist the changes in the database.
  6. Replace the throw statement in the Get method with a call to the Find method on the _db context to fetch the video matching the id passed-in to the method. Return the fetched video.
  7. Replace the throw statement in the GetAll method with a return statement that returns all the videos in the Videos table.
  8. Open the Startup class and change the service registration for the IVideoData interface to create instances of the SqlVideoData class. Also, change the method from AddSingleton to AddScoped for the service to work with Entity Framework.
  9. Save all the files and navigate to the root URL (/Home). The list of videos from the database should be displayed.
  10. Add a new video by navigating to the /Home/Create URL and fill out the form. When you click the Create button in the form, the Details view should display the new video.
  11. Click the Home link and make sure that the video is in the list.
  12. Open the Video table in the SQL Server Object Explorer and verify that the video is in the table.
6
05-06 Summary

In this module, you installed the Entity Framework and User Secrets services in the Setup class.

You also added a DbContext class that communicates with the database, and a new service component class that implements the IVideoData Interface, as a separation between the DbContext and the application.

Finally, you added a new video to the database using the Create view, and verified that it had been added to the database.

06 - Razor Views

1
06-01 Introduction

Introduction

In this module, you will learn about different views that can be used for layout, to include namespaces, and to render partial content in a view.

Layout Views

The _Layout.cshtml Razor view gives the application more structure and makes it easier to display data that should be visible on every page, such as a navigation bar and a footer. You avoid duplication using this view. The underscore at the beginning of the name is not required, but it is a convention that is commonly used among developers. It signifies that the view shouldn’t be rendered as a view result with the View method from a controller action.

The normal views, like the Index view, are rendered inside the _Layout view. This means that they don’t have any knowledge about the navigation and the footer; they only need to render what the action tells them to render.

If you look inside the views you have created, they have some code in common, such as the <html>, <head>, and <body> elements. Because the markup is the same for all the views, it could be moved to the _Layout view.

Shared views, like _Layout, are placed in a folder called Shared inside the Views folder. These views are available anywhere in the application. The layout view doesn’t have to be named _Layout; you can even have multiple layout views in the application if you like.

The _Layout view is a Razor view, which means that you can use C# inside the view, like you can in any other view. It should also have a method called @RenderBody, which is responsible for rendering the different content views the user navigates to, such as the Index and the Details views.

There is an object called @ViewBag in the _Layout view. It is a dynamic object that you can use to send data from the server to the view.

Another method that can be used in the _Layout view is the @RenderSection. This method can be used to render specific sections of HTML from the content view in the _Layout view. There is an asynchronous version of this method that you can use if you want that type of behavior.

2
06-02 Adding the _Layout View

Adding the _Layout View

  1. Add a new folder called Shared to the Views folder.
  2. Add a MVC View Layout Page called _Layout to the Shared folder using the New Item dialog.
  3. Add a <footer> element at the bottom of the <body> element.
  4. Add a call to the @RenderSection method to the <footer> element and pass in the name of the section that could be in any of the views. If you want the section to be optional, then pass in false for the second parameter. Name the section footer and pass in false.
3
06-03 Altering the Content Views

Altering the Content Views

Now that the _Layout view has been added, you need to remove the markup shared among the content views.

Open the Index view and remove the <head> and <body> elements, and do the same for the other views in the Home folder. You can use the Ctrl+E, D keyboard command to format the HTML.

Since you removed the <title> element from the view, you can add it to the ViewBag object as a property called Title. Assign the name of the view to the property. Since the ViewBag is placed inside a C# block, it doesn’t need the @-sign.

You can also use the Layout property in the C# block to tell the MVC framework which layout view to use with the view. The layout view must be specified with an explicit path, beginning with the tilde (~) sign.

The usual C# rules apply inside C# blocks, such as ending code lines with a semicolon.

  1. Open the Index view and remove all the <html>, <head>, and <body> elements, but leave the table and the @model directive.
  2. Add a C# block below the @model directive.
  3. Add a Title property to the ViewBag inside the C# block and assign a title to it (Home, in this case).
  4. Add the Layout property inside the C# block and assign the explicit path to the _Layout.cshtml file.
  5. Add a @section block named footer at the end of the Index view and place a <div> element with the text This is the Index footer inside it.
  6. Repeat steps 1-4 for all the views in the Views/Home folder but change the Title property from Home to Details and Create respectively.
  7. Save all the files and switch to the browser. Navigate to the Index view (/). You should be able to see the footer text below the video list. This verifies that the layout view is used to render the Index view.
4
06-04 The _ViewStart file

The _ViewStart file

The Razor view engine has a convention that looks for a file called _ViewStart.cshtml. This file is executed before any other views, but it has no HTML output. One purpose it has is to remove duplicate code from code blocks in the views, like the Layout declaration. Instead of declaring the location of the _Layout view in each view, it can be placed inside the _ViewStart view. It is possible to override the settings in the _ViewStart view by adding the Layout declaration in individual views.

If you place this view directly in the Views folder, it will be available to all views. Placing it in another folder inside the Views folder makes it available to the views in that folder.

You can assign null to the Layout property in a specific view to stop any layout view from being used with the view.

 

Let’s create the _ViewStart view in the Views folder, and add the Layout declaration in it.

  1. Add a MVC View Start Page to the Views folder (use the New Item dialog). It is important that you name it _ViewStart, to adhere to MVC conventions.
  2. Cut out the _Layout view path from the Index view.
  3. Replace the current value for the Layout property in _ViewStart with the path you copied.
  4. Remove the Layout property from all the views in the Views/Home folder.
  5. Save all the files and navigate to the root (/). You should still see the text This is the Index footer rendered by the _Layout view.
5
06-05 The _ViewImports file

The _ViewImports file

The Razor view engine has a convention that looks for a file called _ViewImports.cshtml. This file is executed before any other views, but it has no HTML output. You can use this file to add using statements that will be used by all the views; this removes code duplication and cleans up the views.

So, if you know that many views will use the same namespaces, then add them to the _ViewImports.cshtml file. Add the file to the Views folder.

  1. Add a MVC View Imports Page file named _ViewImports.cshtml to the Views folder.
  2. Open the Create view and cut out the @using and @addTagHelper rows.
  3. Open the _ViewImports view and paste in the code.
  4. Add a using statement to the AspNetCoreVideo.Entities namespace.
  5. Save the _ViewImports view.
  6. Open the Create view and remove the namespace path in the @model directive. The view should be able to find the Video model from the using statement in the _ViewImports view.
  7. Open the Index view and cut out the AspNetCoreVideo.ViewModels namespace path from the @model directive and add it as a using statement to the _ViewImports view and save it. Leave only the class name in the @model directive.
  8. Open the Details view and delete the AspNetCoreVideo.ViewModels namespace path. Leave only the class name in the @model directive.
  9. Save all the files and navigate to the different views in the browser, to verify that the application still works as before.
6
06-06 Tag Helpers

Tag Helpers

Tag Helpers are new to ASP.NET Core, and can in many instances replace the old HTML helpers. The Tag Helpers blend in with the HTML as they appear to be HTML attributes or HTML elements.

You have already used Tag Helpers in the Create form. There you added the asp-for and asp-validation-for among others. They blend in much better than the alternatives: Label­For, TextBoxFor, EditorFor, and other HTML helpers that are used in previous versions of ASP.NET. You can still use Razor HTML Helpers in ASP.NET Core, and they have one benefit over Tag Helpers; they are tightly coupled to the model. This means that you get Intelli­Sense and can rename properties more easily. In a Tag Helper, the property is added as a string value.

To use the Tag Helpers, you need to add a @addTagHelper directive to the _ViewImports view, or in specific views where you want to use them. The first parameter, the asterisk, specifies that all Tag Helpers in that namespace should be available. You can change this to a specific Tag Helper if you don’t want to import all helpers.

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Let’s add a link calling the Create action from the Index view using Tag Helpers, so that you don’t have to type in the URL to the Create view in the browser. Let’s also replace the ActionLink HTML helper for the Id property, with a Tag Helper that opens the Details view and has the description Details.

Altering the Index View

1.       Open the Index view.

2.       Add an anchor tag (<a>) between the </table> tag and the @section block. Add the text Create to the anchor tag.

3.       Use the asp-action Tag Helper to specify which action in the Home controller you want the link to call. You can add the asp-controller Tag Helper if you want to navigate to a controller that the view doesn’t belong to.

4.       Save the file and navigate to the Index view in the browser. You should see a link with the text Create. When you click the link, the Create view should appear.

5.       Click the Back to List link to get back to the Index view.

6.       Place a breakpoint inside the HTTP GET Create action in the HomeController class, and start the application with debugging (F5).

7.       Click the Create link again. The execution should halt at the breakpoint. This demonstrates that the Action was called by the Tag Helper.

8.       Remove the breakpoint and stop the application in Visual Studio.

9.       Remove the ActionLink for the Id property.

10.   Add an anchor tag that opens the Details view using the asp-action Tag Helper, and the asp-route-id Tag Helper to pass in the video id.

11.   Start the application without debugging (Ctrl+F5). You should now see Details links. Click one to verify that the Details view for that video is displayed.

7
06-07 Adding an Edit View and Its Actions

Adding an Edit View and Its Actions

There are two more views needed to complete the CRUD operations, the Edit and Delete views. Let’s add the Edit view by copying the Create view and modify the form. Then let’s refactor the IVideoData interface, and the classes implementing it. Instead of saving data directly when a video is added or edited, this refactoring will make it possible to add or edit multiple videos before saving the changes to the database.

  1. Copy the Create view and paste it into the Home folder. Rename the view Edit.
  2. Visual Studio sometimes gets confused when a view is copied, pasted, and renamed. To avoid confusion, close the Edit view and open it again.
  3. Change the title to Edit followed by the video title.
  4. Do the same for the view’s heading; use the value from the ViewBag.
  5. Change the asp-action Tag Helper to call an action named Edit; you will add the action to the HomeController class later. Also specify that the form should use the post method; it is safer than using the default get method when posting a form.
  6. Change the submit button’s text to Edit.
  7. Open the Index view and add a link to the Edit view, like you did in the Details view. You can copy and change the Details anchor tag you added earlier. Move the links after the Genre table cell to make the form a little more pleasing to the eye.
  8. To make the Edit link and view work, you have to add HTTP GET and HTTP POST Edit actions to the HomeController class. Let’s start with the HTTP GET action. Copy the HTTP GET Details action and paste it into the class. Rename it Edit and add the HttpGet attribute to it. This will make it possible to open the Edit view with the link you added in the Index view.
  9. Rename the model variable video.
  10. Replace the return statement with one that returns the video object to the view.
  11. Add an HTTP POST Edit action that has an id parameter of type int and a VideoEditViewModel parameter called model. Add the HttpPost attribute to the action.
  12. Fetch the video matching the passed-in id and store it in a variable called video.
  13. Add an if-statement that checks if the model state is invalid, or the video object is null. If any of them are true, then return the view with the model.
  14. Assign the Title and Genre values from the model to the video object you fetched. Entity Framework will keep track of changes to the video objects.
  15. Call the Commit method on the _Video object. This method does not exist yet, but you will add it to the IVideoData service classes shortly. After you have refactored the IVideoData service, the method will work, and save any changes to the database. Since Entity Framework keeps track of any changes to the DbContext, you don’t have to send in the video object to the Commit method.
  16. Add a redirect to the Details view.
8
06-08 Refactoring the IVideoData Service

Refactoring the IVideoData Service

The idea is that you should be able to do multiple changes and add new videos before committing the changes to the database. To achieve this, you must move the SaveChanges method call to a separate method called Commit. Whenever changes should be persisted to the database, the Commit method must be called.

  1. Open the IVideoData interface.
  2. Add a definition for a method called Commit that returns an int. The int value will in some instances reflect the number of records that were affected by the commit.
  3. Open the MockVideoData class and add a Commit method that returns 0. You must add the method even though it isn’t necessary for the mock data. The mock data is instantly saved when in memory. The interface demands that the Commit method is implemented.
  4. Open the SqlVideoData class and add a Commit method that return the results from the call to the SaveChanges method.
  5. Remove the call to the SaveChanges method from the Add method.
  6. Open the HomeController and verify that the Commit method doesn’t have a red squiggly line and therefore is working properly.
  7. Call the Commit method in the Create action, below the call to the Add method. This is necessary since you refactored out the call to the SaveChanges method from the Add method in the SqlVideoData service.
  8. Save all files and navigate to the root URL. The new Edit links should appear to the right of the videos in the listing, beside the Details links.
  9. Click the Edit link for one of the videos to open the new Edit view.
  10. Make some changes to the video and click the Edit button.
  11. The Details view for the video is displayed. Click the Home link to get back to the video list in the Index view.
  12. The Index view should reflect the changes you made to the video.
9
06-09 Partial Views

Partial Views

A partial view has two main purposes. The first is to render a portion of a view; the other is to enable the reuse of markup to clean up the code in a view.

To render a partial view, you can use either the synchronous @Html.Partial method or the asynchronous @Html.PartialAsync method. Both methods take two parameters, where the first is the name of the partial view and the second is an optional model object.

Note that partial views always use data from the parent view model.

The following example would render a partial view called _Video that receives a video object from the parent view’s model. The first code line is synchronous while the second is asynchronous; you choose which one you want to use.

@Html.Partial("_Video", video);

@await Html.PartialAsync("_Video", video);

Let’s create a partial view called _Video to clean up the Index view. It will display the videos as panels, and get rid of that ugly table in the process.

  1. Add a partial view called _Video to the Home folder by right clicking on the Home folder and selecting Add-View. Don’t forget to check the Create as partial view checkbox.
  2. Delete all code inside the view.
  3. Add the VideoViewModel class as its model.
  4. Add a <section> element in the partial view. Sometimes Visual Studio gets confused when a new view has been added and reports elements as missing, to solve this, you save the view and open it again.
  5. Add an <h3> element inside the <section> element and add the video title to it using the @Model object.
  6. Add a <div> element below the <h3> element and add the video genre to it using the @Model object.
  7. Add another <div> element below the previous <div> element.
  8. Copy the Details and Edit links from the Index view and paste them into the newest <div> element. Change the asp-route-id Tag Helper to fetch its value from the @Model object.
  9. Open the Index view and replace the <table> element and all its content with a foreach loop that renders the partial view. The foreach loop is the same as the one in the <table> element, so you can copy it before removing the <table> element.
  10. Place the remaining anchor tag inside a <div> element to make it easier to style.
  11. Remove the @section footer block. You will display other information at the bottom of the page using a View Component in the next section.
  12. Save all the files and navigate to the root URL in the browser. The videos should now be stacked vertically as cards. They might not look pretty, but you can make them look great with CSS styling.
10
06-10 Adding a View Component for the IMessageService Service

View Components

A View Component is almost a complete MVC abstraction. It is a partial view that has its own model, which it gets from a method called Invoke in a controller-like class. A View Component’s model is independent from the current view’s model. You should not use a regular partial view, with a model, from the _Layout view, since it has no model and it is difficult to get one into it. Use a View Component to render partial content in the _Layout view.

In previous versions of MVC, you use @Html.ActionHelper to execute a child action. In this version of MVC it has been replaced with the View Component.

You can look at a View Component as having a controller that you never route to.

View Component views are always placed in a folder called Components inside the Views folder. If you place the folder in the Views/Shared folder, the view can be used from any view. Each View Component has a sub-folder in the Components folder with the same name as the View Component.

Adding a View Component for the IMessageService Service Let’s implement a View Component that uses the IMessageService service to display the configuration message in every view.

  1. Create a new folder called ViewComponents under the project node. This folder will hold the necessary files for View Components to work.
  2. Add a class called Message to the folder and inherit the ViewComponent class. Add a using statement to the Microsoft.AspNetCore.Mvc namespace to get access to the ViewComponent class.
  3. Add a constructor and inject the IMessageService interface to it, name the parameter message, and store it in a private class-level variable called _message. Add a using statement to the Services namespace to get access to the IMessageService class.
  4. Add a public method called Invoke that returns an IViewComponentResult.
  5. Add a variable called model to the Invoke method, which stores the result from the _message.GetMessage method call.
  6. Return the model with the View method. Because the model is a string, the View method gets confused and thinks it is the name of the view to render. To fix this you pass in the name of the view as the first parameter and the model object as its second parameter.
  7. Create a new folder called Components inside the Views/Shared folder.
  8. Add a folder called Message inside the Components folder.
  9. Add a MVC View Page called Default in the Message folder.
  10. Delete all code in the view.
  11. Add an @model directive of type string.
  12. Add a <section> element with a <small> element inside it. Add the @Model value to the <small> element.
  13. Open the _Layout view and call the InvokeAsync method on the Component property inside the <footer> element. Pass in the name of the View Component as a parameter. Remember to use @await when calling an asynchronous method.
  14. Save all the files.
  15. Navigate to all the views, one at a time, to verify that the message from the configuration file (Hello from configuration) is displayed in each of their footers.
11
06-11 Summary

Summary

In this module, you worked with layout views and partial views. You also used new features, such as Tag Helpers, View Components, and the _ViewStart and _ViewImport views.

Using these features allows you to reuse code and decompose a large view into smaller, more maintainable, pieces. They give you the ability to write maintainable and reusable code.

07 - Forms Authentication

1
07-01 Introduction

Introduction

In this module, you will learn about ASP.NET Identity and how you can use it to implement registration and login in your application. You will add the authentication from scratch to learn how all the pieces fit together.

The ASP.NET Identity framework is installed automatically as part of the Microsoft.AspNetCore.All NuGet package, you can see the reference in the .csproj file. It can handle several types of authentication, but in this module, you will focus on Forms Authentication.

The first thing you need to add is a User entity class that inherits from an identity base class, which gives you access to properties such as Username, PasswordHash, and Email. You can add as many properties to the User class as your application needs, but in this module, you will only use some of the inherited properties.

The User class needs to be plugged into a class called UserStore, provided by the Identity framework. It is used when creating and validating a user that then is sent to a database; Entity Framework is supported out of the box. You can implement your own UserStore, for a different database provider.

The User class needs to be plugged into an IdentityDb class that handles all communica­tion with an Entity Framework-supported database, through an Entity Framework DbCon­text. The way this is done is by making your existing VideoDbContext inherit from the IdentityDbContext class instead of the current DbContext class.

The UserStore and the IdentityDbContext work together to store user information and validate against the hashed passwords in the database.

Another class involved in the process is the SignInManager, which will sign in a user once the password has been validated. It can also be used to sign out already logged in users. A cookie is used to handle Forms Authentication sign-in and sign-out. The cookie is then sent with every subsequent request from the browser, so that the user can be identified.

The last piece is the Identity Middleware that needs to be configured to read the cookie and verify the user.

The [Authorize] attribute can be applied to a controller to restrict user access; a user must be signed in and verified to have access to the actions in that controller.

The [AllowAnonymous] attribute can be applied to actions to allow any visitor access to that action, even if they aren’t registered or signed in.

You can use parameters with the [Authorize] attribute to restrict the access even beyond being logged in, which is its default behavior. You can, for instance, add the Roles parameter to specify one or more roles that the user must be in to gain access.

You can also place the [Authorize] attribute on specific actions, instead of on the controller class, to restrict access to specific actions.

2
07-02 Adding the Authorize and AlowAnonymous Attributes

Adding the Authorize and AlowAnonymous Attributes

Let’s start by adding the [Authorize] attribute to the HomeController class, to grant access only to logged in users. Let’s also add the [AllowAnonymous] attribute to the Index action, so that any visitor can see the video list.

  1. Open the HomeController class and add the [Authorize] attribute to it. The [Authorize] attribute is located in the Microsoft.AspNetCore.Authorization namespace.
  2. Add the [AllowAnonymous] attribute to the Index action.
  3. Save all files and navigate to the root URL in the browser. As you can see, the [AllowAnonymous] attribute lets you see the video list in the Index view.
  4. Click the Edit link to edit a video. Instead of being greeted by the Edit view, an error message is displayed. This confirms that the [Authorize] attribute is working. You are not logged in, and are therefore not allowed to use the Edit form.
3
07-03 Configuring the Identity Framework

Configuring the Identity Framework

Once you have changed the inheritance on the VideoDbContext from the current DbCon­text to the IdentityDbContext, the Identity services can be configured in the Configure­Services method, and in the Identity middleware installed in the Configure method, in the Startup class.

The services that need to be configured are the UserStore and SignInManager.

  1. Add the User entity class to the Entities folder and inherit from the IdentityUser class to gain access to its user properties. Add a using statement to the Microsoft.AspNetCore.Identity namespace to get access to the IdentityUser class. It’s in the User class that you can add your own user properties, specific to your application; it could be any property related to the user. Below is a list of all the properties the IdentityUser class will bring.
  2. Open the VideoDbContext and make it inherit the IdentityDbContext class instead of EFs default DbContext. You can specify the type of user it should store, which in this case is the User entity class you just added. The IdentityDbContext class is located in the Microsoft.AspNetCore.Identity.EntityFrameworkCore namespace.
  3. Open the Startup class.
  4. Add the Identity service to the services collection by calling the AddIdentity method. The method takes two generic type parameters: the first is the user you want it to use (the User entity class you just added) and the second is the identity role you want it to use (use the built-in IdentityRole class). You can inherit the IdentityRole class to another class if you want to implement your own identity role behavior. Add the service above the AddMvc method call.
  5. Add using statements to the Entities and Identity namespaces to get access to the User and IdentityRole classes, and then locate the ConfigureServices method.
  6. You must also install the Entity Framework Stores services that handle creation and validation of users against the database. You need to provide the VideoDbContext to it, so that it knows which context to use when communicating with the database. You can use the fluent API to call the AddEntityFrameworkStores method on the AddIdentity method.
  7. Next you need to install the middleware components in the Configure method. The location of the middleware is important. If you place it too late in the pipeline, it will never be executed. Place it above the MVC middleware to make it available to the MVC framework.
  8. Build the application with Ctrl+Shift+B to make sure that it builds correctly.
4
07-04 Creating the AspNet Identity Database Tables

Creating the AspNet Identity Database Tables

Now that the configuration is out of the way, it is time to create a new migration that adds the necessary AspNet identity tables to the database.

  1. Open the Package Manager Console and execute the following command to create the necessary migration file: add-migration IdentityTables
  2. Execute the following command to create the identity tables in the database: update-database
  3. Open the SQL Server Object Explorer and drill down to the tables in your VideoDb database.
5
07-05 User Registration

User Registration

Now that all the configuration and database table creation is done, it is time to focus on how a user can register with the site.

If you run the application as it stands right now, the Identity middleware will redirect to the /Account/Login URL, which doesn’t exist yet. Instead, the next piece of middleware handles the request, and the message Hello from configuration will be displayed in the browser.

To display a Login view, you must add an AccountController class with a Login action. And to log in, the user needs to register. You therefore must implement a Register view, and a Register action in the AccountController class.

  1. Add a class named AccountController to the Controllers folder and let it inherit the Controllers class located in the Microsoft.AspNetCore.Mvc namespace.
  2. Add an HTTP GET Register action to the class. The view doesn’t have to receive a model with data, because the user will supply all the registration information in the view.
  3. Add a class called RegisterViewModel in the ViewModels folder. This will be the view model for the Register view.
  4. Add a string property called Username that is required and has a maximum of 255 characters. The length is determined by the max number of characters that the AspNetUser table can store for a username.
  5. Add a using statement to the System.ComponentModel.DataAnnotations namespace to get access to the necessary data annotation attributes.
  6. Add a string property called Password that is required and has the Password data type.
  7. Add a string property called ConfirmPassword that has the Password data type and uses the Compare attribute to compare its value with the Password property. You can use the C# nameof operator to specify the compare property, instead of using a string literal.
  8. Add a new folder called Account inside the Views folder. This folder will hold all the views related to the Account controller.
  9. Add a MVC View Page view called Register to the Account folder.
  10. Delete all the content in the view.
  11. Add an @model directive for the RegisterViewModel class.
  12. Use the ViewBag to add the text Register to the Title property.
  13. Add an <h1> heading with the text Register.
  14. Add a <form> that posts to the Register action in the Account controller. Use Tag Helpers to create the form.
  15. Add a validation summary that only displays errors related to the model.
  16. Add a <div> that holds a <label> and an <input> for the Username model property and a <span> for the validation.
  17. Repeat step 16 for the Password and ConfirmPassword properties in the model.
  18. Add a submit button inside a <div> to the form. Assign the text Register to the value attribute.
  19. Open the AccountController class.
  20. Add an HTTP POST Register action that will be called by the form when the submit button is clicked. It should return an IActionResult and take a RegisterViewModel parameter called model. The action must be asynchronous to await the result from the UserManager and SignInManager, which you will inject into the controller later.
  21. Add a using statement to the ViewModels namespace to get access to the RegisterViewModel class.
  22. The first thing to do in any HTTP POST action is to check if the model state is valid; if it’s not, then the view should be re-rendered.
  23. Create a new instance of the User entity class and assign its Username property value from the passed-in model, below the if-statement.
  24. Add a using statement to the Entities namespaces to get access to the User class.
  25. To work with the user entity, you need to bring in the UserManager and the SignInManager via the constructor, using dependency injection. Add a constructor to the controller and inject the two classes mentioned above.
  26. Add a using statement to the Identity namespaces to get access to the UserManager, and SignInManager classes.
  27. Next you want to use the UserManager in the HTTP POST Register action to create a new user. Save the result in a variable called result.
  28. If the user was created successfully you want to sign in that user automatically. Use the Succeeded property on the result variable to check if the user was created successfully, and the SignInAsync method on the SignInManager to sign in the user. The second parameter of the method determines if the cookie should be persisted beyond the session or not.
  29. If the user wasn’t created, you want to add the errors to the ModelState object, so that they are sent to the client as model errors, displayed in the validation summary.
  30. Return the view below the else-block.
  31. Save all the files and navigate to the /Account/Register URL. The Register View should be displayed. Fill out the form with a three-letter password and click the Register button. The validation summary should display the errors that were looped into the ModelState object, in the Register action method.
  32. Fill out the form (with correct information this time). You should be redirected to the Index view through the RedirectToAction method in the Register action.
  33. View the data in the AspNetUsers table in the SQL Server Object Explorer to verify that the user was registered.
6
07-06 Adding the _Login Partial View

Login and Logout

In this section, you will implement login and logout in your application. The links will be added to a partial view called _LoginLinks that you will add to the Views/Shared folder. The partial view will then be rendered from the _Layout view using the @Partial or @PartialAsync method.

When an anonymous user arrives at the site, Login and Register links should be available. When a user has logged in or registered, the username and a Logout link should be visible.

You must also create a new view called Login in the Views/Account folder, a view that the Login link opens by calling a Login action in the Account controller.

To work with users and sign-in information in views, you inject the SignInManager and UserManager, similar to the way you use dependency injection in methods and construc­tors in classes.

When an anonymous user clicks a restricted link, like the Edit link, a ReturnUrl parameter is sent with the URL, so that the user will end up on that view when a successful login has been made. When creating the LoginViewModel you must add a property for the return URL, so that the application can redirect to it. Below is an example URL with the ReturnUrl parameter.

http://localhost:51457/Account/Login?ReturnUrl=%2FHome%2FEdit%2F1

Adding the _Login Partial View

This partial view will contain the Login and Register links that will be visible when an anonymous user visits the site, and a Logout link and the username when the user is logged in.

  1. Add a MVC View Page called _LoginLinks to the Views/Shared folder.
  2. Delete all the code in the view.
  3. Add a using statement to the Microsoft.AspNetCore.Identity namespace to get access to the SignInManager and UserManager.
  4. Inject the SignInManager and UserManager to the view, below the using statement.
  5. Add if/else-blocks that check if the user is signed in, using the IsSignedIn method on the SignInManager passing it the User object.
  6. Add a <div> that displays the username to the Signed in user-block. Use the User object’s Identity property.
  7. Add a form to the Signed in user-block that posts to the /Account/Logout action when a submit button is clicked.
  8. Add two anchor tags to the Anonymous user block that navigates to the Login and Register actions in the Account controller.
  9. Open the _Layout view and add a <div> above the @RenderBody method in the <body> element.
  10. Call the @Html.PartialAsync method to render the _LoginLinks partial view in the <div>.
  11. Start the application without debugging (Ctrl+F5). Because you were signed in when registering, the username and a Logout button should be visible. Later when you have implemented the Logout action, the Login and Register links should be visible at the top of the view when logged out.
7
07-07 Adding the Logout Action

Adding the Logout Action

The SignOutAsync method on the SignInManager must be called to log out a user when the Logout button is clicked. The Logout action in the Account controller must be asyn­chronous because the SignOutAsync method is asynchronous.

  1. Open the AccountController class.
  2. Add an async HTTP POST action called Logout that returns a Task<IActionResult>. This action will be called when the Logout link is clicked.
  3. Call the SignOutAsync method on the _signInManager object inside the Logout action.
  4. Because the user is logging out, you want the user to end up on a safe view after the logout process has completed. Add a redirect to the Index action in the Home controller.
8
07-08 Adding the LoginViewModel Class

Adding the LoginViewModel Class

This model is responsible for passing the login information provided by the user, and the ReturnUrl URL parameter value, to the HTTP POST Login action.

The model needs four properties: Username, Password, RememberMe, and ReturnUrl. The RememberMe property determines if the cookie should be a session cookie or if a more persistent cookie should be used.

  1. Add a new class called LoginViewModel to the ViewModels folder.
  2. Add three string properties called Username, Password, and ReturnUrl, and a bool property called RememberMe.
  3. Add the Required attribute to the Username property.
  4. Add a using statement to the DataAnnotations namespace to get access to the data annotation attributes.
  5. Add the DataType.Password and Required attributes to the Password property.
  6. Use the Display attribute to change the label text to Remember Me for the RememberMe property.
9
07-09 Adding the HTTP GET Login Action

Adding the HTTP GET Login Action

This action will be called when the user clicks the Login link. You will need to create an instance of the LoginViewModel and assign the return URL, passed into the action, to its ReturnUrl property. Then pass the model to the view.

  1. Open the AccountController class.
  2. Add an HTTP GET action called Login that takes a string parameter called returnUrl and returns an IActionResult.
  3. Create an instance of the LoginViewModel and assign the return URL passed into the action to its ReturnUrl property.
  4. Return the model with the view.
10
07-10 Adding the HTTP POST Login Action

Adding the HTTP POST Login Action

The HTTP POST Login action will be called when the user clicks the Login button in the Login view. The view’s login form will send the user data to this action; it therefore must have a LoginViewModel as a parameter. The action must be asynchronous because the PasswordSignInAsync method provided by the SignInManager is asynchronous.

  1. Open the AccountController class.
  2. Add an async HTTP POST action called Login that takes an instance of the LoginViewModel as a parameter and returns a Task<IActionResult>.
  3. The first thing to do in any HTTP POST action is to check if the model state is valid; if it’s not, then the view should be re-rendered.
  4. Sign in the user by calling the PasswordSignInAsync method, passing in the username, password, and remember me values. Store the result in a variable called result. The last parameter determines if the user should be locked out, if providing wrong credentials.
  5. Add an if-statement checking if the sign-in succeeded.
  6. Add another if-statement, inside the previous one, that checks that the URL isn’t null or empty and that it is a local URL. It is important to check if it is a local URL, for security reasons. If you don’t do that your application is vulnerable to attacks.
  7. If the return URL exists and is safe, then redirect to it in the if-block.
  8. If the URL is empty or isn’t local, then redirect to the Index action in the Home controller.
  9. Add a ModelState error and return the view with the model below it. Place the code below the if-statement, to be certain that it only is called if the login is unsuccessful.
11
07-11 Adding the Login View

Adding the Login View

You need to add a view called Login to the Account folder, to enable visitors to log in.

  1. Add a MVC View Page view called Login to the Views/Account folder.
  2. Delete all the content in the view.
  3. Add an @model directive for the LoginViewModel class.
  4. Use the ViewBag to add a title with the text Login.
  5. Add an <h2> heading with the text Login.
  6. Add a <form> that posts to the Login action in the Account controller. Use Tag Helpers to create the form, and to return the return URL.
  7. Add a validation summary that only displays errors related to the model.
  8. Add a <div> that holds a <label> and an <input> for the Username model property, and a <span> for the validation.
  9. Repeat step 8 for the Password and RememberMe properties in the model.
  10. Add a submit button with the text Login to the form; place it inside a <div>.
  11. Start the application without debugging (Ctrl+F5). Log out if you are signed in.
  12. Click the Edit link for one of the videos. The Login view should be displayed because you are an anonymous user. Note the ReturnUrl parameter in the URL.
  13. Log in as a registered user. The Edit view, for the video you tried to edit before, should open. Note the username and the Logout button at the top of the view.
  14. Click the Logout button to log out the current user. You should be taken to the Index view. Note the Login and Register links at the top of the view.
12
07-12 Summary

Summary

In this module, you used ASP.NET Identity to secure your application, implementing regis­tration and login from scratch.

The first thing you did was to add a User entity class that inherited the IdentityUser base class. This gave you access to properties such as Username, PasswordHash, and Email.

Then you plugged the User entity into a UserStore and an IdentityDb class. This made it possible to create and validate a user, which then was stored in the database.

The UserManager and SignInManager were then used to implement registration and login for users, with a cookie that handles the Forms Authentication.

The [Authorize] and [AllowAnonymous] attributes were used to restrict user access to controller actions.

You also added views to register, log in, and log out a user.

In the next module, you will use front-end frameworks to style the application.

08 - Front-End Frameworks

1
08-01 Installing Bower and the Frameworks

Front-End Frameworks

In this module, you will learn how to install front-end libraries using Bower. The two types of libraries you will install are for styling and client-side validation.

Bootstrap: This is the most popular library for styling and responsive design. You will use some Bootstrap CSS classes to style the video list and the navigation links. You can find out more about Bootstrap on their site: http://getBootstrap.com.

JQuery: You will use JQuery and JQuery Validation to perform client-side validation. This will make sure that the user input is conforming to the validation rules before the data is sent to the server action. Some validation rules are added by the framework; others you have added yourself to the entity and view model classes as attributes. Examples of valida­tion rules are: password restrictions set by the framework (can be changed), and the Required, MaxLength, and DataType attributes.

Installing Bower and the Frameworks

Bower is the preferred way to install front-end frameworks in ASP.NET Core 2.0. When the libraries have been installed, they must be referenced from the _Layout view for global access, or in individual views for local access. You can use the environment tag to specify the environment the libraries should be accessible from. You usually want the un-minified libraries in the Development environment for easy debugging, and the minified versions in the Staging and Production environments for faster load times.

Many times, you can achieve even faster load times if you use Content Delivery Networks (CDNs), servers that have cached versions of the libraries that can be called. You can find information about the Microsoft’s CDNs at: https://docs.microsoft.com/en-us/aspnet/ajax/cdn/overview.

Four attributes are used to check that the JavaScript libraries have been installed: asp-fallback-test-class, asp-fallback-test-property, asp-fallback-test-value, and asp-fallback-test.

Two attributes are used to load an alternative source if the JavaScript libraries haven’t been installed: asp-fallback-src and asp-fallback-href.

  1. Open the Startup class and add the Static Files middleware by calling the app.UseStaticFiles method above the UseMvc method call in the Configure method to enable loading of CSS and JavaScript files.
  2. Add a Bower Configuration File to the project node. It’s important that it is named bower.json. You can search for bower in the New Item dialog. Note that: When installing Bower in ASP.NET Core 2.0, you might encounter an error that prevents Bower to be installed properly. To solve this, you have to add the following line to the Bower Configuration File and save the file. "registry": "https://registry.bower.io"
  3. Install Bootstrap by typing it directly into the dependencies section of the bower.json file. Bootstrap has a dependency on JQuery, so that library will be automatically installed. Save the file to install Bootstrap.
  4. Install the JQuery-Validation and JQuery-Validation-Unobtrusive libraries using Bower. Save the file to install the libraries.
  5. Expand the Dependencies/Bower node in the Solution Explorer to verify that the libraries have been installed.
  6. Expand the wwwroot node in the Solution Explorer. It should now have a folder named lib, under which the installed libraries reside.
  7. Open the _Layout view and add two <environment> Tag Helpers below the <title> element inside the <head> element. The first should only include the Development environment, and the second should exclude the Development environment. You can change environment in the project settings. In these two Tag Helpers, you specify CSS libraries you want to load when the view loads.
  8. Add the un-minified Bootstrap library to the Development environment and the minified version for any other environments.
  9. Repeat step 6 at the bottom of the <body> element. In these two Tag Helpers, you specify JavaScript libraries you want to load when the HTML has finished loading.
2
08-02 Adding a Navigation Bar

Styling with Bootstrap

Let’s add a navigation bar in the _Layout view, and style it and the video list in the Index view, using Bootstrap.

Adding a Navigation Bar
  1. Open the _Layout view.
  2. Add a <nav> element at the top of the <body> element and decorate it with the navbar and navbar-default Bootstrap classes, to create the navigation bar placeholder.
  3. Add a <div> inside the <nav> that will act as the navigation bar container. Add the container-fluid Bootstrap class to it, to make it stretch across the whole screen.
  4. Add a <div> inside the fluid container <div>; it will act as the navigation bar header. Add the navbar-header Bootstrap class to it.
  5. Add an anchor tag to the navigation bar header. This link will take the user back to the application root (the Index view) if clicked. Add the Bootstrap class navbar-brand and the text Video Application to it.
  6. Cut out the <div> containing the call to the _LoginLinks partial view and paste it below the navbar-brand <div>. Add the Bootstrap classes navbar, nabar-nav, and pull-right to the <div> to turn the links into buttons and right align them in the navigation bar.
  7. Open the _LoginLinks partial view.
  8. Cut out the Razor expression that fetches the user’s name, and paste it at the top of the form. Delete the <div>.
  9. Add the Bootstrap classes navbar-btn, btn, and btn-danger to the submit button to style it.
  10. Add the Bootstrap classes btn, btn-xs, btn-default, and navbar-btn to the <a> elements to turn the links into buttons.
  11. Add 10px left and bottom margin to the Create button in the Index view.
  12. Save all files and start the application without debugging (Ctrl+F5). A navigation bar with the brand name and the links should be visible at the top of the view.
3
08-03 Styling the Index View

Styling the Index View

Let’s make the video list a bit more appealing by adding Bootstrap classes to make them appear like panels. The image below shows the end result.

  1. Open the Index view.
  2. Add the Bootstrap classes btn and btn-success to the Create link in the Index view. This should turn the button green, with the default Bootstrap theme.
  3. Open the _Video view.
  4. Add the panel and panel-primary Bootstrap classes to the <section> element, to turn it into a panel with blue heading background.
4
08-04 Adding Client-Side Validation

Adding Client-Side Validation

To take advantage of client-side validation, you only have to add the jquery, jquery. validate, and jquery.validate.unobtrusive JavaScript libraries. You have already added the libraries in a previous section, so now you only have to check that the validation works.

Pay attention to the URL field in the browser as you click the Login, Edit, and Create buttons when you try to enter invalid data (you can for instance leave one of the text fields empty). The URL should not refresh, because no round-trip is made to the server.

  1. Run the application without debugging (Ctrl+F5).
  2. Click the Edit button in the Index view; this should display the Login view. If you’re already logged in then click the Logout button and repeat this step.
  3. Leave the Username field empty and click the Login button. Pay attention to the URL; it should not refresh. An error message should be displayed in the form.
  4. Log in with correct credentials.
  5. Clear the Title field and click the Edit button. Pay attention to the URL; it should not refresh. Two error messages should be displayed, one for the validation summary, above the controls, and one beside the text field.
  6. Click the Back to List link to return to the Index view.
  7. Click the Create link below the video list.
  8. Try to add a video with an empty title. Pay attention to the URL; it should not refresh. The same type of error displayed for the Edit view should be displayed.
5
08-05 Summary

Summary In this module, you used Bower to add JQuery libraries to enforce client-side validation, and Bootstrap to style the Index view with CSS classes.

This was the last video in this course. I truly hope you enjoyed it. Keep an eye out for other courses by me.

You can view and review the lecture materials indefinitely, like an on-demand channel.
Definitely! If you have an internet connection, courses on Udemy are available on any device at any time. If you don't have an internet connection, some instructors also let their students download course lectures. That's up to the instructor though, so make sure you get on their good side!
4.4
4.4 out of 5
36 Ratings

Detailed Rating

Stars 5
10
Stars 4
22
Stars 3
1
Stars 2
1
Stars 1
2
768efde40a01ae09addf4153c7c77d36
30-Day Money-Back Guarantee

Includes

3 hours on-demand video
Full lifetime access
Access on mobile and TV
Certificate of Completion