What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
ASP.NET Core Fundamentals
by Scott Allen
This course will cover the fundamentals of what you need to know to start building your first ASP.NET Core application with the MVC framework.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Learning Check
Recommended
Course Overview
Course Overview
(Music playing) Hi, this is Scott Allen, and welcome to my course on ASP.NET Core. ASP.NET is the new web framework for Microsoft. It's been redesigned from the ground up to be fast, flexible, modern, and work across different platforms. Moving forward, ASP.NET Core is the framework you want to use for web development with .NET. In this course, we will build an application by starting from an empty project, so you can see how all the pieces come together. We'll install middleware to build a processing pipeline, and then move on to work with the MVC framework. If you have any experience with MVC or web API over the last few years, you'll notice some familiar features. We still have models, views, and controllers, but I'll also show you what's new, as we look at tag helpers, view components, and how dependency injection is a first class design pattern. You'll also see how to work with SQL Server using the new entity framework, implement forms authentication using the ASP.NET identity framework, and install and use CSS and JavaScript libraries like Bootstrap and jQuery. By the end of the course, you'll have everything you need to start using ASP.NET Core and write an application that can create, edit, and view data from a database. I do expect that you are already familiar with the C# programming language, and have some knowledge of HTML and web programming in general. If not, we have lots of C# and HTML courses to choose from on Pluralsight, and after the course look for new courses covering even more details of programming with ASP.NET Core.
Building Your First ASP.NET Core Application
Introduction
Hi this is Scott and this course will help you build your first application with ASP.NET Core. In this course, we'll be using the new ASP.NET Core Framework version 2.0 to build a web application with APIs that can display and edit restaurant information. I'm assuming you already know how to work with the C# language, because we're going to spend our time together focusing on the ASP.NET Core Framework. In this first module, I will show you a couple different techniques that you can use to get started with your first project.
Setup
For setup, we need to make sure that you have the .NET Core Framework installed on your computer. If you go to the webpage microsoft.com/net, this is a webpage where you can download the various flavors and versions of the .NET Framework. Of course, web pages will update and change all the time, but I can find a downloads link here, and if I follow that link, what I am interested in is downloading .NET Core. So after following one more link, I will come to a table that shows all of the .NET Core downloads. And I do want to download a package that is the .NET Core SDK, or Software Development Kit. The SDK includes all the tools that you would use to create a .NET Core project. You can see there are downloads and installers for Windows, also for macOS, and also for Linux, because .NET Core supports all three platforms. And once you follow one of these links and run the installer, or follow the instructions to install the package that you downloaded, it is then time to select an editor that you can use for writing code. You can use any text editor when writing code for .NET Core. There are no ties to the Microsoft Editors and IDEs. In this course, I will mostly be using Visual Studio, and you can download the free version of Visual Studio, which is Community. I'll be using Visual Studio 2017, and after you download Visual Studio, make sure you install the latest updates. I'll be using Visual Studio 2017 with update 3, because that includes some tools that make .NET Core development with 2.0 a little bit easier. Now this version of Visual Studio only works on Windows, but there is also a version of Visual Studio for the Mac. And if you want a more lightweight experience on Windows or the Mac or on Linux, you can download Visual Studio Code, and I will be showing you some features of Visual Studio Code throughout this course. But once you have the SDK installed, and once you have your tool of choice installed, it's time to create your first project. Let's do that with Visual Studio 2017 first.
A New Project
Inside of Visual Studio 2017, I can always start a new project by going to the File menu, selecting New, and then select Project. This will launch the new project dialog where I can select the general type of project that I want to create. I want to create a .NET Core Visual C# project. And specifically in the templates that are listed for .NET Core, I want to create an ASP.NET Core Web Application. I can give my application a name. Let's call this OdeToFood, because it will be all about restaurants, and let's place this into a development folder that is on my hard drive. And with those settings in place, I can select OK, and the next dialog that appears will offer me various different starting points for my application. So first of all, I do want this to run on .NET Core. I do want to make sure that I have ASP.NET Core 2.0 selected. And then the various templates inside of here, I can start with an empty project, or with a project that is geared or set up towards producing a Web API, which would be an HTTP-based API. I can also create a web application, and there are templates to work with various front-end frameworks, like React. Since we really want to take a look at how things work and how to put things together from scratch, I'm going to select the empty template. The empty template is going to produce a project that will have the bare minimum of ASP.NET Core inside. And once I press OK, Visual Studio will go out and create that project and will create a solution for that project to live inside of, and it will go out and automatically restore any NuGet dependencies that are required by this project, so the libraries that I'm going to use when I'm building my web application. As soon as all of that work is finished, let me go to the Debug menu and say Start Without Debugging. I could also do that with Ctrl+F5. I don't want to debug and step through my program, I just want to give this is a quick run and see if everything is working. And by default, this empty application, which appeared over here on a second screen, can only display the text Hello World! That's how simple it is. But I do want to call your attention to a couple things. First of all, if I go into the Windows System Tray, I can see a little icon for IIS Express. That's the development web server that Microsoft ships with Visual Studio. And if I right-click on this, I can see that my application, OdeToFood, is running in this version of IIS, it's running on localhost and port 52311. Those ports are randomly assigned, so you might see a different port number, and that's okay. And if you've done any ASP.NET development in the past, all of this might look very familiar. You create an ASP.NET project by going to File New Project. You can run that project and Visual Studio will launch IIS Express to host the application, and then launch a web browser to browse to that URL and IIS Express where the application lives. But there are some significant differences between ASP.NET and ASP.NET Core. We will see some of those differences as we move through the course and uncover features of ASP.NET Core, but before we start figuring out what is inside of this project and how we can make this application behave differently, let's take a look at another way to create a new ASP.NET Core application, this time using tools that you could use in any environment, from macOS, to Linux, to Windows.
Command Line Tools and Code
For those of you working on macOS or Linux, and even for those of you who work on Windows, I want to show you another approach that you can use to create, run, and modify an ASP.NET Core web application. Let's get started by opening up a Command Prompt or a Shell. Because regardless of what platform you're on, you should be able to come to your command-line interface and execute the command .net. This is the .NET Core command-line interface, or CLI. And I can ask the CLI what version of .NET Core I'm running. I can see I'm running version 2. And I can also ask dotnet for some additional help. I just want to scroll up a bit to show you the list of commands that are available with dotnet. I can create a new project with this command-line interface. I can also run a project and build a project, that is, compile all of my source code files. So anything that I can do with Visual Studio, I can also do with this command-line tool, which is a cross-platform tool. It will run anywhere that is supported by .NET Core. Let's take a look at the option to create a new project. What I will do is switch over into my development folder. Let's make a new folder temporarily called OdeToFood2 and switch into that folder. And inside of here, I want to run dotnet new. What I will see is a help screen that basically tells me you haven't provided enough information for me to create a new project. One of the options that you have to specify here is the type of template that you want to use to create this new project. So this list of templates will look very familiar after seeing the File New Project dialog in Visual Studio. Just like in Visual Studio, there's a template to create an empty ASP.NET Core project, as well as a full web app, as well as a web app with Angular and with React.js. So if I run dotnet new web, what I should get is an ASP.NET Core Empty project, the default language will be C#, and this project will contain the same files and have the same behavior as the project that I created using Visual Studio. So let's try dotnet new web. Dotnet will infer the name of my project from the name of the folder that I'm in, so this is the OdeToFood2 project, and if I do a directory listing, I can see just like in Visual Studio, I have a Program.cs, and a Startup.cs, and a wwwroot folder. We're going to explain the purpose of those files and that directory as we move throughout this course. For right now, I just want to dotnet run this project to see how this web application behaves. Behind the scenes, dotnet run will see that the project hasn't been built yet, it will build the project, and then we'll start my application running. Now, this time, since I'm not in Visual Studio, I'm running this from the command-line, I won't be using IIS Express. IIS Express only works on Windows. Instead, I have an application that started its own web server, and this web server is listening on localhost:5000. By the way, I could execute this same command, dotnet run, in the folder where OdeToFood exists, and I would have the exact same behavior. So there are no ties between .NET Core and ASP.NET Core in Visual Studio. But what I should be able to do now is open up a web browser and come over to localhost:5000, and just like with the project that I created in Visual Studio, I see the output Hello World! And I know that request reached this OdeToFood2 project, because I can see some logging statements that indicate that a request started and a request finished. Now let me press Ctrl+C to stop that application, and what I want to do is use Notepad to look at the file Startup.cs. Again, more on this file later in the course. For right now, I just want to call your attention to the string literal here at the bottom of the file Hello World. That is the response that is being sent to the browser for every request to this web application, that's all I can do. And I just want to modify this to say Hello World from OdeToFood2 and then save this file to show you that I can use any text editor and any tools that I want to interact with an ASP.NET Core project and there are no ties to Visual Studio or Windows. .NET Core is a cross platform framework. Now I can go back into my command history and execute dotnet run again. Dotnet run will see that a file has changed. It will recompile the application. And again, we're listening on locahost:5000. If I do a refresh, I can see that the change that I made to that C# file is now reflected in the output of my program. Now let's open up this new project in Visual Studio Code. I could also open up this project with Visual Studio if I wanted to. There is a csproj file here, a C# project file. That's a project file that Visual Studio understands. So I could open this application and edit this application with Visual Studio. More details on csproj in just a moment, but I could open this application with Visual Studio. I could open this application with Visual Studio Code. So when you install Visual Studio Code, there's an option to put the code command in your path. That allows you to launch Visual Studio Code from the command-line. And by passing in this extra dot, I'm telling Visual Studio Code please open the current folder as a project. So when I do that and open up this project, I can see the files that are inside the folder, there's my Startup.cs file. I can modify this file again and change the text that's inside. And I just want to show you, if you haven't used Visual Studio Code before, the experience is very similar to Visual Studio. There are features like IntelliSense. So if I go up to my http context and go to the response object, I can see different things that I can do with the response. I can send files, I can do a redirect, and I can also run and debug my application from Visual Studio Code. There's just a couple things that you'll want to install. First of all, the square icon over here on the left, this is a list of the extensions that I have installed. I do want to have the C# extension installed. If you currently don't have that installed, it's very simple to do a search for the C# extension. And then there will be an install button here that I can click and that extension will get installed. I can also see when I open up this project for the first time a little prompt that says the required asset to build and debug are missing from this project. Do I want to add them? I can say yes, let's add them. What that will do is add a .vscode folder into this directory, and inside of that folder there will be a couple JSON files that tell Visual Studio Code what to do when I want to run this application or debug this application. So let's come over here on line 30 and I want to click over here in the left-hand side beside the live numbers, and that little red circle indicates that I have a breakpoint there. Then I can come to the debug menu, and I can say that I want to debug this application. I could also just press F5 to do that. What this will do is build the application, it's going to again launch a web browser that points to localhost:5000 and will do that for me, and the browser is not going to respond right away, because back here in Visual Studio Code I am sitting on that breakpoint. I can now hover over variables and fields. I can also continue now, tell this to continue until I hit the breakpoint again or a breakpoint again. And when I do that, I can now see the response in the browser. So let's go in and stop debugging, and if you haven't used Visual Studio Code before, hopefully this little demonstration will show you the Visual Studio Code is a quite capable environment. And it's a tool that you can use across different platforms. So for the rest of this course, I will primarily use Visual Studio. But anything that I show you or do in Visual Studio, you could also do with Visual Studio Code, and ultimately, you could do with any text editor and the .NET CLI.
The ASP.NET Core Project Structure
Back inside of Visual Studio, let's take a look at what is inside of our new empty project. This project isn't completely empty, of course, there's just enough code here to say Hello World, and we want to see how that works so we can understand how ASP.NET Core works. First of all, both Visual Studio and the .NET CLI that we just used, they both understand this application to be a .NET Core project, because there is a project file inside of the folder on the hard drive. The project file for C# projects has a .csproj extension, and that file is displayed in Visual Studio Solution Explorer window as this node in a tree. Everything inside of that node is part of the OdeToFood project. Let's take a closer look at that file. I can inspect the contents of my csproj file by right-clicking that node and going to the Edit OdeToFood.csproj context menu item. And when I click this entry, just a couple observations. First of all, for those of you who have edited csproj files inside of Visual Studio before, notice that I didn't have to unload my project to edit this file. The XML file simply opens up in an editor for me to modify. And secondly, the csproj file format is relatively simple compared to csproj files of the past. And there's two important pieces of information inside of here. First of all is the TargetFramework. The netcoreapp2.0 value that we see for TargetFramework, that's a moniker, or short name, that says my web application is going to use .NET Core version 2. I could also target the full .NET Framework, the desktop framework that is installed with windows. That would be a moniker like net46 for .NET 4.6, or I could also use previous standards for .NET Core, for example, netcoreapp1.0, like you can see in the tool tip that appears here. The other piece of information is the single package reference that I have here. In the future, we're going to have additional package references, but these are the NuGet packages, or the libraries, that my application depends on to do work. Right now we have a single package reference, it's Microsoft.AspNetCore.All, that's what we call a metapackage, because it brings in additional packages. It's bringing in all the packages related to Microsoft.AspNetCore. You'll also notice the csproj file includes this ItemGroup that says please include this wwwroot folder. That's typically not something that you have to do for every folder, but wwwroot is a special folder. We will talk about that in more detail in the next module. For right now, I just want to point out that what you don't see in the csproj file is a list of all the C# source code files that are a part of the project, and that's because with .NET Core, the file system really determines what is inside of my project, so every .cs file in the folder with the csproj file, or in a subfolder, is a part of my project. And to demonstrate that, let me come back out to the command-line. So I am inside the same folder that Visual Studio is pointing to, and from here, I could do a dotnet build or a dotnet run. I could run this application just as easily from the command-line as I can from Visual Studio. And what I want to do is add a new C# code file. What I'll do is echo a null character to a file. Let's just call it foo.cs. And when I press Return, I want you to watch the Solution Explorer window, because foo.cs will become part of my project. And I could edit this file using Visual Studio Code or Notepad or any editor, and the next time I do a dotnet build, foo.cs would be included as part of the compilation process. And if I delete foo.cs from the file system, foo.cs will disappear from Solution Explorer. So that's one important aspect of .NET Core and ASP.NET Core. The projects are now based on the file system. Yes, there's a csproj file that includes information about what framework I'm targeting, what packages that I'm using, but there is no manifest that has to list everything that's in my project. Any C# file that I place into this folder will be included as part of the build. And now let's talk about the two C# files that are here in this project already, program.cs and startup.cs. First of all, program.cs. Program.cs, if you've been doing .NET programming for any length of time and you've ever written a console application, the structure of this file looks like the structure that you would see inside of a typical console mode application. That is, I have a program class, and I have a static void Main entry point that takes command-line arguments. And this is because an ASP.NET Core application is structured as a console mode application. It's something I can run from the command-line. We did that previously with OdeToFood too, I did a dotnet run, that spun up a web server, and I could browse to that web server on localhost:5000. I could do the same thing with this project. and when I do a dotnet run, what the dotnet runtime has to do is find an entry point for my program. So it's going to look for the static void main entry point, and what this main entry point will do is build a web host, passing along any arguments from the command-line, and then tell that web host, or that web server, to start running. Now when Visual Studio runs this particular project, it just happens to execute the same code, because behind the scenes it's all just dotnet run. But it will place this web host behind IIS Express, so IIS Express acts much like a proxy server. It's going to forward requests into my application. But my application is a separate process that is up and running and has its own server. That web server is configured inside of BuildWebHost. We're doing that by using a class from Microsoft.AspNetCore.Hosting, and that is the web host class that has a static method that makes this easy to spin up a web server, it's called CreateDefaultBuilder, and once I have a web host builder, I need to configure that builder a little bit. There's additional methods here that I can invoke to customize this environment, but right now things are very simple. This is an empty project, after all. We're simply telling that web host builder to use the startup class to configure how the application should behave. We'll look at startup here in just a second, but after configuration, I can tell the web host builder to build itself, that gives me back an IWebHost, and I can tell the web host to start running and listening for HTTP connections or HTTPS connections. So program.cs and a static void main entry point always a part of your ASP.NET Core application. And most ASP.NET Core applications will also include the startup class that will configure how the application behaves. There's two methods inside of the startup class. There's ConfigureServices, which we will look at a little bit later, and there is the Configure method. We'll look at this Configure method in much more detail in the next module. For right now, I just want to tell you that this Configure method configures the HTTP processing pipeline for my ASP.NET Core application. That is, for every HTTP message that arrives, it is the code inside of this Configure method that will define the components that respond to that request. Right now we really just have a single piece of code that's going to say for every request that arrives, it doesn't matter what the URL is or what headers are present in that request, I just want to write out into the HTTP response a message that contains the text Hello World. So, no fancy HTML, no JSON responses, we're simply going to respond to every request with this text Hello World. Throughout the course, we will be customizing this Configure method so that we can use the MVC Framework, so that we can respond with HTML, so that we can respond with JSON for our APIs, but right at this moment, this is the only thing the application can do. And for those of you that have done ASP.NET development in the past, notice in my project there's no remnants of the previous version of the ASP.NET. There is no global.asax. There is no web.config file. In fact, in my project currently, there's no configuration file at all that I can use during runtime to hold configuration items, like a database connection string. What I want to do next is show you how we can take this application and make this greeting message configurable. That is, I want some sort of configuration file as part of my project that I can update either in development or in a production server that can change the text that we use to respond to every request. Let's look at that next.
Adding a Configuration Source
To understand how we will add some configuration data to our application, we first have to understand the little bit of code that we saw inside of the program.cs file, specifically, the bit that says WebHost.CreateDefaultBuilder. A web host builder is an object that knows how to set up our web server environment, and the default web host builder sets up our environment in a specific way. These are settings we can change by writing our own builder code, but here is what happens with the default. First, the default web host builder will set up the application to use the Kestrel web server. Kestrel is the codename for a web server that ships as part of ASP.NET Core, and this is a cross-platform web server that runs on all platforms supported by .NET Core. It is this server that will listen for HTTP connections in our process, and it is this server that we can use directly if we run from the command-line. The builder also sets up IIS Integration. If our application is running behind IIS, it is this integration that allows IIS to pass through Windows credentials to the Kestrel server that is running in our process. That's important if you're building intranet applications for users inside your company firewall; otherwise, you probably don't care about the IIS integration. The builder also sets up some default logging, and we can see the output of this logging in the console when we run from the command-line, and in Visual Studio's output window when we run from Visual Studio. And again, all of these settings can be changed and customized, but here is the important part when it comes to configuration. The default web host builder will create an object that implements an IConfiguration interface. We can access this object throughout our application, and we can retrieve configuration information through this interface. This is what I'm about to demonstrate. By default, configuration information can come from a few different sources. First, the configuration service can try to read settings from a file in the root of the project named appsettings.json. The configuration service will also try to read values from a user secrets file. We will talk about user secrets later in the course. We can also pass configuration values through environment variables and through command-line arguments. So let's take a look at how to set up appsettings.json and use this IConfiguration service. My goal, again, is to replace this hard-coded string, Hello World, with a greeting that I read from a configuration source. And for those of you that have programmed an ASP.NET previously, we no longer use XML configuration files like web.config, but this WebHost builder, the default builder that we use inside of program.cs, it will set up a configuration source that includes a JSON file named appsettings.json. All I need to do is get a file named appsettings.json into the root of my project. One easy way to do that with Visual Studio is to right-click on the project, say that I want to add a new item, and in this list of templates for different types of files that I can add, there is a template for ASP.NET Configuration File, and it already has the appropriate name, which is appsettings.json. So let me add this to the project, and I can see that appsettings.json already has a connection string defined. I'm going to leave this here so later in the course when we start to work with a database, we won't already have this connection string in place, but I'm going to add a new property, which is the Greeting that I want my application to display. So the key to this configuration entry will be Greeting, and the value, let's just make this Hello with a couple exclamation points. Let me save this file. This is all we need in our configuration source. And now inside of startup.cs, I need access to that IConfiguration service, so that I can read that value. The way I can do that is to ask for another parameter here in my Configure method, and I can do this because ASP.NET Core uses dependency injection in various places throughout the framework. One of those places is when ASP.NET Core invokes this Configure method. Then ASP.NET Core will actually analyze the parameters that I'm asking for here in this method, and if ASP.NET understands the types of those parameters, it will pass in a service or an object that can fulfill this requirement. So for right now, this Configure method requires an IApplicationBuilder and an IHostingEnvironment. We'll talk about both of those more in the next module. But ASP.NET Core knows about those services, it knows objects that implement those interfaces, so it will pass those objects in. I also now want something that implements IConfiguration. To do that, I'm going to need to bring in a namespace, Microsoft.Extensions.Configuration. An easy way to do that is with the Ctrl+period option in Visual Studio, and I will name this parameter configuration. So now I have hopefully an object that I can use to read that greeting value. So inside of this lambda expression, which is the little bit of code that executes for every request that we've received, let me first try to read that greeting value. So I can go to the configuration service, and I can index into this object using a key, that would be the name of the configuration entry in my file, which is Greeting, and that should give me a string back, which will be the value associated with that greeting. Now later in the course, we'll look at some more sophisticated techniques that we can use to map configuration items to complex objects, but for right now I just want to pull out this string, and when the user makes a request to this website, I want to display that greeting. And now I will save all the files in this project, and instead of running this application from Visual Studio, let me come over to my Command Prompt, and let's run the application here. I will simply do a dotnet run. Now when Visual Studio creates a new ASP.NET Core project, it does add a file called launchsettings.json, which is a configuration file that .NET the CLI tool will actually use to figure out how to get this process started. And launch settings is telling dotnet run to launch and use a port of 52312, so that's slightly different than what we saw before when I did dotnet run, and we were listening on localhost:5000, but that's all because of launchSettings.json. You can go in there and look and tinker around if you want to see how to change some settings. All I want to do is go to the browser, and instead of going to port 52311, let's go to port 52312, and I can see the value from my configuration file, which is Hello. Now you might remember that I told you that this IConfiguration service actually uses a few different sources of configuration information. So not only the appsettings.json file, but also environment variables and command-line arguments. And the way this works is that if IConfiguration finds a setting inside of my appsettings.json file, it will use that setting. But because IConfiguration also looks at environment variables next, if there's an environment variable named Greeting, that greeting value would override the setting here in appsettings.json, and if there's a command-line parameter, which by default is the last configuration source added, a greeting value passed via the command-line will override both an environment variable and a setting inside of appsettings.json. And this can be useful, because you can put some default settings for development inside of appsettings.json, and you can deploy that file into production, and when you set up your production server, if you create environment variables to override the settings inside of appsettings.json, that's one nice way to override things like a connection string for production and also keep secrets like database passwords on your server, and you don't have to check them into source control. But more on those scenarios later. For right now, I just want to demonstrate that if I come back to the command-line, let's kill this process, now let's do a dotnet run and say that Greeting= and let's enter Hello with a few more exclamation points to see if we get the correct value. So once this process starts, I'll come back to the browser, and we will refresh on port 52312, and I can see I get the configuration value that was specified from the command-line that's overriding or hiding the value that is in appsetting.json. So hopefully this will give you a little bit of an idea of how configuration works in ASP.NET Core, and how we can use an IConfiguration Service. And I promise you we'll see some more sophisticated techniques as we move throughout the course. But for right now, I want to return to this topic of dependency injection and how we can just ask for what seems like arbitrary things here inside of the Configure method. Because what I want to do next is have this method not go directly to the configuration service, I want to create a custom greeting service that this method uses. And this greeting service might retrieve a greeting from the configuration source, but it might also go to a database or a web service. I'm going to abstract away the source of this greeting. And this will show you can implement your own custom service and have ASP.NET Core manage that service and inject that service into different components that will need that service.
Creating and Injecting Greeting Service
Now imagine instead of going directly to the configuration service, we had a service that implements an interface IGreeter. We haven't created that interface yet, and before I do I just want to figure out what it would look like. So instead of going to configuration, I want to go to the greeter service, and I wanted to get the message of the day. And behind the scenes, whatever object implements this interface, it might be going to a database, it might be going to a web service, it might be randomly picking a greeting from a text file full of greetings. This code inside of the startup file doesn't really care; it just wants a service that implements this interface, so it can invoke a method and give back a string value to display to the user. Let's use Visual Studio to create the IGreeter interface. I can do that by putting the cursor on top, pressing Ctrl+period, selecting the first entry here, which is generate an interface IGreeter. And over in the Solution Explorer, let me open up this file. And back in startup, I can also generate a method into this interface, so press Ctrl+period here, generate a method. If I look inside of IGreeter, I can now see I do have a method, GetMessageOfTheDay, and I want this to return a value of type string. Let's also go ahead and create a class, the Greeter class, that implements IGreeter, and I will use Visual Studio to generate a method for me using the Ctrl+period trick again. But instead of throwing a System.NotImplementedException, let's start off with the simplest possible thing. I'll return a string that says Greetings. So now I have an interface definition for my service, I have a concrete implementation for that interface that will still return a hard-coded string, we'll change that in just a bit, and I have a startup file that will ask ASP.NET Core for some object that implements IGreeter and use that object to get and display the message of the day. Before I make any other changes, let me go ahead and save all the files in this project. And let's come back to the console, and what I want to do is stop this process here. What I want to do is go back to running this through Visual Studio. So, Debug, let's start without debugging. This application is not going to work yet, but the error message that we're going to see is going to be informative. So this opened on a different window, let me drag it over, and I can see I have an error message, No service for type IGreeter has been registered. So in order for ASP.NET to inject a service that implements IGreeter, I first have to configure this service. I have to tell ASP.NET Core what do you use when some component in the system needs an IGreeter. You see, some services have already been registered for us, so the IApplicationBuilder, the IHostingEnvironment, we wouldn't be able to get very far at all in an application if ASP.NET did not already have those services configured into the application. But IGreeter is a custom service that I'm building, and I need to tell ASP.NET Core about this service. And that's the purpose of the ConfigureServices method. So the Startup class really has two different responsibilities; one is to give you a place to register your custom services, so that ASP.NET Core can use those services and pass them around not only to itself, if you want to customize how the ASP.NET Core framework behaves, but also to other areas of the application like the Configure method. The Configure method, its responsibility is to set up the HTTP processing pipeline that's going to be used to respond to requests. And right now we respond to every request just by writing out a greeting, but in order to get this IGreeter, I need to register this service. I can do that by walking up to this IServiceCollection object, and there's a number of methods and extension methods here that will allow me to add services. So I can add an antiforgery service that's already built into ASP.NET Core. And I can add my own services. So there's a couple different ways that you can register a service and tell ASP.NET about the lifetime of that service, that is, how long should that service be around and when should ASP.NET create an instance and then tear down an instance of that service. So the AddSingleton method is a way of telling ASP.NET Core you will only ever need one instance of this service for the entire application. There's also AddTransient, which is a method I can use to tell ASP.NET Core anytime someone needs an IGreeter, create a new instance for them. You don't need to keep it around or save it or reuse it. And there's also AddScoped. This will allow me to create a service that ASP.NET Core will create once per every HTTP request, so the service is scoped to a specific request, and the service is reused throughout that request and then thrown away. We'll be using these methods more throughout the course, and I'll explain some of the nuances between them. For right now, we're just going to add a singleton. So I'm going to tell ASP.NET Core whenever someone needs a service that implements the interface IGreeter, then what I want you to do is create an instance of the following class, Greeter, and pass that service along. So there's a number of different overloads here that allow you some control over how that instance is instantiated, but if I just use two different generic type parameters here, the first type parameter specifies the type of the service that I'm registering and the second generic type parameter Greeter, that tells ASP.NET Core about the implementation for that interface. And I don't need to provide any other code, because ASP.NET Core should be able to figure out how to create a greeter. So with those changes in place, let me just hit Shift+Ctrl+B to do a build, make sure there's no compile errors, the build was successful, and let's come back out to the browser window and do a refresh. Now I can see I have a successful result. So ASP.NET Core saw that I needed this IGreeter parameter, was able to determine that that would require an instance of this greeter class that we created, so ASP.NET Core instantiated that class and passed it along. What's the big deal, you might be saying, why couldn't I just inside of this method say var greeter = new greeter and create an instance of that class directly? Well, the whole idea of managing these dependencies as parameters and receiving them indirectly instead of using the new keyword is that my software becomes a lot more extensible and flexible and testable. Because now I'm not tied to a specific implementation of IGreeter inside of the Configure method, I can instead configure different types of greeting components to be used at runtime and not have to change anything inside of my application, except this ConfigureServices method. I could also, let's say, inside of a unit test be able to invoke this method and pass in any greeter that I want, instead of trying to work around a greeter that was hard-coded into this method. So that's one advantage of this dependency injection approach that ASP.NET Core takes, and we will see this again and again throughout the course, taking advantage of dependency injection. The ability to take a dependency, like something that implements IGreeter, take that as a parameter to my method, so that dependency is passed to me instead of me creating it directly. And the neat thing about dependency injection is that you can create an entire graph of objects this way. In other words, what would happen if my Greeter class had a constructor that required some parameters? It's easy to see how ASP.NET Core could look at the class in this existing form and say all I need to do is construct a new instance of Greeter and constructing this class is easy; the constructor doesn't take any parameters. But what if I wanted a parameter here like an IConfiguration service? Now, as I bring in the namespace, now ASP.NET Core has a little more work to do inside of startup when it sees that there's a method that needs IGreeter, and now it has to construct a greeter, and it sees that the greeter itself requires some other service, IConfiguration. Fortunately, the ASP.NET container, the dependency injection system, can figure all of this out. So it can analyze my Startup class and say, oh, you need an IGreeter, let me go out and create the one that you specified. When it looks at Greeter, it sees this needs an IConfiguration service, and that service has already been registered by our default WebHost builder. And now what I could do inside of the greeter is save off this configuration source into a field, and I'll generate that field with Ctrl+period. And now instead of returning a hard-coded string, I could go back to retrieving a greeting from my configuration file. I have a little more flexibility now inside of Startup, because it doesn't necessarily need to know where that greeting comes from. If that's an area of flexibility that I need in my software because I anticipate the source of this greeting might change in the future, then I now have an extensibility point where I can create a different implementation of IGreeter and just plug that into the system. So again, a quick build to make sure we don't have any compiler errors, and coming back to the browser, if I refresh, we are now back to the value that comes out of the configuration file. So the goal of this exercise was fairly two-fold. One was to show you some of the inherent flexibility that ASP.NET Core offers, because it achieves extensibility and flexibility through this technique known as dependency injection, and the ability to register services and ask for services as parameters to your methods and to your constructors. And this also hopefully gave you an idea around the configuration system in ASP.NET Core, how you can use appsettings.json and environment variables and command-line arguments to pass configuration information to your application. And we will use both of these techniques and ideas moving forward throughout the course.
Summary
In this first module, we looked at creating an ASP.NET Core application, both using Visual Studio, but also from the command-line. And we learned a few things about how .NET Core works. We've also started to work with the configuration system in ASP.NET Core, as well as the service registration and dependency injection features. We'll build on all these topics as we move to add real features to the application, but first, we'll need a better understanding of the Startup Configure method that is shown here on the screen. In the next module, we'll learn about middleware in ASP.NET Core, and how to customize this Configure method to do exactly what we need.
Startup and Middleware
Introduction
In this module, we're going to move forward with our application and set up our middleware. Middleware in ASP.NET Core controls how our application responds to HTTP requests. Middleware is also how we display error information, and it's a key piece in how we authenticate and authorize a user to perform specific actions. We're going to learn a bit about how middleware works in this module and how to set up middleware during application startup. By the end of the module, we'll have a processing pipeline that allows us to use ASP.NET MVC.
How Middleware Works
When an HTTP request arrives at our server, and let's pretend we have an HTTP POST request to the URL /reviews. Perhaps this is a call to create a new review. What we need is software that will respond to that request. In ASP.NET Core, it is ultimately middleware components that will determine how to process a request. Each piece of middleware in ASP.NET Core is an object, and each piece of middleware has a very specific focused and limited role, so ultimately we need many pieces of middleware for an application to behave appropriately. First, let's imagine that we want to log information about every request to our application. So the first piece of middleware that we might add to an application is a logger. The logger can see everything about the incoming request, the path, the query string, the headers, any cookies and access tokens. And the logger can record information about the request, even change information about the request if it wanted to or reject the request and stop processing right away. But chances are, a logger is simply going to record some information and then pass along the request to the next piece of middleware in the pipeline. That's the next piece of middleware that we've added to our application. Let's say we've added an authorizer. An authorizer might be looking for a specific cookie or access token in the headers. If the authorizer finds the token, it allows the request to proceed. If not, perhaps the authorizer itself responds to the request with an HTTP error code or redirect to send a user to a login page. But otherwise, the authorizer will pass the request to the next piece of middleware. Perhaps this middleware is a router. A router might look at the URL and try to figure out that you want to call some method on a class. And maybe that method returns JSON data or XML data or an HTML page. But the router's responsibility is to look all throughout the application for something to respond to this request, and if it doesn't find anything to respond, it could return an HTTP 404 Not Found error, or the router might find the right component and that component produces XML or JSON, and now the pipeline starts to reverse. So this is a classic pipeline design pattern, but it's bidirectional. The request flows into the middleware in the order that I added that middleware to the application, and when a particular piece of middleware or some other component produces a response, that response flows back out through the middleware. The router would return control to the authorizer that would be able to see that response. The authorizer probably not interested in a response, but the authorizer will return control to the logger, and when the logger sees that the rest of the pipeline is finished, it might record that fact and log the total amount of time taken to process this particular request. And then this HTML response that we've produced with a 200 OK status okay, that would flow out of the server and over the network to the client who is waiting for this result. And this is the essence of what middleware is all about in ASP.NET Core. We set up a series of components to make our application behave a specific way, and respond to HTTP requests. We probably want to add middleware to handle errors. We will need middleware to serve up static files that live on the filesystem. And we need to middleware to send HTTP requests to the MVC framework, which we'll do later in the course, and that will ultimately allow us to show restaurant information to users. Perhaps that restaurant information is an HTML, or could be in JSON or XML. I'm not going to give you all the low-level details about middleware in this course, but let me show you enough of the basics that you can move forward and understand how this pipeline works in an ASP.NET Core application.
Using IApplicationBuilder
Inside an ASP.NET Core application, when the application begins to execute and we are configuring our web host using a builder, we can register a class that represents our startup logic using this UseStartup method. And what ASP.NET Core will do is instantiate this class and invoke two methods. The first method is the ConfigureServices method. We used that in the last module, and it is this method where we can register services for ASP.NET Core to inject into other components. So that includes both custom services, like the Greeter service that we have created, as well as other services provided by ASP.NET that are just not registered by default. For example, when we start using the MVC framework to produce HTML and JSON, we're going to need to register a group of services that the MVC framework requires, but we'll get to that later. The second method that ASP.NET Core will invoke is the Configure method. And it is inside of this method that only ever executes once where we configure our middleware using the object that implements IApplicationBuilder. So we are building an application and adding middleware to handle HTTP request in a very precise way. Currently there's just two pieces of middleware inside of Configure. One is UseDeveloperExceptionPage. For right now, I'm going to comment this code out using Ctrl+K and C. We will come back to that in a moment. And that leaves us with just one piece of middleware, the little bit of code here that is registered using app.Run. And app.Run is something you don't normally see in a real ASP.NET Core application, because you can really only use that to register very simple middleware, middleware that just perhaps writes directly into the response and displays some text, although you could do HTML or JSON here also. Most of the middleware that you will install in a real ASP.NET Core application is middleware that you install by going up to this application builder and invoking a method that starts with the word Use. And I can see in the IntelliSense window there's quite a number of methods that start with the word Use. These are all extension methods for IApplicationBuilder. And as you install different NuGet packages that might contain more pieces of middleware, you'll have access to more Use methods that allow you to install different pieces of middleware. There's middleware here to set up cross origin resource sharing headers. I can install middleware that allows the user to browse directories on my filesystem. There's middleware here to use the MVC framework, model view controller. We'll need to add that middleware later in the course, but for right now let me find a piece of middleware that I install using UseWelcomePage. Like app.Run, this is a simple piece of middleware, and it's going to respond to every request by default and display a simple welcome page. So it might be useful for diagnostics, but if I save Startup.cs and come back to the browser, when I refresh, I will see the result of the UseWelcomePage middleware. And what I want to point out for right now is that you have to be careful about the order in which you install middleware. Because UseWelcomePage responds to every request, now it doesn't matter I go in the application, I could go to /foo or /bar, every request is going to look like this welcome page. The middleware that I have inside of app.Run that displays a greeting will never have a chance to display, because UseWelcomePage will never call the next piece of middleware in the pipeline. However, there are some options that I can pass to UseWelcomePage. Many pieces of middleware will allow you to pass along an options object, in this case the options object is the WelcomePageOptions class that I need to instantiate, and in these options I can set a path for the welcome page to use so it only responds to a request whose path in this case would be /wp. Let me save Startup.cs, come back to the browser, and if I refresh on /bar now, that request is going to tunnel through all the middleware and hit app.Run where we will display our greeting. Because UseWelcomePage inspected that request and said no, I'm not going to handle this one, let's just pass it along to the next piece of middleware. But if I go to instead of /bar, go to /wp, now we're back to the welcome page. So now throughout the rest of this module, and even later in the course, we're going to be installing different pieces of middleware, and I'll always give you an idea of where that middleware should appear among all the other pieces of middleware that you have, because the order in which you invoke these use methods is significant. And now for those of you who are wondering about middleware and how it might operate, at a lower level, let me do a little bit of a demonstration. In order to follow this demonstration, you really need to be comfortable with delegates and .NET and the fun type. And I will say that this is not the type of programming that you would do on a day-to-day basis, so if you're not comfortable in these lower level details, which you don't need on a day-to-day basis, and feel free to skip ahead. But what I want to show you is how to write a piece of middleware using the app.Use method. This allows me to write a slightly lower level piece of middleware than app.Run. App.Use requires me to pass in a function that takes a request delegate and returns a request delegate. And a request delegate, by the way, which is a .NET delegate, so it's executable code, it's a function, a request delegate is something that takes an HTTP context and returns a task. So it's typically going to be an async method. So app.Use, I need to pass in a function. I'm going to give this function a parameter that I name next, because this parameter, which is a request delegate, really represents the next piece of middleware in the pipeline. If I choose to invoke next inside of my middleware, then I am choosing to allow the next piece of middleware to process this request. We'll see that in just a moment. And what I need to return is a request delegate. That is a function that takes an HTTP context object and returns a task. So what I'd like to show you is how to conditionally process a request, that is, I only want to handle a request inside of this piece of middleware if we reach a specific path just like the welcome page is doing here; otherwise, I want control to flow through to the next piece of middleware, which is UseWelcomePage. And in order to make this more obvious, let's write out some logging information inside of my middleware. So I'm going to ask ASP.NET to give me, in addition to IApplicationBuilder and IGreeter and so forth, ILogger that is specifically configured for this startup class. So ILogger is another service that you can ask for in almost any ASP.NET component so you can write out logging information. I just need to bring in the namespace, Microsoft.Extensions.Logging. And now what I can do inside of my middleware is first of all I'm going to write some information, log some information, that this is a request incoming. Because at this point in my middleware, I have just received a call from ASP.NET Core telling me about a request. Everything about that request I can get to through this context object. For example, I could say if context.Request.Path, let's say if it starts with a segment /mym for my middleware, just something easy to type in, if we do have a request that starts with /mym, then I want to do something with my middleware. I don't want control to pass to the next piece of middleware, I want to write out a response. And I can do that using the awake keyword, which means I will make this an async lambda. I will await the result of using context.Response, so I'm going to go to the response, and I will write async, just that we hit this middleware. Otherwise, what do I do? Well if I'm not going to handle the request and I'm not going to produce a response, I should give the next piece of middleware in the pipeline a chance to respond. And I can do that by invoking the request delegate that I was given, so invoking next, and that requires me to pass along an HTTP context, so let's pass along the context that we've received, and we can also await the processing of that next piece of middleware. So let's come up here and add some more logging statements. First of all, this is Request incoming, which I'm still trying to spell correctly, let me copy that line. If we reach this point, I could say Request handled; otherwise, down here, I could say Request, or Response perhaps, outgoing. Because there's many different reason that you might write a custom piece of middleware, but most middleware is there to either look at an incoming request, or look at outgoing responses, or trying to handle requests in some manner. And everything here is really about the request incoming. Once you call next, some other piece of middleware might have already started to write a response. And going back to that diagram that we had on an earlier slide, after you call next, this is control flow going back out of the pipeline. I also want to point out, because this might not be obvious, the outer function that I pass into app.Use is only invoked once. ASP.NET Core invokes this function when the framework is ready to set up the pipeline. And this function needs to return the middleware function to ASP.NET Core. So this inner function that I am returning to the caller, this is the middleware. This is the function that is invoked once per HTTP request that reaches this middleware. And let's just see if all of this works. I'm going to save this. I'm also going to view the Output window, and let's go over to ASP.NET Core Web Server. This will actually show us the logging information that's being logged, so I'm going to clear everything that's here and come back to the browser, and let's do a refresh on /wp. This changed my output, so let's go back to ASP.NET Core, and here I can see Request incoming, Request outgoing. So the request for /wp was handled by this piece of middleware, and it just sort of traveled through my middleware. I looked at the request, wasn't interested, invoked next, and the welcome page handled this. But if I come to /mym, my middleware is hit. I should be able to see here in the output that request was incoming and the request was handled, that's good. And although this was a very quick introduction, hopefully this gives you some deeper insight into how this middleware processing pipeline actually works.
Showing Exception Details
Now let's talk about what happens when something goes wrong inside of our application. First of all, I'm going to clean things up a little bit, remove the app.Use middleware that we just programmed, remove the welcome page. And what I'm going to do inside of app.Run is throw a new exception. And we'll just pass along the message error. So this will happen now with every single request that comes to our application, and I just want to show you over in the browser when I do a refresh, I will receive a display that's actually generated by the browser, because all the server did was return an HTTP 500 error. And in some browsers, you might not see anything at all; it just so happens that in Microsoft Edge there's a somewhat friendly UI that's programmed into the browser to tell me that the server couldn't respond. And this is not typically something that I want to show an end user when they're using my application and something goes wrong. What I generally want to do is show them a page, perhaps with an email address or a phone number to contact support, and if I'm a developer, I really want to have more details on what happened back on the server. And that's the purpose of the UseDeveloperExceptionPage, so I'm just going to uncomment the line of code that adds this piece of middleware to the pipeline. We'll come back and talk about the if statement here next. And most pieces of middleware that you install are pieces of middleware that are there to inspect an incoming request. So it wants to inspect that request to do some logging or to examine authentication cookies, or to perhaps produce some HTML like the welcome page did. The developer exception page middleware is different. The developer exception page middleware just wants to sit at the front of the pipeline and allow all requests to flow through to other pieces of middleware. It really doesn't care about the incoming request. But if another piece of middleware later in the pipeline throws an unhandled exception, that's when the developer exception page can jump in. The developer exception page can catch that exception and prevent that scenario where we don't send a body back in the response to the web browser. Instead what the developer exception page will do is give us a user interface that a developer might like. So I can see that I made a request to the web server, there was an exception. I can even see that that exception happened on line 37 of Startup.cs, which is very useful information for debugging. I can see some additional exception details, a full stack trace, I can look at the query string, any cookies that were involved in this request, and any other headers that were sent, which is all very useful information that I can use as a developer to come back and fix the application, which in this case is just getting rid of this hard-coded exception. But of course, that's not the type of user interface display that I want for the average end user. They're not going to be interested in a stack trace. In fact, some stack traces can give a user too much information, information that a malicious user might use against me. So I want to prevent that information from reaching anyone when I'm on a production website. And that's a situation where I can match my middleware to the specific environment that I'm operating in. Let's talk about that next.
Middleware to Match the Environment
ASP.NET Core has the concept of a runtime environment. I can find out information about the environment that my application is running inside of by going to the IHostingEnvironment service. This is one of those services like IGreeter and ILogger that I can inject into components throughout the application, and here we're asking for that service inside of our configure method. With this service, I can find out various pieces of information about the environment that I'm in, like my ApplicationName, as well as the absolute path to some significant folders inside of my application, and I can also ask about the type of environment. Is this a development environment or production environment or a staging environment? Those are three well-known environments for ASP.NET Core. I can also create my own arbitrary names, like QA if I wanted a QA environment. And the obvious question then is how do I set or control this environment? If I look at IHostingEnvironment.EnvironmentName, then this will give me a string value a name like development production staging QA. I can also set the name here, but commonly what you do with ASP.NET Core is you set the environment using an environment variable, and then name of that environment variable is ASPNETCORE_ENVIRONMENT. The default environment name is production, since that is the safest choice. But if you set an environment variable with the name ASPNETCORE_ENVIRONMENT to the value development, and then run the application with that environment variable in place, that is when this method IsDevelopment would return true. Now we've seen this DeveloperExceptionPage at work. We noticed there when we run without debugging from Visual Studio, so the question is, where does this environment variable get set when I run from Visual Studio? And the answer is that inside of the properties for this solution, there is a file, launchSettings.json. Other tools like the .NET CLI tool that we looked at earlier also respect the settings that are in launchSettings.json, so you can use these settings even if you're not using Visual Studio, you can use this with other tools. And inside of this file, you can set up multiple profiles, that is, multiple different ways to execute your application. This first profile here is saying I want to run my application through dotnet.Run, but under IISExpress. And when I select to run under that profile, I want my tools to automatically set up an ASPNETCORE_ENVIRONMENT variable with the value Development. And currently every time we run with Visual Studio, whether it's with the debugger or without the debugger, we are using this IISExpress profile. I know that because in the toolbar, this little dropdown, this is where I can select the profile that I want to use when executing. So, currently it's set to IIS Express. I can also select this profile OdeToFood. We can see those profile settings inside of launchSettings.json also. In this case, when the settings file says commandName Project, this is really saying behind the scenes use dotnet run to start my project, but I still want you to set an ASPNETCORE_ENVIRONMENT variable to the value Development. Let's change that, and let's set this to Production, and I want to show you how to run with this profile, at least when you are inside of Visual Studio, and I also want to change my startup file. So in the startup, in addition to writing out the value of the greeting, let's also write out IHostingEnvironment.EnvironmentName, so that we know we have the write setting. And let me save all the files and select that I want to use the OdeToFood profile, and now let's start without debugging. What we should see is that a command-line window pops up and that a browser opens. I do see Hello. I am running in the Production environment. And if I flip back to the command-line, I can see this is the same output that we would see if we used dotnet.Run. Now one way to add and remove profiles is to come into Visual Studio or Visual Studio Code and modify launchSettings.json. And if you want a friendlier way to do this, you can also right-click on a project when you're inside of Visual Studio, come into the properties, and come to this Debug tab. Here I can see the list of profiles from launchSettings are displayed in a dropdown list inside of Visual Studio. I can create a new one, I can delete a profile, and it's inside of here where I can modify profiles. I want this profile to launch using IIS Express. I want this environment variable. I do want to launch the browser when we run. And this is where I can set the starting port, and this is where I could enable SSL, which we'll do later in the course. For right now, I'm going to switch back to using the IIS Express profile, and just to double-check things let's start without debugging again. This time we want to see the Command Prompt open, and this time I do see Hello!!: Development. So the way that you can use these different environments is to customize your application to the environment. When I'm in development, I want to see an exception page that shows me details about the exception. I want to see stack traces. But my user typically doesn't want to see that, so if I'm in production or any other environment but development, I might use a different piece of middleware that will allow me to show a different UI to the user something like, sorry, something has gone wrong, here's the phone number or the email address to contact support. We'll use that a little bit later in the course. I also want to point out that you can have multiple appsettings files based on the environment name. So by default, the application will use appsettings.json. But let me copy this and paste it into Solution Explorer again, and this time I'm going to rename it, because what ASP.NET Core will also look for in addition to appsettings.json is it will then look for an appsettings.environmentname.json file. So if I create an appsettings.Development.json file, any settings in this file will override the settings that are in appsettings.json. And this would allow you to do things like define different database connection strings for different environments. Let's go into appsettings.Development.json and let's change the greeting. And we could say something like A development Hello!! Save this file, start without debugging, and I now have a settings file that will contain settings specific to a development environment.
Serving Files
I hope by now that you're starting to see how middleware entirely defines how my application is going to behave and respond to HTTP requests. Right now the only thing my application can do is show a friendly developer exception page when there's an unhandled exception, and display a message of the day. I do not even have the ability to display static files from the filesystem. And most applications need that capability. We need to serve up JavaScript files, CSS files, image files, HTML files, and in previous versions of ASP.NET, you could just add those files anywhere inside of your application and ASP.NET would serve those files. With ASP.NET Core, you cannot serve any files from the filesystem until you install a specific piece of middleware that will add that feature to your application. And by default, that middleware is only going to serve files that are in the wwwroot folder or some subfolder of that folder. One reason for that is that it's easy to place all of your static content in that folder and not have to worry about blocking requests to special files like source code files, .cs files, or appsettings.json, which might contain some sensitive information like a connection string. So first, let's create an HTML file inside of the wwwroot folder, so I'm going to right-click there, say that I want to add a new item, select HTML Page, and let's call this index.html. And inside of this file, let's add some simple markups, so perhaps a div, and we can just say This is index.html! Once I save this file and come back out to the application, which is still running, and refresh, even if I tried to go to index.html, the only response I can receive from my application is this greeting that we have configured to respond to every request. However, there is a piece of middleware that I can install inside of Startup.cs, and I can do that using app.Use, and the name of the method that I want is UseStaticFiles. And with that piece of middleware in place, when I come back to the browser and refresh on /index.html, now that piece of middleware has inspected that request, it's gone out to the filesystem, it is found an index.html file, so it's going to pick up the contents and stream them back to the user. A request for any other URL, let's say if I go to /foo or /bar, those requests are going to fall through the static files middleware, because it doesn't find a matching file with that name, and go back to the app.Run middleware, which is just going to display the greeting. What if I wanted index.html to be my default file? That is, if someone requests the root of the website, I want to respond with index.html. If that's the case, there's another piece of middleware, which is UseDefaultFiles. What default files will do is look at an incoming request, and if that request is for a directory, like the root of an application, this piece of middleware will look inside of that directory to see if there is a default file. So the default filenames are configurable, but one of the default default filenames is index.html. Like every other piece of middleware, there's an options object that you can pass in to UseDefaultFiles, and you can specify what default files you want to look for. But since index.html is a default, all I should need to do is save Startup.cs, come back to my browser and refresh, and request to the root of the website will find index.html. And this is another example of where the order in which you place middleware is important, because UseDefaultFiles will do is not serve up the default file, that is, it's not going to read that file from the filesystem and send it back to the user, instead UseDefaultFiles will simply change the request path, and then invoke the next piece of middleware, which is UseStaticFiles, and if UseDefaultFiles changes the request to make it look as if the request was actually going for index.html, then UseStaticFiles can serve up that content. So if I were to swap these two lines of code, the application wouldn't behave as expected. I need to use default files before I use static files, so that default files can properly define the environment for static files to work. And if I want both of these behaviors, there's another extension method that behind the scenes will actually install a couple different pieces of middleware, but this is UseFileServer. What UseFileServer will do behind the scenes is install the default files middleware, as well as the static files middleware, and there's an options parameter I can pass in to enable things like directory browsing, but UseFileServer should also allow me to serve up index.html from the root. And if I go to another path like /foo, I'm back to my greeting. Because static file sees that that does not match a file that's on the disk, and then it allows the request to flow through to the next piece of middleware, which is my app.Run middleware. So with this point, we are handling exceptions, we are serving up static files, and what we want to look at in the next module is the MVC Framework. We want to start looking at controllers specifically in the MVC Framework. And in order for the MVC Framework to work, we're going to need to install some middleware and also some services that the MVC Framework will require. We'll get set up with those pieces in the next clip.
Setting up ASP.NET MVC Middleware
Ultimately we want to build a web application on top of ASP.NET Core, and specifically we want to use the ASP.NET MVC Framework, although we could build an entire application just using middleware. ASP.NET MVC, the model view controller framework, that framework gives us some additional features that makes it easy to create HTML pages and HTTP-based APIs. The MVC Framework makes this easy, because we can map an incoming request to a method on a class, and inside of that method it becomes easy to query a database or read a file. We can execute all sorts of logic and do calculations and then respond to the user by building HTML, XML, or JSON. We'll see how all that works in the rest of the course. For now, we just want to see how to get set up to use the MVC Framework. Step one is to always make sure that we have the Microsoft.ASPNETCORE.MVC package installed. That gives us access to the assemblies and classes provided by the MVC Framework. Once the package is installed, we need to register all these services that ASP.NET MVC requires at runtime. We'll do this inside of the ConfigureServices method, where we also configured our IGreeter service since the application. And finally, we need to add middleware for ASP.NET MVC to receive requests. Essentially the middleware takes an HTTP request that comes in and tries to direct that request to a C# class that will handle that request. I'll show you a bit about what that class can look like in this module, but we'll go into much more detail in the next module. For right now, we just want to have all the setup in place that we can move forward. As I mentioned, the first step in working with the MVC Framework is to make sure that we have a specific NuGet package installed. Now when I create a project using the empty template with Visual Studio and dotnet.Run, I just want to show you that we have one package reference for the entire application. That's the Microsoft.AspNetCore.All package. This is what we call a meta package. It's a package that brings in other NuGet packages, and as the name implies, this brings in all the ASP.NET Core packages. And that includes the MVC Framework. I can verify this if I go to Dependencies. What Dependencies can show you is all of your primary dependencies, for example, I depend on Microsoft.AspNetCore.All, as well as dependencies of your dependencies. So if I expand this node, I can see all of these subpackages that are brought in, and that includes Microsoft.AspNetCore.MVC. In fact, there's a number of MVC-related packages that the all package brings in. So we don't need to explicitly add a NuGet reference if I'm using Microsoft.AspNetCore.All. But I do need to add the services and I do need to add the middleware. First, let's change this back to using just a static files middleware. That's because I only want to respond with a static file if an incoming request exactly matches that static file. Otherwise, I want the request to pass through to the next piece of middleware, which will be using the MVC Framework with default route. I promise to explain more about routing in the next module. But let me just say now that the MVC Framework has some specific inventions that when it sees an incoming URL, it looks at specific pieces of that URL to map that request to a method on a class that the MVC Framework will instantiate. And I'll give you a preview of what that class looks like, but for right now let me save this file, and I want do a refresh, just to show you that I missed a step. I see an exception telling me that the MVC Framework was unable to find the required services. So, in additional to any custom services that I might want to register for my application, like the IGreeting service, I might also need to add more framework services that just aren't there by default, and the MVC Framework is a good example. The MVC Framework requires a number of services that it will use to do things like turn a view into HTML. We'll see how that works later in the course. But since those services are not there by default, I need to explicitly add them inside of ConfigureServices. And of course, there's going to be overloads of this method that I can use to configure some specific behavior of those services. We'll see that later in the course, but now that I have a NuGet package installed, I have the services registered, I have middleware in place that will route my request through the MVC Framework, it's now time for me to create a controller, because ultimately with the MVC Framework, you want to direct an incoming request to a controller. And actually throughout this course, we'll see a number of different ways to handle requests with the MVC Framework, but the C in MVC stands for controller, and what I want to do is go ahead and create a new folder in my application. Let's call this Controllers. This will be where I place the controllers for the application. And inside of that folder, I'm going to add a new item. And although you will see MVC Controller Class here in the template, I'm going to go with something a little more general, which is, I'm just going to add a C# class. I will call this class HomeController. That name is significant, because again, the MVC Framework has some very specific conventions about how it will map an incoming request to a method on a class. And by default, it is the HomeController that will receive a request to the root of the application. So if I request the root of the application, the MVC Framework should see that request, it will instantiate this class, and then it will invoke a method or it will first look to see if this method exists, but if it finds an Index method, the MVC Framework will invoke this method, and whatever I return will influence what the user sees in the browser. For right now, I'm just going to return a simple string, which is Hello from the HomeController. And this class doesn't need to derive from any specific base class, we will do that later in the course, because there is a base class that we can use that gives us a lot more features. But right now I just want to save all the files in the application. Come back to my web browser, and let's issue a request and make sure this goes to the root of the website, and I'll see Hello from the HomeController. At this point, we are ready to start working with the MVC Framework, and we'll do that in the next module by looking at routing and how to write these controller classes.
Summary
In this module, we learned more about ASP.NET Core middleware, and how to set up our middleware in the Configure method of a Startup class. We now have a processing pipeline set up where a request to the root of the website will travel through several pieces of middleware, including the middleware that we use to serve up static files, and finally, reach the MVC middleware, which can then forward the request to a controller class. Right now we have a simple controller that displays a Hello message, but in the next module, we will learn more about controllers and see how request can reach different controllers, and how they can produce HTML and JSON from a controller.
Controllers in the MVC Framework
Introduction
Hi, this is Scott, and in this module we will start learning the ASP.NET Core MVC Framework. The MVC Framework gets its name from a popular design pattern, which we will also learn about, and then we'll go into the mechanics of how to use controllers to process HTTP requests. We'll see that controllers can produce text, serialize objects into JavaScript, and render HTML to the client.
The Model View Controller Design Pattern
The MVC Framework is named for the MVC design pattern, where the M stands for model, the V stands for view, and C is for controller. The MVC design pattern is a popular design pattern for the user interface layer of a software application, and in larger applications you typically combine a model view controller UI layer with other design patterns in an application, like data access patterns and messaging patterns, to build the full application stack. In MVC, it is the controller that receives the initial HTTP request. We'll see how to map an HTTP request to a method on a controller class, but once I'm inside of that controller method, it is the controller's job to figure out how to put together the information that will respond to that HTTP request. Perhaps the user is pointing their browser to the /restaurants URL of the application and sending an HTTP GET request. So the user is requesting a list of restaurants, and it's the controller that needs to put together the information to display this list of movies. To do that, the controller can build a model. The model doesn't know anything about the HTTP request or the controller. The model is responsible for holding the information that the user wants to see. So in this case, a list of restaurants, and the model is just another C# class that we can use, perhaps more than one class if it's a complex model. At this point, if you're building an API layer, the Controller method can simply return the model, and that model will get serialized into XML or JSON or any other format that you need to come up with for your API layer. But if you need to render HTML and build an HTML page, the controller can now select a view that will render that model. The view takes the information in the model and it uses it to construct an HTML page, and the HTML is sent back to the client in the HTTP response. Thus, we've completed an entire HTTP request and response transaction. And these are the very basics of the MVC design pattern. We'll be working in each one of these steps in this module, and the overall idea here is to keep a separation of concerns, so the controller is responsible for taking a request and building a model. It's the model that carries the information and logic that we need into the view, and the view is only responsible for transforming the model into HTML. Three components, each with their own focus on a specific part of the job. Now in order for all this to work, we need to find a way to send HTTP request to the right controller, and in ASP.NET MVC, this process is known as routing. Let's look at how to route requests to different controllers.
Routing
The ASP.NET middleware we installed in the last module needs a way to determine if a given HTTP request should go to a controller for processing or not. That MVC middleware will make this decision based on the URL and some routing configuration information that we provide. Now one way to define this routing configuration is to define the route for our controllers inside of Startup.cs when we add the MVC middleware. This approach is often referred to as convention-based routing. And what we do is we define templates that tell the MVC Framework how to look at a URL and find a controller name and an action name. Remember the ultimate goal is to take an HTTP request and use information in that request to invoke an action on a controller, and a controller is just a C# class and an action is a public method that's on that class. Another approach to routing is something we call attribute-based routing. With attribute-based routing, we use C# attributes on the controller classes and methods themselves to let ASP.NET MVC know when to call a specific method. Let's look at these two different approaches to routing over the next two clips.
Conventional Routes
In the previous module, we created a simple HomeController with an Index method, so that is an action, and because we have the MVC middleware added to the application, I am able to reach this by going to the root of the website, simply localhost:52311. What I want to show you is what is really happening behind the scenes, so instead of using MVC with the default route, let's simply use MVC, which means there are no routes configured, and the MVC Framework won't really know what to do with the request, it won't know how to map that request to a specific controller. And to prove that, let me just change some of the text inside of here. Instead of displaying a greeting, let's display the text Not found. And if I were to refresh the application now instead of reaching the HomeController, I see the text Not found, and you'll notice there's no space here. Let me give you an advanced tip. When you are writing directly into the response, sometimes you have to be a little bit careful about the HTTP headers that you send back. So currently what I'm doing is just writing into the response a simple string, and that's all the server will send back to my web browser, and my web browser is trying to interpret what content it is seeing, and it's not getting any help from any HTTP headers about how to interpret this content, so it has guessed wrong. Sometimes when you're working with low-level middleware like this, you'll need to make sure that you set HTTP headers like the status code, and in this case, it is the ContentType, which will tell the web browser exactly how to interpret the response. I want to use what's known as a mime type and tell the browser that the content type it is seeing is text/plain. If you search the web for mime types, you can see the various content type identifiers that you can use here, but a simple refresh of the browser, and I now see the proper text Not found. But back to the problem of trying to get to the HomeController. I'm going to use an overload of UseMvc that takes an action of IRouteBuilder. So I need to give UseMvc a method that it can invoke and pass along a route builder and give me a chance to configure the routes. In this case, instead of using a lambda expression, let's just generate a named method with Visual Studio. The name of the method will be ConfigureRoutes. I'll use Ctrl+period in Visual Studio to generate that method for me, and I can see I do get a method that takes an IRouteBuilder parameter, and let's give that a proper name like routeBuilder. So inside of here using the routeBuilder object, I can define one or more routes for my MVC application. These are all done with some form of map method. The method that I want is MapRoute. With MapRoute, I can define a friendly name for my route, and then a template. And it is the template that the MVC Framework will use to pick apart a URL and try to find information that will allow the MVC Framework to figure out what controller it should instantiate and what method then to invoke on that controller object. So let's say that I want to map an incoming request like /Home/Index to the HomeController's index action. In that case, I need to tell the MVC Framework that it can find the controller name in the first part of the URL, and the action name in the second part of the URL. And by the way, I don't explicitly have to tell the MVC Framework that the name of the controller ends with the controller; the MVC Framework will just add the name controller to the end of this name. So the URL like /Home/Index should go to the HomeController. To do that, I will specify parameters for this particular route. I specify parameters inside of curly braces. The name of this parameter is controller. And this tells the MVC Framework that the first part of the URL will contain the name of the controller. And then the second segment of the URL will contain the action name. So given a URL like /Home/Index, the MVC Framework will say, ah, the controller name is Home, let me go look for a class named HomeController, and then the action name is Index. Let me find an Index method on that controller. I can also have some literal text in here. So let's say that I wanted only request for admin/Home/Index to go to that HomeController. Let my template would look like admin/controller name /action. And again, I can define multiple routes with the routeBuilder. For this course, we'll just be using this single conventional route. And I can also specify other arbitrary parameters. For example, let's say that I'm creating a controller that has to look up a record in the database. In that case, the controller's going to need some sort of identifier that it can retrieve and use that identifier to query the database. So I could say inside of the route template if you see another slash and some additional information like the number 4, treat that as an id parameter. And we'll see later in the course how a controller action can receive this parameter and then use that value to query the database or do some other work. Now my HomeContorller doesn't need a parameter, and right now with this syntax, the MVC Framework would have to see a parameter for this route to be executed, but if I add a question mark here, that will tell the MVC Framework, ah, this particular part of the URL, this third segment, it's optional. So just seeing /Home/Index will be enough information. So with that template in place, let me save the file and come back and refresh the root of the website. We're still going to see Not found, I'll explain why in just a second. But if I go to my server name /home/index, I will reach the HomeController. So the MVC Framework applied this template to the incoming URL and said this is the controller name, this is the action name. But then why does a request to the root of the website not reach the HomeController? That's because this template is not exactly like the template that UseMvc with default route would apply. UseMvc with default route applies some defaults to these parameters. So, using the equal sign, I can say here's where you will find the action, but if this part of the URL does not exist, just assume a default name of Index. And the controller is also option in the sense that I will provide a default. If you do not see /something, then just treat the controller name as the HomeController. And with that default conventional route in place, I should now be able to reach the HomeController's index action by going to the root of the website, as well as going to just /home, because the MVC Framework will know that the default action is index and I can also reach it by fully specifying /home/index. All three of those URLs go to the same controller and invoke the same action. And now I have quite a bit of flexibility in my MVC application, because I can reach any controller and any action using this simple template. So for example, I could go into the Controllers folder and let's add another controller. Let's call this the AboutController. This would represent about information for the website. So I could give this controller an action, let's call it Phone, and return a phone number to contact the owners of the website. And let's just add in a fake phone number. And maybe another action, let's call it Address, that will return some address information. Let me save that file, and now how do I reach the AboutController phone action? I simply go to controller name, which is about/action name, which is phone, and I'll see the phone number displayed. I can also go to /about/address, and the second action is invoked. So I can now have dozens or hundreds of controllers inside of my application and be able to reach them from a browser and navigate between those actions just by having a properly structured URL /controller name /action. This is what some people would call the conventional route, or the default route, but let's look at another way to configure routes, one that I can use alongside this conventional templated-based routing, and that is attribute-based routing. We'll look at that next.
Attribute Routes
Another way to define routes in an MVC application is to use a Route attribute. So this Route attribute is in the namespace Microsoft.AspNetCore.Mvc. I'll use Ctrl+period to bring in the namespace, and let me go ahead and use Ctrl+period to remove namespaces that I'm not using. Now this Route attribute I can apply it at the controller level. I can also apply it at an action level. And what I'm doing is specifying what needs to be in the URL to reach this particular controller or action. So let's say that I want a request for just /about to reach the Phone action. Essentially I want the Phone action to be the default action for this entire controller. And that is the type of routing configuration that doesn't work very well with my default route, because my default route says that the default action should have the name Index. Fortunately, I can combine these attribute routes with the route builder templates, and I can make this work. So the first thing I will do is say that this route for this controller should start with about. So I want to see /about in the first part of the URL. And then when I apply a Route attribute inside of here by default, it's hierarchical, so this Route attribute would describe the second segment of the URL. In this case, I'm just going to leave this as an empty string, and that's a way of saying if you just see /about, then come to this controller and invoke this action. But before I go any farther, let me save the changes here and let's go to just /about, and I want to show you an exception that occurs, because the MVC Framework has been able to figure out that it needs to go to the AboutController, because of /about. But now inside of this controller, it sees there's two public methods, and the MVC Framework is not sure which one should execute. So it throws an exception. So in order for this to work, I either need to be more explicit in my URL, or more explicit with my routing configuration. So if I say that this particular action can only respond when the URL is /about/address, now that MVC Framework should be able to differentiate between those two, and a request to /about will invoke my phone action and return a phone number. Attribute routes also know about special tokens that can make this type of configuration a little bit easier. One type of token is the token controller. So, tokens always appear inside of square brackets, and when I use the controller token, that tells the MVC Framework that what appears here in the URL should be the name of this controller, whatever class that I'm attached to. And this approach can be a little more robust, because now the MVC Framework will know if I rename this class, let's call it the foo controller, that the route should start with foo. There's also an action token. So I can say inside of square brackets here action, and the MVC Framework will know just to use the name of this method in the URL. And again, these are hierarchical, so when I have a route inside of a route, it will always be this bit of information /this other bit of information. And then also instead of specifying action everywhere throughout this controller, if it has a few actions on it, there's a better syntax for that where I can say Route controller / and then an action name, so I always need to see /about/ and then phone or address. And yes, you can include parameters here. You can also include literal text. So let's say I only want to respond to company/about/phone. So I will add the literal text company to my Route attribute, and now I would need to go to /company/about/phone to be able to see the phone number. So again, attribute-based routing is great for those situations where you want to create a route that is specific to a controller or an action inside of controller. Maybe it needs special conventions, maybe it needs additional parameters that aren't specified inside of the routes inside of Startup.cs, and then some people will avoid the template routes altogether and say that they want to keep all the routing configuration right next to their controllers. So they will only use attribute-based routes, but you can combine them both inside of an application if you want. So now that we know a little bit more about how to route a request to a controller and to an action inside of the controller, let's start taking a deeper look at controllers themselves and see what functionality they can offer.
Action Results
So far in our project we've been using plain C# classes as controllers. These classes don't derive from a base class, and although you can use this approach with MVC, it's more common to derive a controller from an MVC controller base class. This base class gives me access to lots of contextual information about a request, as well as methods that will help me build results to send back to the client. I can send back simple types like strings and integers, but I can also send back complex objects, like an object to represent a restaurant and all the data associated with the restaurant. We typically encapsulate these results into an object that implements the IActionResult interface. And there's many different result types that implement this interface, result types that may contain models, or the contents of a file, or a view that will build HTML. Let's take a look in the project. Inside of the Index action of our HomeController, let's use the this keyword just to see what's available as an API, and I can see there is not much here. These are mostly just the members that I inherit from my base class, which is currently system.object. That's the implicit base class for everything that's programmed in .NET. But I can derive from this controller class that is available in the Microsoft.AspNetCore.Mvc namespace, and now if I come back and look at the members that are available, I can see I now have a wide variety of methods and properties that are available. Many of these properties are contextual; that is, if I want to know more about the controller context, which includes information about this controller, and even the action descriptor, I can find out things like what is the name of the action that I am inside of, what is the name of the controller. There is also an HttpContext object. This is the same HttpContext object that is available inside of a middleware request delegate. I can manipulate the response object, I can also look at the request, I could look at the headers of the request if I needed to. But I will warn you that you typically want to avoid using HttpContext inside of an MVC controller. That's not always possible, but generally speaking, there's usually an easier way to get to things that are in the request or to manipulate the response. And taking the easier path, which is usually through some feature that the MVC Framework provides, results in code that's a little bit cleaner and also a little bit easier to unit test. We will see some examples as we move through the course. And in addition to those contextual properties, there's also a number of methods that are available. The majority of these methods are used to produce something that implements IActionResult. For example, here's a method, BadRequest, which I could return this from the method, and this will allow me to build up an object that implements IActionResult that sends back an HTTP status code of 400 to the client. So, a 400 indicates an error. I'm telling the client they've made a bad request, perhaps they passed me some invalid data. There are also methods to send back a file. So I can send back a file by providing a byte array or a filestream or by providing the path to a file on the drive. There's also a helper method here that will allow me to send back content in a more structured manner. And that's the Content method. So all of these methods that I'm showing you, BadResult, File, Content, these are all methods that will construct an object that implements IActionResult. And if I use this return statement here, I will need to change the return type of my method, this is no longer returning a string. Technically it is returning a ContentResult object, and anytime you're implementing a controller action that returns a very specific type of result, you can always strongly type this return type to the exact type that you are returning. Many times, and we'll see this in this course, you might need to return different action results, depending on data that the user has provided, and if that's the case, you can always set the return type to IActionResult, and all of these result helpers, Content, File, BadRequest, they all produce objects that implement this IActionResult. So if I save this file and refresh the browser at the root of the website, which will invoke this HomeController, the client's not going to see any difference. I'm still responding with a plain text result Hello from the HomeController, but now I am doing this through an action result. And action results are interesting, because this is an important concept in the MVC Framework, particularly if you want to unit test these controllers. An action result like Content doesn't immediately write into the response. It doesn't start sending anything back to the client right away. Instead we return this data structure, this IActionResult, that tells the MVC Framework what to do next. And so somewhere later in the processing pipeline, the MVC Framework will say, well, I called this controller action, it produced an action result, now it's time to execute that action result. And the core concept here is the concept of separate deciding what to do from actually doing that thing. So what I want to do is return some content Hello from the HomeController, and all my controller action needs to do is decide that that is what I want to do, return that content. And it's later in the processing pipeline where the MVC Framework actually takes this object, this Content result, and says, okay, let's turn that into some text that we will send back down over the network. Put this into an HTTP response message and send it back over the wire. And the reason this concept is important, this separation of deciding what to do versus actually doing that thing, is that it adds some additional flexibility. One example is that I can easily test this HomeController and invoke methods and inspect the data structures that have returned without setting up a web server or having any sort of network communication happen. There's also additional extensibility points in the MVC Framework where you can plug in your own code that inspects every action result that comes back from any controller action, and you might want to do that to log some information or perhaps augment the information that's being sent back in a result, or even change the result in specific circumstances. And that's really why we had this concept of an action result. This is what I am deciding to do. I'm deciding to render some content. And to give you a specific example of that flexibility, let's introduce a model into our project. Remember, models are those components that will hold information that I will use to produce a response to the user. So let me add a new folder to the project called Models, which isn't required, and by the way, controllers do not have to be in a folder named Controllers, models do not have to be in a folder named Models, I'm simply using this to make things easy. You can even include models in a different class library project if you want to. But let's add a class that we can use to represent a Restaurant. And for every restaurant, I'm just going to start with a simple couple properties. Let's give every restaurant an Id, an identifier, and we're not going to hook this up to a database just yet, but later in the course that Id property will be the primary key that we can use to locate a specific restaurant. And let's give every restaurant a name. And what I want to do in the HomeController is not just return simple Content, I want to build a model and display that model information somehow. So my model for right now until we get a database hooked up, I'm simply going to instantiate a new instance of restaurant, which requires me to bring in OdeToFood.Models, and we'll just hard-code some values here. So Id = 1, Name = let's say Scott's Pizza Place. And what I want to do is send this model information back to the client. Let me introduce you to a new type of result, which is the ObjectResult. I can actually use the new constructor to new this up and give it my model, and an ObjectResult is another one of those results that implements IActionResult. Now notice this name is not very descriptive. This is an ObjectResult. What does that mean? Am I going to produce HTML, am I going to produce XML or JSON? Well, in this case, it's not really up to this controller to decide that. What the controller is saying is the information you want is this Restaurant information. I'm going to place that information into an ObjectResult, and something else later in the pipeline will figure out what to do with that result. Let's go ahead and try this in the browser, and what we will see is that I receive JSON in the response. And this is, if you're building APIs, what your controllers would look like. You'll have a request arrive at the action, typically with some parameters that you can use to retrieve information from another web service or from a database. You'll use that information to build a model, and then you will return an ObjectResult. So the controller's decision is to return an ObjectResult. Later in the processing pipeline, the MVC Framework will say now I have this ObjectResult, I need to send that result back to an HTTP response, what do I do? And it goes up to the ObjectResult and says please write yourself into the response. What the ObjectResult will do is determine if it should take this model and serialize that model as JSON, or XML, or some other format that you've plugged into the MVC Framework, so it could be any custom format, CSV format, PDF format, images, whatever your application needs. And so those of you that are familiar with the term RESTful APIs or HTTP-based APIs, what ObjectResult is doing behind the scenes is content negotiation. It is looking at the except types header that the client sends in the HTTP request, and trying to figure out if the client will accept a JSON result. And in this scenario I'm demonstrating that is exactly what happens. ObjectResult takes my model, uses a JSON serializer to serialize that object into JavaScript object notation, and we send that back in an HTTP response. But what if I wanted to take this restaurant and instead of building an API endpoint to retrieve restaurant information, I wanted to build a webpage using HTML to display that restaurant information, that's what we will look at next.
Rendering Views
The most popular method for creating HTML is to use the Razor view engine of ASP.NET MVC. To use the Razor view engine, a controller action decides to produce a ViewResult object, and a ViewResult can carry the name of the Razor view that we want to render, and a view, by the way, will be a file on the filesystem by default, a file with a .cshtml extension for C#, and this ViewResult object can optionally carry along a model object for the view to use when it creates HTML. When the MVC Framework sees that your controller has decided to produce a ViewResult, the framework will go out and find the view on the filesystem and then execute that view, which will produce HTML. And it is this HTML that the framework will send back to the client. Let's see how this works. Inside the Index action, instead of returning a new ObjectResult, I want to call one of the built-in helper methods, which is the View method. The View method will return a ViewResult, and that tells the MVC Framework go out and find a view and render that view. What I want to do is save this controller and just show you what happens if I refresh the application with no other changes, and what happens is the framework returned with an exception, an error. But this error is very informative. The error tells me that the MVC Framework is looking for a view named Index. Where did it get the name Index? Well, if I return a ViewResult and I don't specify a specific name, the framework will assume I want to find the view that has the same name as the action, which returned that ViewResult. So the action name is Index, therefore, the view name we're looking for is Index. There's also an overload here, so if I wanted to specify a different view name, let's say the home view, home.cshtml, now the MVC Framework is looking for something called Home. It also looks for this in a very specific location by default. This Home.cshtml view has to live either in the Views/Home folder of my project, or in Views/Shared. So when you are creating a view that's only going to be used by a single controller, you would place that in a folder /Views/name of the controller. So, the name of the controller is HomeController, therefore, I could create this view in the Home folder. There's also a Shared folder. We'll look at this later in the course. If I'm creating a view that's going to be used by multiple controllers, I can place that view into the Shared folder to make it easy to find from different controllers. But what I want to do is just return a simple ViewResult using the default name, which is Index, and I want that to render HTML. So I'm going to need to go into my project and right-click and say that I want to add a new folder. That new folder will contain the views for my project, and by the way, models and controllers can live anywhere, but Views by default do have to be inside of this Views folder, and inside of here, I will want a folder called Home. So all of the views that the HomeController will use live in this folder, and now let's actually create a view file. So I want to add a new item, and in this dialog that appears, let's look for MVC View Page. The default name that Visual Studio selected, Index.cshtml, that is good, so let's go ahead and add this. Let me to start with delete everything that is in here at first, and then use an html snippet. So all I did here was type html and hit the Tab key, because html is a code snippet that you can use inside of Visual Studio to generate the basic structure for an html page. And now that I have a head and a body, let's go ahead and put in an h1 message. Let's just say Hello. And we can add a div that says This is the index view. Then we save everything and come back and refresh, and hopefully we will now be producing html, which works. So my HomeController basically said render the default view for the Index action of the HomeController. The MVC Framework went out and found this .cshtml file, that's the extension for Razor views, found that file, rendered what was inside, produced an HTML response, and that's what we see in the browser. And what I really want to do here is I want to take this restaurant information and use that to build my HTML. In order to do that, I can pass a model object along into the ViewResult, and that ViewResult will take my model object and deliver it to the views so that I can use it there. So inside of the view, there is a special property that I have access to here using the @ sign. And that is the Model property, Model with an uppercase M. So anytime I use an @ sign, I can start writing C# code. I can write a block of code using curly braces, we'll see that later, or I can just say @ and then some expression. And so if I use this keyword again and look at IntelliSense, these are all methods and properties that I inherit from a base class that's provided by the Razor view engine. And one of the properties that I have access to is the Model property, and that Model property will represent the model object that was passed in to the ViewResult. And since I passed a restaurant to my ViewResult and a restaurant has a name, I should be able to write the expression @Model.Name, which will produce a string with the restaurant name, and that restaurant name should now appear in the HTML output, which it does. Now you'll notice when I said @Model. there's no IntelliSense there to tell me that I have an Id property and a Name property. And that's because here in Visual Studio there's no information available to tell Razor or Visual Studio that my Model object will be a restaurant. So this Model property by default is typed as dynamic, which means I could write any expression here, and the compiler is going to allow that to go through, but at runtime I might not have a foo property. I want to be able to type Model. and actually know that I'm working with a restaurant. So one way to do that is to add a directive to this Razor view, and that's the model directive. So this is potentially confusing, but @model with a lowercase m, that is a directive. Think of a directive as information that the Razor view engine uses to construct the code behind the scenes. So in a way I'm influencing the code generation for this Razor view. What I can do with this model directive is tell the Razor view engine the exact type of a model object that should be passed into this view. So I can say that this should be OdeToFood.Models.Restaurant. That is the type of model that you should receive. And now when I come to my uppercase Model property, this property you'll notice is now typed as OdeToFood.Models.Restaurant, and I should be able to say Model. and see that my model object, yes, it does have a name property and it has an Id property. So I could say down here something like the ID value is @Model.Id. So the whole idea behind Razor, which we'll look at again in more detail later in the course, is that it makes it easy to intersperse HTML, like body and h1 and div, with these little C# expressions that allow me to pick off pieces of the model and display them at the appropriate place in the HTML. So when my users come to the homepage, I know they want to see information about restaurants. My HomeController will build a model with the restaurant information that we need to display to the user. And if I want to render that model as HTML, I'll use a View result to describe what Razor view to render and pass along that model object, and then it's up to Razor to accept that model object and mix up HTML with C# expressions to produce the appropriate display for my user. And what I've just described is the essence of the model view controller design pattern. The controller receives the request and builds a model. The model can carry information along to the view. The controller is only responsible for receiving that request and building the model. The view is only responsible for taking the model and producing HTML. And in the end, we have a nice separation of concerns so that our data and our markup and our logic to retrieve data is not all mixed into the same location.
A Table Full of Restaurants
Let's build on the example that we have. And instead of displaying a single restaurant, I want to display multiple restaurants, because by the end of this course we will have a Microsoft SQL server attached to the application. The SQL server database will contain multiple restaurants, and when the user comes to the homepage of the website, I want to display all the restaurants that are in the database. And in order to do this, I'm going to go ahead and set things up so that it will be easy to do this when we switch over to SQL Server. And what I want the HomeController to do is to talk to a service that I'm about to define and tell that service go out and tell me about all the restaurants that you have. So let me create a new folder in the project, let's call this Services, and this can be a folder where I define all the services that my application needs, including I could take IGreeter and put it into the Services folder, and once I do that, I will want to open up IGreeter and make sure the namespaces is consistent with the folder that I am inside of, because in .NET and C# Projects, the convention is to use the same namespace as your project structure. So IGreeter is in the folder OdeToFood/Services, so I will make the namespace OdeToFood.Services. That means we'll need to fix up some using statements in other parts of the application inside of Startup.cs, but we'll do that later. For right now, I want to add another class, actually an interface, and let's call this IRestaurantData.cs. Because just like with the greeter, I want my controller to talk to a service through an interface definition. That makes it easy for me to swap out the actual implementation of the service at test time or at runtime. And all this interface needs to do is to define all the operations that I need to retrieve and update and delete restaurants. And the only thing I want to do right now is retrieve all the restaurants. So let me add a method to IRestaurantData called GetAll, and what this can do is return an IEnumerable of Restaurant. And I will need to bring in the namespace OdeToFood.Models. And now with my interface definition in place, let me add an implementation for this using a class, and we will call this InMemoryRestaurantData. Because later in the course, we will be hooking up to a SQL Server database. We'll see how to use the entity from from ASP.NET Core, but for right now to keep things simple, I'm just going to hold a list of restaurants in memory, and we'll just display some restaurants that we have hard-coded inside of this class. So let's create a private field, a list of restaurant, and again, bring in the namespace. We'll call this _restaurant. And to initialize that list, I will add a constructor, so use the ctor code snippet, press Tab, and I want restaurants to be a new list of restaurant. And let's just come up with some restaurant names. So a new restaurant with an Id of 1, let's give this the name Scott's Pizza Place. Let's create another restaurant with an Id of 2, a name of Tersiguels. And 1 more restaurant really quickly, Id of 3, name equals let's use King's Contrivance. And now I just need in-memory restaurant data to implement the interface IRestaurantData, which will require me to have a method GetAll. I used Visual Studio to generate that method with Ctrl+period. I am going to move the field declaration down to the bottom of the class definition, that would be where I like to see private fields declared. But the implementation of GetAll is very simple. All I need to do is return this collection of restaurants that I have. I could even throw in some link statements to do a query and say given a restaurant I want to order by, the name of the restaurant, that will be ascending order by default. And I do want to caution you that you have to be very careful when you're using an abstraction like this that contains a list, because a list is not thread-safe, and if I were to use InMemoryRestaurantData, a single instance across multiple requests, I can run into some threading issues. But I'm not going to address those issues in this class, because I'm going to assume that we only use InMemoryRestaurantData for testing or development. And later when we build an implementation of IRestaurantData that does talk to SQL Server, we won't have to worry about holding an in-memory list, and we won't have those threading issues. Now how do I use InMemoryRestaurantData or IRestaurantData from my controller? And the answer is just like we saw in the Configure method earlier, I typically just want to rely on ASP.NET Core to inject services into the components where I need them. So in the Configure method, we're injecting ILogger and the IGreeter, and since IGreeter was a custom service, we had to register IGreeter here inside of ConfigureServices. I'm going to do the same thing with restaurant data. First I need to bring in the namespace OdeToFood.Services. That will allow us to access IGreeter and IRestaurantData. And now instead of using AddSingleton, I'm going to use AddScoped and say whenever someone needs an instance of IRestaurantData, for right now let's give them InMemoryRestaurantData. So you might remember AddSingleton as a way of telling ASP.NET Core if any component across the entire application needs an IGreeter, give them an instance of greeter and use the same instance throughout the application everywhere for every request. Scoped is just a way of telling ASP.NET Core any component that needs IRestaurantData create an instance for each HTTP request and reused that instance throughout that one request. After that, you can throw it away and create another instance for the next request. That is an HTTP Scoped lifetime for my service, and that's typically what you would want for a data access component. But now with my service defined and my service is now registered with the service collection in ASP.NET Core, let's go to the HomeController, and one way to get access to the service is to write a constructor and to say I need an IRestaurantData service. Let's call that restaurantData. All I need to do here is bring in the namespace, OdeToFood.Services, and when the framework knows that a request is going to the HomeController and it needs to create an instance of this controller for that request, and by the way, that controller instance is only used for one single request, then the framework is also smart enough to look at this constructor, see that this constructor is requiring a service, and the framework will go to a service provider and say, do we know what an IRestaurantData is, can I have also an instance of that? Which, since we registered this properly, shouldn't be a problem. So let me take this incoming restaurantData object and save it into a new private field that I'm about to create with Ctrl+period, _restaurantData, and now inside the Index method, instead of me hard-coding the model here and building something, I'm going to go to restaurantData and tell it to give me all the restaurants. What's interesting about this approach, what we'll see later in the course is that when we do switch over to using a real database instead of an in-memory collection, I will not need to change any code inside of this controller. All it needs to know is that it is getting something, some object that implements IRestaurantData and that will deliver IRestaurantData. Now before I go any farther, let me save all the files and come back to the browser and refresh, just to show you that the Razor view engine is going to complain and tell me that what we passed into the view is an OrderedEnumerable of restaurant, that is, a collection of restaurants. But what the view requires is a model type of a single restaurant. And that is because, you might remember, of the @model directive. I'm telling the framework I need a single restaurant, but I pass in a collection of restaurants, therefore, I see a runtime error. That's easy enough to fix. I just need to say, actually, what I will take is any IEnumerable of restaurant. So that could be a list, an ordered enumerable, an iqueryable, there's dozens of interfaces in .NET that implement IEnumerable. So at this point, I can pretty much work with any collection. And now instead of displaying a single restaurant, let me use a code snippet for table and press Tab. What I want to do is have one table row for each restaurant, and in Razor, I can use a foreach C# statement. So again, the @ sign to switch over into C# mode, and now I could say foreach restaurant that is in the Model, so uppercase M, to reach the Model property, I want to execute the following code. And the following code will actually be a mix of C# expressions and HTML markups. So what the Razor engine is going to do is say, oh, I see you're writing a loop, a foreach loop, around this block of HTML, and what Razor will do is stamp out a copy of this tr tag once, or each restaurant in the model. And Razor is also smart enough to switch between C# mode, which is my foreach statement and an opening curly brace, and switch back into literal HTML mode when it sees the opening tr tag, and then I can switch back again into C# mode. So, we will be looking at Razor in more detail later in the course, but in addition to expressions like restaurant.Id, which will output something into my HTML, I can also have C# code blocks, code blocks like foreach statements, for statements, if statements, and with any sort of looping construct like this, I'll be creating multiple copies of this HTML snippet. So I want to write out the restaurant Id, and then inside of another table cell, let's write out the restaurant.Name. And with those changes in place, I should be able to save index.cshtml, refresh this page, and although the user interface is not that pretty, I now have all the restaurants that I know about displaying on this page and sorted alphabetically.
Summary
In this module, we learned about the model view controller design pattern, and began to see how the different components can work with each other to process and HTTP request. The controller receives some request, and then uses perhaps some services that are defined in the application to build a model. If you're building an API, you might return that model directly from the controller action. The model can be serialized into JSON or XML or a number of different formats. You can do with the ObjectResult class that we saw. Or you might have the controller select a view to render that model, and the view can produce the HTML that we send back in the response. Now that we know a little bit more about controllers in the MVC design pattern, let's also see what we can do with models in the MVC design pattern and take a look at how to create and edit restaurant information.
Models in the MVC Framework
Introduction
Hi, this is Scott Allen, and in this module, we're going to take a closer look at models in the MVC Framework, and working with data in general. It turns out there's actually a couple different types of models that we can use in an ASP.NET application, and in this module, I want to introduce you to the concept of a View Model, both input and output View Models. Let's get started by looking at an overview of a View Model.
Models and View Models
Quite often in an MVC application, you find yourself in the following situation: you have an application with a database to store data, and that data could be users, restaurants, movies, invoices; whatever is important to you or your business. When you store something like a restaurant in the database, the database will require and store specific pieces of data. For a restaurant, imagine we're storing the restaurant name, the address, and the type of cuisine the restaurant serves. Now somewhere else in the application, you might build a view to edit that restaurant information that's stored in the database. The Edit View will need the name, the address, and the cuisine type of a restaurant to display those attributes and allow the user to edit the data. But an Edit View might need even more data than what their database stores for a single restaurant and a single table. For example, the Edit View might want to display a list of all the different cuisine types that are available, and allow the user to select one. So we could build a list of all the possible cuisine types that are stored throughout the restaurant's table in the database. The problem is when we query the database for a single restaurant, we only get the restaurant data with the current cuisine, not all the possible cuisines, and it's this situation where I like to think of two different model objects. One model object is what I call the Entity Model. An Entity is an object that I persist into the database. So an Entity Model typically looks just like my database schema. If the database stores the name and address for a single restaurant, my restaurant entity object will store name and address via properties. The other type of model object that I think about is a View Model. A View Model is an object that I use to carry information between a view and a controller. It contains everything that a view will need to render HTML. So in the case of an Edit View or a single restaurant, I need a View Model that contains not only the information for my restaurant entity: the restaurant name, the address, the cuisine type, and also an ID would be required; but I also need the additional information that the view needs to produce the list of all possible cuisines. Quite often this requires the controller to make multiple database queries and pull back multiple entities, and then place all that information into a single View Model, which some people would also call a Data Transfer Object, or DTO, because the View Model carries information around. We don't persist the DTO or save the DTO into a database, but we do often copy information into a DTO from an entity, and also from a DTO back into an entity, and we have different model types to serve different requirements in an application. Let me give you a more concrete example. In our application, the homepage currently displays a list of restaurants that come from our restaurant data source. Well let's imagine that this homepage needs additional information. In addition to displaying the restaurants, it also has to display the message of the day, which comes from a different data source. In this case, just putting together a list of restaurants doesn't provide enough information for that view to display everything that it's responsible for. What I want in this case is a special purpose View Model class, something that's dedicated to the index action of the HomeController, and can carry all the information that this Index view needs; and to do that, I'm going to create a new folder in my project, and let's create a ViewModels folder. So I want to think of my models, like restaurant, really as entities. This is information that I'm going to save in the database. I might even consider renaming this folder to Entities, and changing the namespace. Or in a large project, moving all of my entities to a different project. I'm going to leave this be, but we are going to place our ViewModels, that is models dedicated to a specific view, in a separate folder. So inside of ViewModels, let me add a new class, and we will call this the HomeIndexViewModel. So the name of the controller and the name of the action. You might even consider setting up a directory hierarchy like we have for views, so all of the View Models that are used by the home controller can be in a single folder. But this is a simple application, I'm just going to put everything under ViewModels. What does the Index View need to display? Well I know that it needs to display a list of restaurants. So let me add one property that is an IEnumerable of Restaurant that will require me to bring in the OdeToFood.Models namespace, and we will name this property, Restaurants. I also know that the Index View wants to display the current message of the day, so let's add another property of type string called CurrentMessage, and with that, I now have a single object that can encapsulate everything that the Index View needs. So let me clean up the namespaces here, save and close this file, and over in the HomeController, instead of just getting a list of restaurants, let me say that my model is a new instance of a HomeIndexViewModel. I'll just need to bring in this namespace, OdeToFood.ViewModels, and now I can start to populate properties. So model.Restaurants, I can still retrieve from my restaurantData service, asking it to get all restaurants, and model.CurrentMessage, where can I retrieve the current message? Well you might remember earlier in the course we introduced a service, the IGreeter service, so let me add that as a parameter to my HomeController constructor. Again, when the framework creates this controller since that is a service that the framework knows about, it can inject in IGreeter, and just like I did with restaurantData, let's save this this greeter service into a private field that I'll generate with Ctrl+., and now inside of the Index action, all I need to do is say greeter get me the message of the day, that's my current message. I now have a ViewModel that I've put together in my HomeController, that contains all the information that the Index View will need, and now since that data structure has changed a little bit, I just need to change the Index View around. So instead of being an IEnumerable of restaurants, this is going to be an OdeToFood.ViewModels.HomeIndexViewModel instance. So now instead of looping over Model, which will be in HomeIndexViewModel, I need to go to Model.Restaurants, and I also want to display the current message. So maybe an h1 tag that will display Model.CurrentMessage. Let me make sure that I have saved all of my files, so let's refresh the HomeIndex action, and I now have my CurrentMessage, as well as the restaurants that I need, and the controller has taken care of putting all of that together into a single object for my view to consume. That's really the goal of a ViewModel, and there are output ViewModels, which is what we just saw, so putting together a model that I will use to output a response, and then there's also what I think of as input models in ASP.NET core. Let's start looking at input models next.
Detail a Restaurant
Now that we know about output models, let's turn our attention to a very simple input model. What I want to do is, currently in this application we have a list of restaurants, I want to give the user the ability to click on a link to go to the details and see everything about a specific restaurant. To do that, this link will have to point to a new controller action that builds a different type of model; a model that contains just a single restaurant, and in order for the controller action to locate the restaurant that I want to view information about, I'm going to need to pass along an input model, which in this case could just be the ID of the restaurant, a simple integer. To get started, let's go ahead and build that controller action, or at least start to build it. I want an action named Details that will return an IActionResult, and it will take in input model, a parameter, an integer parameter named id. To prove that we have the correct id, let's start with just simply returning Content and do id.ToString to see the id that we pass in being echoed back. So where will this input model come from? Well any time you have a parameter on an action method, the framework will do anything that it possibly can to try to populate that parameter. It will look through the HTTP request that's coming in and look for something named id, or whatever the name of this parameter is; and yes you could have multiple parameters, and these parameters could be of type int, or nullable int, or string, or date time, or even a complex type like restaurant, which we'll see later. One of the places that the MVC framework will look is inside of the routing data. So whatever information it can glean from the route, and because we have a route template that says you can pick apart a route like /home, /action, /something, that third segment of the URL, the framework will treat as the id parameter, and that means if I save my controller with this new action details, I should now be able to go to /home/details and let's say enter the number 22 into the URL and the controller action will echo back that id which is 22. So I can now go to /home/details/ any identifier, and as long as it's convertible into an integer, I'll receive that parameter, and that means I can now build my model. I can walk up to the restaurantData service and tell it to get a single movie using that id. This method is not available yet, we'll have to create that. So first, let's swing over to the interface that the controller is programming against, and I'll add a new method here that can return a single restaurant, the name of the method is Get, and it takes an id parameter. With that method in the interface, I now need to go into the one implementation that we have for that interface, the InMemoryRestaurantData, I can generate that method in Visual Studio using Ctrl+., saying that I want to implement an interface. If I scroll down I can find that new method, and now this is just a simple link query. So out of the restaurants, find me the first restaurant where given a restaurant r, the id property matches this incoming id parameter. So FirstOrDefault, if you're not familiar with that link operator, the first restaurant it encounters where this lambda expression returns true, it will return that object, that restaurant. If its expression never returns true, this operator will return a default value for this type, restaurant, since restaurant is a reference type, FirstOrDefault will return a null value if we pass in an id that cannot be located. We'll have to deal with that null value in just a moment, but for now, save this file, come back to the home controller, and let's simply return a view with that model object. Now I need a new view. So inside of the Home folder, I want to add a New Item, in this case, I want to add an MVC View Page; let's call this Details.cshtml, and instead of building a full HTML response with a head tag and a body tag, I'm going to put together just the simplest possible markup to display some restaurant information. Later in the course, when we dive into more details about the razor view engine, I'll introduce you to layout views, which allow me to control all of the views in my application, and make sure they all have and render a head tag and a body tag, and maybe a side bar, a footer with some copyright information. We'll look at all that later. For right now, I just want to use the model directive to say that this view should expect to receive a model of type Restaurant, a single restaurant. Let's have an h1 tag that displays @Model.Name, so the Model property is typed as Restaurant. I want to display the name of a restaurant, and now all the other restaurant details could appear here. So you could imagine if our restaurant had an address, we could do @Model.Address.Street, @Model.Address.City, and at this point, it is all about writing the HTML to produce the user interface that we want to see, and using @Model. whatever to sprinkle data into the HTML. But I should now have everything in place so that if I go to an id that exists, like /home/details/3, I do indeed see the restaurant name for the restaurant with an id of 3. So ASP.NET MVC has identified this last segment of the URL as an id parameter, it's passed that into my Details action, and that allows me to retrieve the correct restaurant. I also want to point out that the MVC framework will look in the query string automatically for me. So if I go to /home/details?id=, and let's just go to 2, that will give me the restaurant with an id of 2. So the framework will automatically look in the routing data, the framework will automatically look in the routing data, the framework will automatically look in the query string; as we'll see later, the framework will also look inside of posted form values. So when the user is typing into the inputs of a form and submitting that form, we'll see how that MVC framework can automatically populate a more complex model based on those form models. Now you may be wondering what happens if I have an id in the route, so I go to /home/details/3, and I also have an id in the query string; so id=2, in this case, King's Contrivance is the restaurant with an id of 3 because it just so happens the MVC framework will look in the routing data before it looks in the query string. So the routing data will always win there, and what happens if I go to /home/details/22? In this case, my restaurant data source could not find a restaurant with that id so it returned a null value, and inside of my View, when I try to say @Model.Name, and dereference that null value, I have a null reference exception. The also popular object reference not set to an instance of an object exception. There's a couple different ways to handle this. One way would be to write some additional code in the View. Even in the View, there's a couple different ways to handle this, but I could if I wanted to, have if statements in my View. So I could say if the incoming model object is null, then write out some UI that says sorry but we couldn't find that restaurant and I could have an else statement here that would render the rest of this markup if the model was actually present. That's one way to do this. I generally try to avoid using if statements in my View. I believe that type of logic should be figured out inside of the controller before we even get to the View. So inside of the controller, inside of here, I could also check if the model is equal to null. If it is, maybe I've created a restaurant NotFound View.cshtml file, and I could render that view and have it display a nice friendly error message. There is also, if you are building an API, a NotFound result. This is an object that implements IActionResult, just like View result, and this NotFound simple returns an HTTP response with the status code set to 404, so 404 not found. It's typically not a result that you want to use when you're building a web application, that is you're trying to serve HTML to a user that's actually viewing this in a browser. Because the browser will receive the 404 response and display whatever page the browser displays when there's a 404 error. Generally, for web applications, you want to display something that's a little bit nicer than the default browser error page. Something else I could do is also redirect the user so there's a number of methods that I inherit in my controller class that allow me to send back status codes 302 and 301 telling the browser, no you didn't want to come here, you actually wanted to go over to this URL. One of those redirect results: RedirectToAction, will send back an HTTP status code 302, which tells the browser to go somewhere else, and then I can simply provide the name of the action that I want to go to; in this case, the Index action. I can pass additional parameters if I want to reach an action in a different controller. In this case, I just want to go to the Index action on the same controller that I'm inside of, so that's the only parameter that I need to pass along, and in C#, I can also use the nameof operator to get that string in a way that's easier to refactor if I ever change the name of this method. And with that change in place, I should be able to refresh this page, and now instead of an error page, my browser was redirected back to the list of restaurants. Now I think that's an okay behavior in this scenario, because ideally the user won't be typing in a URL. I'm going to provide a link here to get to the details for that restaurant so they can never make that error. Let's go over into our Index view, and see how to build that link. I'll just add another table cell and show you there's a couple different ways I could build that link. One is to simply write out the anchor tag myself. So I could say: I want a link whose href points to /home/details, and then include the Id for the specific restaurant that I'm building a link for here, and let's display the text details, that should allow me now to click on a link and see something about Tersiguels, see something about Scott's Pizza Place, perfect. Another way to build a link that I want to show you, an approach that you'll see in a lot of older ASP.NET MVC applications is to use what we call Html helpers. So in addition to the Model property on our View, there's also an Html property, and here, you can see in the IntelliSense window, there's dozens and dozens of what we call Html helpers, which are methods that we can give some information and they will produce Html for us. One of these methods is the ActionLink method. With ActionLink, I can type in the text that I want to display, let's call this one Go, just to make it different from details. I can pass in the name of the controller action that I want to reach, I can also pass in the name of the controller, and I need to pass along any additional parameters that I need that controller action to have. This restaurant.Id has to be somewhere in the request that this link will send off, and with Html helpers, the way to do that, it's a little bit quirky, but if I build a new anonymously typed object, and use it as a collection of key value pairs. So I can say the key is Id, the value that you need to pass along for that parameter is going to be restaurant.Id, and a lot of people would consider ActionLink to be a little more robust than building this URL by hand because ActionLink will actually consult a routing table to say if I need to reach such and such a controller action with such and such a parameter, what does the URL look like; and that way if I ever change my routing templates, ActionLink will automatically change my links, whereas I would have to go through and change these types of URLs by hand. Now, Html helpers have been around in the ASP.NET MVC framework forever, but honestly, they've always been a little bit plunky, and hard to read, and hard to use. So I want to introduce you to a new feature for ASP.NET Core, a feature known as tag helpers. But because we started this project with the completely empty project type, I just need to make a quick modification here. Inside of the Views folder, I'm going to add a new item, and I'm going to search for Imports, because what I want to add is an MVC View Imports Page. We will talk in more detail later in the course about what the View Imports is, but for now, just think of View Imports as a special razor view that does not render anything, it's simply there to provide instructions to the framework and to the razor view engine on how views should behave, and what capabilities views should have. So I'm going to add this to the Views folder, not to the Home folder, but to the Views folder itself, and I'm going to put a special directive in here that will allow me to use tag helpers that have been written by Microsoft. So @addTagHelper, I'll use an asterisk to say that I want to add all the tag helpers that are available from the following assembly: Microsoft.AspNetCore.Mvc.TagHelpers; and again, if you created an application from one of the more fully-featured project template types, this special file: ViewImports.cshtml would already be in our project and you would already have access to tag helpers. I just had to put this in place since we started with an empty project. But now what I can do, when I write an anchor tag, I no longer have to hardcode the URL, but I can use tag helpers from Microsoft, tag helpers look just like Html attributes, they kind of blend in to your markup, the ones from Microsoft all start with the prefix asp-, and I can use tag helpers like asp-action to say this anchor tag should point to the controller action Details. So the controller action on the same controller that I rendered from, and even though this looks like an Html attribute, it is an identifier that the razor view engine will pick up to do server side processing. So, just because it looks like Html, doesn't mean anything is being done on the client, the razor view engine will change the markup that's being emitted and influenced by these tag helpers. In this case, what asp-action will do is combine together with some other tag helpers to set the href for this anchor tag so that I don't have to hard code it. I'm simply saying: figure out how to get to Details, and by the way, I need a route parameter that is passed. The way I do that is with the asp-route tag helper. I can add another dash here, and the name of any parameter that I want to pass along, so this would be passing along a parameter named foo with the value bar. In my case though, what I really want to do is pass long a parameter named id with the value: restaurant.Id, and I want to display the text more. So, these tag helpers are a lot easier on the eyes compared to the Html helpers, but the end result is the same. So once I save all my files and refresh, I now have links that are built with tag helpers that allow me to move around, and one more tag helper, it would be nice when I'm on the details for a specific restaurant to click on the link to get back to the list of everything. So first of all, let me clean up my View, remove the other two links that we've been generating, and now come over to Details.cshtml, and I want to use a tag helper here also. I want an anchor tag that points to the action Index, and again, if this was in a different controller, I could also specify the controller, but let me just be explicit here and say Index action on the home controller, display the text Home, and now if I refresh the details page, I have a link that allows me to get back to the homepage. And what we're dealing with now is a very simple input model, just a simple integer value, but what happens when I want to give the user the ability to create new restaurants and they have to fill out lots of complex information about a restaurant, how do I accept those values as an input model? Let's look at that next.
Create a Restaurant
Now let's take a look at a more complex input model. What we want to do is give the user the ability to create a new restaurant, and save that restaurant ultimately in a database. With Html, anytime that you want to give the user the ability to enter data into a page, you use an Html form element. How will we build this form? Like everything else with MVC, we will have an HTTP get request that arrives at a controller action, and this controller action will return a view that includes a form tag. So to create a restaurant or edit an existing restaurant, we'll need action that returns Html with a form, and inside this form, we will have input elements to enter text and numbers. We might also have selects, and sliders, and checkboxes. There's a variety of input controls, but every one of these controls will need a name because ASP.NET will process the incoming data by name and try to match that up with a model object that we can pass as a parameter into a controller action. So you can imagine in input, type= text whose name equals name= Name, we can have the framework automatically map the value that the user types into that input to the name property of a restaurant. It will do that when the form hosts itself back to that web server. We always want to do create, and write, and update, and delete scenarios using an HTTP post, and we're going to see just how easy it is with the framework to map incoming form values from our inputs into a model object that is a parameter to our controller action. This makes our life easy, as long as we follow these naming conventions. Let's see how it works. Back in the application, I want to link here on the homepage that will take me to another page that will display a form to create the new restaurant. so in the Index view, below the table, let me create an anchor tag and use the asp-action tag helper to say manipulate this anchor tag to go to the Create action for the existing home controller, and I can display the text Add Restaurant. Currently, we do not have a create action on the home controller, let's go ahead and add one; an action that will return an IActionResult, the action name is Create, I do not need any parameters, and I'm just going to simply return a view to get started with. In this case, I'm not going to need any view models to put together information for this View; obviously if I wanted to edit a restaurant, I'd first have to retrieve that restaurant, and then send that restaurant to the view or editing, but simply creating a restaurant, that's a little bit easier. I don't need to start with an existing model, except there are those circumstances where you need to put together information to build the form to allow the user to create something. You might need to put together, for example, a list of countries that your application supports, and allow the user to select a country when they're creating something. To somewhat simulate that situation, let's build out our restaurant to include some more information. What I want to do is store the cuisine type of our restaurant, and to do that I'm going to add a new class file to this project, let's call that CuisineType, only this will not be a class, I want to build this as an enum. So what are the available cuisine types for the application? Let's start with a simple list. We'll start with None, that represents a restaurant where we don't know the cuisine type, and we can say there is Italian food, there's French food, and there's German food; there's obviously much more food than that around the world, but let's just work with those four entries for now, and now that I have a cuisine type, let me add a property to my restaurant of type CuisineType, and the name of the property can be Cuisine. So now when I allow a user to create a restaurant, I want them to enter both the name of the restaurant, as well as select a cuisine type from a list of the available cuisine types. Now since I have an enum here, I'm actually going to be able to build that list inside of the View itself, and let's just take a look at how that works. So inside of the Home folder, let's add a new item; in this case, I want an MVC View Page, I'm going to say this is the Create.cshtml page, and again, I'm just going to build the bare minimum Html, no head tag, no body tag, we'll come back and fix that up later, and also, this is going to be one of the uglier forms that you'll ever see on the web because we're not going to add any styling or try to make this look nice, we're just going to focus on the functionality. So my model type is an OdeToFood.Models.Restaurant, that is the type of model that I'm going to be manipulating, even if I'm not passing that model into the View, this meta information's going to give razor some information that helps me out with IntelliSense here in just a bit. So let's add a header that will just let us know that we are creating a restaurant, and then we will start with a form tag. Now the form method, we do want this to be post, anytime we are creating, deleting, or updating something, we should always send that information through an HTTP post, so method= post is fine. I'm actually going to remove the action parameter here because by default, this form will always post back to the same URL that it came from. So if my browser issues a Get request to /home/create, and that URL responds with a form, when I submit this form, this will post back to /home/create, and we'll see how to handle that in just a moment. In order to submit, I will need an input of type submit, let's give it the name of save, and let's give it the value to display to the user of Save. That's the button they'll click on to save a restaurant. Now, I just need input for the user to enter cuisine information and the name of the restaurant. So for the name of the restaurant, I can build an input, in fact, let me use the code snippet, input, and press Tab, that will build me an input with a type attribute. What would the type be? I would want this to be text. What would the name of this input be? Well this is where I want to make sure that the names of my inputs match up with the names of the properties that I'm trying to bind against in my input view model, which we'll have to build in the next clip. So if I have a restaurant and I want the framework to automatically take the value of this input, input it into the Name property of my restaurant, then I would have to give this the name of Name, as opposed to Title, or Street, or Location. This goes to the Name property, and the initial value is going to be empty because I'm creating a fresh new restaurant. Now another way to write this input is to use tag helpers that are available, or different input types. So asp-for is a particularly useful tag helper when you're building forms because it can associate an input with a specific property on your model. So I can say this input is for, and you'll notice I have IntelliSense here, this is for the Name property of the restaurant, and this IntelliSense list is driven off the model type that is here; and in just a moment, I'll show you how this tag helper, asp-for, manipulates the Html attributes of this input element using metadata that it gleans from that name property, the Restaurant. For example, how does it know what type this input should be? Well because the Name property is of type string, the asp-for tag helper will set the type of this input to text, and allow the user to enter arbitrary text. What about the select option? Ideally, I want to use a select for the cuisine type because I want to force the user to select one of the specific cuisines that is available. One way to build that is to write a select, and give it the proper name, and write out all the options for the different options I have in my enumeration, but another way to build this is again to use tag helpers. So I want a select for, which property? For the Cuisine property of my restaurant. But I also have to provide that list of items that the browser will display in the select, and I can do that with another tag helper that's available; you can see it pops up in IntelliSense right away, asp-items. Asp-items, what I need to pass in you'll notice here in the IntelliSense window, I need to pass in an IEnumerable of SelectListItem. That's the type of information that I could build back in my controller action, and pass as part of the model for this view to use, and I could just pass that information along to asp-items. What we're going to do, since we're conveniently using an enum, is actually build our items out of metadata from that enumeration. The way I can do that is by using an Html helper, which is GetEnumSelectList. This is an Html helper where you essentially pass in a type, like CuisineType, and it will use reflection to grab the available entries available for that enum and build a select list for me. So all I need to do is pass in a generic type of CuisineType, which, before I do that, would be easier to do if I have a using statement for OdeToFood.Models. So just like a C# file can have a using statement so that I can use type names without a full namespace qualification, razor views can also have a using statement, and now anything that's in this namespace OdeToFood.Models also available throughout the razor view. So CuisineType I can specify here without fully specifying the namespace. So build me a select, an Html select for the Cuisine property that uses the items that you find in this CuisineType enum; and if I save this view, and let's go to refresh here to get a create link, now I have my link to add a restaurant, when I come here I now have a text box where I can type in the name of a restaurant, and I have a list of the available cuisine types. Let's take a quick look at the source code for this and view the Html that razor rendered for us. So I can see on the first input here, all I had was input asp-for= Name. With this asp=for tag helper, which again was processed on the server inside of razor, emitted an input element that has a type of text, and id of Name, a name of Name, and an empty initial value. If my model object was an actual restaurant object that my controller passed to this View, this asp-for tag helper would also populate the initial value of this input with the name of the restaurant. So that's something that I could use when I build out the Edit View. It makes the Edit View very simple, and I can also see for the Html select, in addition to some data- attributes, which we'll take a look at later in the course, this also has the appropriate id and name so that the selected cuisine type, the MVC framework will be able to take this value and just automatically be able to populate the cuisine property of a new restaurant. Now there's one last bit of Html that I want to call your attention to, this is an input with the name RequestVerificationToken, it's type is hidden, so the user doesn't see those, and it does contain indeed, a cryptographically significant token. This token is in place to prevent a cross-site request forgery. In other words, this token helps to ensure that the form the user is submitting is a form that your web application created. It's not a form that some other malicious website might've created trying to trick a user into submitting information to my site; and we'll see in a moment that it's very easy to validate this token to make sure the user is submitting information that I wanted them to submit. And we'll see that in the next clip, because what we want to do next is to be able to fill out some information to be able to click the Save button and have all this information arrive at the server, where we can build a new restaurant and save it into our data source.
Accepting Form Input
Now we are ready to post our restaurant to the web server, and hopefully this will reach a controller action that has an input model as a parameter to the action. As I said before, the MVC framework does everything it can to populate your action parameters based on information in the HTTP request environment. Like earlier, we passed in Id parameter to the Details action, and MVC populated the Id parameter with the Id that the framework found in the route data. It could also populate that from the query string. For complex input types, like a restaurant, the framework will look at the properties on that type and map route data, query string data, and form data into the properties. So if I post a form with the name and cuisine in the form data, the MVC framework will automatically map and move these values into their perspective properties on the input model, because a restaurant object has a name property, the framework will find that posted form value with the name of Name, and the value of Dolce, and move that value into my restaurant's name property. You do have to be careful when selecting an input model because the framework will by default try to set all the properties on that model using information that it finds in the request, and you can never, ever trust data coming over the network. Let's see how this works and how to avoid problems. Back in Visual Studio, when the user clicks on the submit button, the form that we have rendered will create an Http post that goes back to /home/create and includes Name and Cuisine values. I want to capture those values and save them. So because this is coming back to a Create action, I will add another version of Create inside of my controller, but this version of Create needs to take an input model. So one type of input model that I could use is a restaurant itself. A restaurant has a name property, it has a cuisine property, the MVC framework will be able to map those form values into this object. Just like it did previously with a simple integer input. However, you always have to remember that the MVC framework is going to try to map everything that is in this object, and that would give a malicious user a chance to add additional posted form values to an Http request and try to manipulate information inside of this model that they should not have access to. This is a web security vulnerability known as over posting. You are receiving more information in the Http request than you expected based on the form that you gave the user, and that's because a malicious user wouldn't necessarily play by the rules of the form that you gave them. Now there are various ways that you can avoid the over posting vulnerability with the MVC framework, but one simple and very straightforward technique to use is to always create a dedicated input model that only contains the properties that you would expect to post from a form. In other words, let's go into ViewModels and create a class that will serve as an input model for restaurant. So let's call this the RestaurantEditModel. This is something we might use to edit an existing restaurant and also create a new restaurant, and all I need on this new ViewModel are properties that match the names of the fields that I have in my form. So, in other words, if I have a Name property, that will capture the name value coming in from the form, and if I have a CuisineType property once I bring in the namespace here, if I give this the name Cuisine, that will allow me to capture the incoming cuisine. So now I have an Edit ViewModel that I will clean up and save, and I can use this input model as a parameter to my Create action. So RestaurantEditModel, we'll just call this model, and what we'll have to do inside of this action is copy the information from that input model over into a real restaurant, and then tell our data source to save that new restaurant. Before we do that, I just want to have a return statement that will allow this project to compile and show you the potential problem that we can run into. Now I as the developer know that I want this Create action to respond to an Http Get request, when the browser comes out and requests /home/create with a Get request, I want to render the view that will display the form to the user, and then when the user clicks on the save button to submit that form, I want that post request to come to this controller action that receives the input model so I know that, but the MVC framework doesn't necessarily know that. So if I refresh /home/create, and issue another Get request for this resource, I'll receive the AmbiguousActionException. This is how the framework tells me that it could not identify the precise action that it's supposed to invoke. It sees two candidates here, both named Create, but based on the information it has, the framework doesn't know if it should call this Create or this other Create. So what we have to do is add what we call route constraints that will allow the framework to select one of these two actions, and there are constraints that we can add via C# attributes that can give the framework a little more information about which action to invoke. So the HttpGet constraint tells the framework this version of Create should only be invoked if the request is an HttpGet request, and there is also an HttpPost attribute. And now if I issue a Get request from the browser for /home/create, the framework has enough information to understand which of these two actions it should invoke. So it invokes the one with the HttpGet constraint, and this is a pattern that you'll commonly see when working with forms. You'll want one version of an action that only responds to a Get request and returns the form, and another version of the action that accepts an input model and only responds to a POST. So now that we can arrive at the correct action, let's go ahead and create a new restaurant, and we can copy information from our model into this new restaurant. So the newRestaurant.Name equals the input model Name and the newRestaurant Cuisine type equals the model's Cuisine, and I have now safely assured that I have only copied in information that I received from the form into this new restaurant, and a malicious user isn't setting any properties that I don't expect. At this point, I would expect to be able to go to my restaurantData source, and we do not have an Add method yet, but I would expect to say I want to add a newRestaurant to the collection of restaurants that you manage, and I might even expect that my data source could do some manipulation on that restaurant, and might even return me a new object that represents that new restaurant, so I'm going to expect Add to return a restaurant object to me, I'll capture that in a variable, and then what do we want to do? Well, ideally we'll show something to the user that will allow them to understand that the new restaurant has been created successfully. Since I do have the ability to select which view I'm going to render from every controller action, let's do something that's very simple right now. I'm going to render the Details view, and I'm going to have it display this new restaurant that we just added to the data source. There's a bit of a flaw in this logic, there's something about this scenario that I should avoid, but we'll come back to that later. Let's just see if we can get this to work. First, I'm going to need to make an add method available. So on the interface that the controller programs against, let's have a method that returns a restaurant, the name of the method is Add, and it expects to take a restaurant parameter, and that will include the information that it wants to place into the database. We will also have to come over to the implementation of that interface in our InMemoryRestaurantData, and implement this new method; I'll do that with Ctrl+. and let me scroll down, and let's provide a very simple InMemory implementation. Now the one property that we haven't populated in a restaurant is the Id property, and that's because typically the database is going to generate some sort of Id, because that Id has to be a unique value. Since we're only working with InMemory data, let's just try to compute a value, and this is good for development or testing but obviously you couldn't do this in production. I can say that the restaurant's Id will be a value that is computed by looking at my existing restaurants, finding the maximum Id value using the Max operator, and then let's just add one. At that point, I can walk up to the list of restaurants that I have and say, let's add this new restaurant. Then, all I need to do in this case is return that restaurant because we do expect to return a restaurant object, and my data source should be in place. Now the one caveat here is that we are dealing with a service that holds restaurant data in memory, and now that we're modifying that list, I want every Http request that comes to the application to see that same list, and that's going to require one small change. First, let me save all the files, and now come into Startup.cs, and currently when we configure the framework to inject the RestaurantData service into the various components that need it, we said to inject that service with a scoped lifetime. That means instantiate an instance of InMemory data for each Http request, reuse that instance throughout the request, and then throw it away. That's not going to work if I want to see the changes in this InMemory list across several requests. So I'm going to change this to AddSingleton, which is only going to work under development in test. I guarantee you will have an exception or a corrupt list of restaurants if you try to use this and there's multiple users in the application adding restaurants. It will happen eventually, but for right now, we should be able to see if all of our changes have worked. I should be able to accept the input model, map the input model into a new restaurant, and tell the data source to save that restaurant. Now I just want to change around the Details view slightly to display some additional information. I actually want to see if the Id was generated properly, so let's also display @Model.Id, and I want to know if the cuisine was properly mapped, so let's also say that the Cuisine is @Model.Cuisine. So once again, save all the files in my project, let's come back and issue a Get request for this form, and let's see if we can create a new restaurant. Let's call something Sabatino's, and we will set the cuisine type to Italian, click Save, and I do arrive on the details view. I can see the new restaurant has an Id of 4, that seems right, it has a Cuisine of Italian, seems like we've saved everything. I could even go to the homepage now, see a list of all the restaurants, and see that Sabatino's is included, which is good, and let me go to the back button to come back to the details view, which was really rendered by /home/create, and that's actually going to create a bit of a problem. So we're going to address that problem and then also take a look at data validation over the next couple clips.
POST Redirect GET Pattern
At this point, I want to introduce you to, or remind you of the POST - REDIRECT -GET Pattern for web applications. Currently, our client, the browser, has a form that will use an Http POST to send new restaurant data to the server, and currently our server responds immediately with the details of the new restaurant. But responding to the POST operation with an Html page can cause troubles, because if the user decides to refresh the browser, the browser will need to send another Http POST message to the web application, and that will try to add another restaurant to the data store again. It's common practice in web applications that when you have a successful POST operation, you respond to the POST with an Http redirect status code, and tell the browser to send off a new distinct GET request to read the new data from somewhere else. Remember, POST is for write operations, and GET is for read operations. By redirecting the client and having them issue a new GET request, we can deliver a new page for reading, and that's a page that the user can bookmark and refresh in the browser at any time without trying to write new data. Let's see how to add this to our application. When we last left the browser, we were looking at the Details view for a new restaurant that we created by successfully posting to /home/create, and as a user, if I want to refresh this page to make sure I do have the latest information, every browser I can think of is going to display some sort of warning, because I'm about to do something dangerous, I'm about to refresh a POST operation; and that means the info will be resubmitted to the site. This can lead to duplicate restaurants, duplicate charges on my credit card, duplicate invoices; it's generally a situation that we want to avoid, and we don't want to put the user into a situation where this can happen, and with the MVC framework, there is an easy solution. Back in our controller, what we should do instead of immediately returning a view with the Html to display the details, we should send back a redirect response. Remember earlier, we used a redirect response so that if the user entered an Id for a restaurant that didn't exist, we could redirect back to the Index action. In this case, what we want to do is redirect to the Details action. We want to redirect and formulate a URL so the browser will issue a fresh Http GET request for that Details action so instead of return View, I want to RedirectToAction, the name of the action again is the Details action, and now what I need to do is pass along some additional information so the Details action knows which restaurant to look at. I can do that by using the second parameter here: routeValues, and just like we saw earlier with the Html helper, one way to do this is to create a new anonymously typed object whose properties represent the parameters that we want to pass along. I want to pass along an Id property, so I will say create an id property that is set to the newRestaurants.Id property, the Id that was calculated by the data layer, and behind the scenes, RedirectToAction will take that information, consult with the routing rule for that defined for the application, and determine that the Id parameter can go into the last segment of the URL. If I had additional parameters that I wanted to pass along, those parameters will reach the other action also because if this doesn't exist in the routing rules, then this value would be placed into the query string. So we'd have something like /home/details/id query string foo=bar. But with this change in place, let's come out and carefully issue another GET request for /home/create that will send me back to the form where I can create another restaurant, let's call this The Airport Inn, I'm just going to select a cuisine, click Save, and now I can see that I have been redirected to /Home/Details/4. This is a response that I can refresh all day long, and that's just a simple Http GET request now, and we've put the user in a situation that's a lot less dangerous than what we had before. Now you might notice that the Id here is 4, which is the same as the restaurant we created earlier, which has now been lost, and that's because when I made changes to the application, ASP.NET had to recompile and restart this application, that all happens behind the scenes, but since we're only maintaining these restaurants in an InMemory list, we lost all of the changes that we had before, the previous restaurant that we added. That problem is going to go away when we start using a real database behind the scenes. We'll do that later in the course. For right now, let's talk about what happens if the user enters invalid data. For example, we don't want the user to be able to create a restaurant that has no name, we'll look at that topic next.
Model Validation with Data Annotations
When the user creates a new restaurant, there are some rules that we want to apply to the data that they give us. For instance, we want the restaurant name to be a required field. One of the easiest techniques for enforcing rules like this is to use data annotations that are available in .NET core. Annotations are attributes that we can apply to a class, attributes like MinLength and MaxLength. That would require a string input to meet a certain minimum or maximum size. We can also check the range of a number, it has to be between 1 and 10. We can match a string input against a regular expression, or compare two model values when using this compare annotation. Some data annotations also influence the display of a model value or the label for a model value. There are annotations like Display and DataType. If I tell the framework that a given model value will hold an email address, the framework can then render an input element where the type attribute is set to email, and on some devices, this will give the user a slightly different input element, or a different keyboard to enter the value into that input. Let's add some annotations in our project. Currently, if the user doesn't enter a restaurant name and just clicks the Save button, we will create a new restaurant in our data source that doesn't have a name. That is ultimately the scenario that we want to learn how to prevent in this clip, but along the way, I want to show you a few other things. First of all, how does the user even know that they are supposed to enter a restaurant name here? Currently we have no label associated with the input, let's fix that. I'm going to select the input element and right-click and say that I want to wrap this with a div. I could also do that with Shift+Alt+W, and inside what I want to do is add a label for this input, and I can do that using an Html label element, and then add a tag helper asp-for, the same tag helper that we used with input, and with a label, what asp-for will do is determine what text should be associated with this label. It will do that by going to the model object, in this case a restaurant, looking at any metadata that is available about the restaurant's name property, and then determining what text to display. I'll show you how this turns out in just a second. Let's also wrap our select with a div and add a label here that is asp-for Cuisine. Now when I save this view and refresh the browser, we will have labels. So I know this is Name, I know this is Cuisine. How did asp-for determine that this should be called Name? It determined that because it looked at the restaurant, saw that this was of type string, the name of the property is Name, so it simply uses the name of the property as the text for the label. What if I wanted to change that behavior? What if I wanted to display restaurant name? That's where I can start using these annotations, which are C# attributes, to influence the display of labels and inputs, as well as add validation rules. So first of all, all of these attributes are in System.ComponentModel.DataAnnotations, and once I bring that namespace in, I can go up to any property and start adding annotations. For example, DataType, Display, DisplayFormat. DisplayFormat is a useful annotation when you're working with numeric types and date times, because you can use C# formatting strings to tell the framework how a particular value should be displayed. Should it be a currency? Should it be a number with a specific number of digits after the decimal place? I can also use Display to say that when you are displaying a Name for this particular property, I want you to use the following text. Let's use the text, Restaurant Name. Just by saving Restaurant, and refreshing my form, the tag helpers will pick up on that new piece of metadata, and I now have a label that says Restaurant Name; and yes you can localize these through the use of resources in ASP.NET Core. I can also have multiple annotations for a specific property, so I could also tell the framework that yes, the underlying type here in my class is a string, but really I want you to treat this as if it is a password as opposed to an email address or a phone number. When I do password, the ASP tag helper, when it's creating this input, we'll no longer render an input type equals text, instead it will render an input type equals password, and that means when the user types into this input, we don't see the characters that are being typed. That's obviously not how I want to treat the name of a restaurant, so let me get rid of that particular annotation, I just wanted to demonstrate that. Now the specific annotations that we use to validate data are annotations like Required. So I can say this particular string is required, if the string comes in as null or empty, then I want some sort of validation error raised. I can also say that the maximum length of the string should be 80 characters, and with that information in place, the tag helpers can render additional attributes into the Html that can be used for client-side validation. We're going to leave that for a later module in this course. We will come back and look at client-side validation. But right now, I also want to talk about our input validation. So whenever you have an input model to a controller action with the MVC framework, not only will the framework try to bind information that's in the Http request into my model, but the framework will also apply some validations based on data annotations. So if I use data annotations on my RestaurantEdit view model, I can ask the MVC framework, did all of the validations pass, and the MVC framework can tell me yes or no. It can also generate error messages for me that I can display to the user. Let's see how this works. For the RestaurantEdit view model, I want to go to the name and also add an annotation that says this is Required, which will force me to bring in System.ComponentModel.DataAnnotations, and also that the maximum length here is 80 characters. I could do these as two separate attributes or place them both on the same line like this, and now, back in the controller, when I receive this input model, the MVC framework, this process of moving data into my input model from the request, from the posted form values, and the query string, and the route data, that's a process that the framework calls model binding; binding data into your model, and anytime the framework does model binding, it also produces a data structure known as model state, which gives you information about that binding process. Did it work? What values did it see? What properties were populated? And I can access information about the state of model binding by going to a property on my controller called ModelState. From here, I can do things like add custom model errors if I have logic that I cannot express through a data annotation. I can look at the count of errors that the framework discovered when it did the model binding, and I can look at a Boolean property, IsValid, to determine if there were any errors or not. Let me place ModelState.IsValid into an if statement, because really what I want to do is only create a new restaurant and add that restaurant to the data source, and redirect to an action if ModelState was valid. Otherwise, what do I want to do? Well if ModelState is not valid, that means that the user submitted information that did not pass all of my data validation rules. So perhaps they did not enter the name for a restaurant, and the best thing for me to do in that situation is to represent the form that the user is working on, and allow them to try to correct any information that they didn't enter. So if I simply return the Create View again, the MVC framework is going to be able to present that form to the user again, and these tag helpers that are working behind the scenes are going to work with this ModelState data structure to figure out which properties had some associated errors, so we can display error messages to the user. But I do, in my markup, inside of my razor view, have to select a location of where these error messages are going to display. So I can create a simple span and use an asp-validation-for tag helper to say display validation messages and errors for the name property, and I can also do that for my select input. So I could say give me a span that provides validation messages for Cuisine. There is also asp-validation-summary that can provide a list of all the errors collected from the form, and that might be something that you place at the top or the bottom of the page if you want to provide a summary of everything that went wrong inside of a form. But with these changes in place, with my validation tag helpers with the check on ModelState.IsValid with the data annotations that I have on the restaurant ViewModel; if I save all my files and come back out to the Create form, and let me just do a refresh to make sure we have all the latest things loaded, I should now not be able to click the Save button and create a restaurant with an empty name because the model binding process in the MVC framework determine that the name is required. So when I re-render that Create form, because what we just did was detect that ModelState was invalid and re-render the Create view, what will happen is the tag helpers can also detect that there was some bad model state, and they can display the errors associated with ModelState. So the Name field is required. That's the default error message, you can customize any of those. Now there is one additional bit of validation that we really must ensure that we always do when we're working with form POST. You might remember earlier when we inspected this page, we saw that inside of a form there was a hidden input request verification token, and I told you that this token exists to prevent cross-site forgery request. On the server, we always have to check to make sure that we are verifying this token, and that is a simple attribute that we can add to any controller action that takes a POST operation, and this is particularly important if you are authenticating users using cookies, but I want to add the attribute, which is ValidateAntiForgeryToken. And again, any time you're authenticating users with cookies, you definitely want to use ValidateAntiForgeryToken, but it's also just a pretty good practice whenever you're building web applications that accept for POST in general. You'll find other security-related courses on Pluralsight that go into more details about cross-site request forgeries and how to prevent them, but ultimately, this attribute, along with this hidden input, will help to ensure that the form the user is submitting to our web server is a form that we gave to that user for submission.
Summary
In this module, we made a distinction between entities and models, and I gave you some examples of different types of models: input models and output models. We also saw how to use data annotations to influence the labels and the inputs created by tag helpers, like asp-for, and we saw how tag helpers work with forms and controllers and validation rules to produce model state and validation error messages. In a controller, you always want to check ModelState.IsValid to make sure an input model is properly populated, and if not, you typically re-render the view that displays the input form to the user so they can fix any validation problems and resubmit the form. Also, always remember to validate anti-forgery tokens in your web applications.
Using the Entity Framework
Introduction
Hi, this is Scott, and in this module, we're going to set up and configure our application to save and read data from a SQL Server database. To work with the database, we're going to use the entity framework core. This is a rewritten version of the entity framework to work with the new .NET Core framework, although if you have worked with the entity framework in the past, you'll see many familiar pieces. To get started, let's first talk about your SQL Server installation.
SQL Server LocalDB
In my application, I'm going to use SQL Server LocalDB, and if you are comfortable with SQL Server, you can use any database that you like; a local database, a remote database. As long as you have permissions to create a new database on the instance that you're connecting to, you should be just fine. I'll be using LocalDB, this is a special edition of SQL Server that is optimized for developers. Visual Studio 2017, even the free community edition, will install LocalDB by default. If for some reason you do not have a LocalDB installation, rerun the Visual Studio 2017 installer, and on the individual components tab where you can select what is installed, and under the Data category, just make sure that you install SQL Server Express 2016 LocalDB, and you'll probably also want to install SQL Server data tools, and SQL Server command line utilities. That can make working with SQL Server a little bit easier. I want to show you some tips on how to verify your LocalDB installation, including how to use a command line tool and how to use a SQL Server object explorer in Visual Studio, and these steps will just help to ensure that your environment is correctly set up for us to continue with the entity framework. So two quick tips on how you can verify your SQL Server LocalDB installation. The first tip is to run a command line tool. To do this, I need to open a special developer command prompt, so I'm going to use Windows to search for Developer Command Prompt for VS 2017; this is a special command prompt that Visual Studio 2017 installs and configures to set it up so that certain executables are in the path, and one of the executables that I can use is SQL LocalDB, I want to run this executable and I want to pass a parameter of info to give me information about SQL Server LocalDB. If you don't have this command, perhaps you didn't install the command line tools when you installed Visual Studio 2017, and that's okay. I'll show you another approach to actually connect the SQL Server LocalDB, but if you execute this command and it returns with some information, there's a good chance everything is set up properly. What this command is listing with the info parameter are two local instances of SQL Server LocalDB, and it is the first instance: MSSQLLocalDB, that we will be using in this course, or at least I will be using. Again, you can use any database that you're comfortable with, and if you're trying to follow along with this course and running on Mac OS or Linux, go out and do a search for SQL Server on Docker containers, you'll even be able to run SQL Server on Linux or Mac OS using Docker. Another tool that I want to show you inside of Visual Studio is if you go to the View menu and select SQL Server Object Explorer, this is a nice graphical environment that you can use to explore a database, and by default, SQL Server Object Explorer should just connect to LocalDB, and it's from inside of here where I can click around and see currently we don't have any databases. This is where I'm hoping to see the OdeToFood database appear later in this module, and I can also right-click on the server, select Properties, and if I scroll all the way up to the top of the Properties window, I can find a connection string. The most important part of the connection string is the instance to connect to, and that is the first part of the connection string here that says Data Source equals, and then to connect to LocalDB, I use LocalDB inside of parentheses \MSSQLLocalDB. So if you want to connect to your LocalDB from another tool, a graphical tool or a command line tool, you can use a connection string that starts with Data Source=(localdb)\MSSQLLocalDB. That should allow you to connect also here from SQL Server Object Explorer if this connection doesn't appear right from the start. But now that I'm sure I have a SQL Server Local DB, it's time to learn how to use the entity framework to connect to that server and create a database, and manage some data inside.
Installing the Entity Framework
In order to use the entity framework to connect to a SQL Server database, and insert, update, delete, and re-data, my project will need to reference some entity framework core NuGet packages. It turns out that we already have a reference to the packages that we need at runtime. You might remember when we created this project, and I'm just going to right-click the project and edit OdeToFood.csproj, this is the project file for the application, we started with a NuGet package reference to Microsoft.AspNetCore.All. This package only exists to reference other packages, and it references dozens of packages, lots of packages that will bring in all sorts of different features, including the entity framework. I can verify that if I go into Dependencies, NuGet, expand AspNetCoreAll, here you can see a list of all the packages, and if I come up to the search box here, I can search for entity framework or some part of that string, and see that the all package references Microsoft.EntityFrameworkCore.SqlServer. That is the specific package that I would need to start with to talk to a Microsoft SQL Server database from the entity framework; and there's many additional packages here also. If I'm in a project where I'm not using Microsoft.AspNetCore.All, I can always right-click the project, go to Manage NuGet Packages, and from here, I could browse and search for Microsoft.EntityFrameworkCore, and install the SQL Server package that I need. I could also do this from the command line. So, you might remember the .NET CLI that we looked at earlier in the course. There is a .NET add command where I can say I want to add a package reference, and then provide the package name. But I also want to point out that this .NET command is extensible. In addition to .NET add and .NET build and .NET run, there are NuGet packages out there that exist that can add additional commands for .NET to execute. The command ultimately that I want to get to is the dotnet-ef command. Currently if I try to run this, I receive an error: No executable found matching dotnet-ef, but it is this command that I want to be able to have access to because this command will allow me to create a SQL Server database based on my C# classes that are my entities. I can also use this tool dotnet-ef to update a database if I make changes to my C# classes. I can also generate SQL scripts. If I want to take those SQL scripts and manually update a database to match the entities that I'm using. All I need to do to have dotnet-ef work is add an additional NuGet package reference, but it's a little bit different than your standard package reference. So I'm going to place this into a different item group just to keep it separate from my normal package references, and what I want to add is a DotNetCliToolReference. This tells dotnet, as well as IDEs like Visual Studio, that there is a NuGet package that I want to reference, and it is a tool. So it might not be something that my application needs when it is executing at runtime in production, but it's a tool that I need to be able to reach through the .NET CLI. The name of the tool is Microsoft.EntityFrameworkCore.Tools.DotNet. I also want to specify a version, and for the version, there might be a newer version by the time you watch this, but I generally want to stick with the same version or at least the same major version of .NET Core and ASP.NET Core. So, I want a .NET CLI tool reference for Microsoft.EntityFrameworkCore.Tools.DotNet, I'm going to save my project file, come back to my command prompt, and now when I am in the same folder as my OdeToFood.csproj file, it should be as easy as just typing dotnet-ef, and I now have access to the Entity Framework Tool .NET Command Line Tools. These tools will help me manage my database, as well as the migrations that I might need to apply to that database, as well as my DbContext types. DbContext is a very important type of the entity framework, and now that we have the tools installed and we know we have the proper NuGet package references, let's talk about this DbContext.
Implementing a DbContext
To use the entity framework, we need to create a class that derives from the entity framework's DbContext base class. Each DbContext class that you have will give you access to a single database, and I don't mean a physical database. You can always change your connection string so that a single DbContext class can point to different servers and different databases over time, but logically, a DbContext class maps to a specific database that has a schema the DbContext understands, and on that DbContext class, you can create properties that are of type DbSet of T. The generic type parameter T will be a type of entity like restaurant is an entity in the OdeToFood application. So if I want an OdeToFood database, I'll create an OdeToFood DbContext class and implement a DbSet of restaurant on that class. Each DbSet in this class will map to a table and the database, so if I have a property DbSet of restaurant, and the name of that property is restaurant, the entity framework will by default look for a table restaurants inside of my database. Now there's lots of mapping and configuration that you can do to override these defaults, but we won't need to do anything fancy in this application. Let's just dive in and see how this works. The first step is to create my own class that derives from the entity frameworks DbContext class. I could place this new class in a folder called Entities, I could place this class in my existing folder, with my other models, many people would even take this class and all the entities like restaurant, and move them into a different class library. We're just going to take a simple approach, I will create a new folder, and I will name this new folder Data, and I will create my class inside of this data folder. So add a Class, let's call this OdeToFood DBContext.cs. I do need to derive from the entity framework's DbContext base class; that means I'll need to bring in the namespace: Microsoft.EntityFrameworkCore, and then for each entity that I have, I will want to create a property of type DbSet of T, where T is my entity type. This project has just a single entity so far, which is Restaurant, and that will require me to bring in the namespace OdeToFood.Models, and I want to name this property: Restaurants. There'd be a lot more I can do with the entity framework; I could have multiple entities, I could have related entities, one to many relationships, and one to one relationships, but in this application, we're going to focus on getting this DbContext up and running and configured to talk to a SQL Server, and saving restaurant information. In order to configure this DbContext so that it can work with different databases, I'm going to add a constructor with the ctor code snippet, and allow this class to take some options that can specify what database to connect to. We'll see that in just a second; and those options that will include information like the connection string, we will pass them in an existing class that the entity framework defines called DbContextOptions. Now in some scenarios, you might want to save these options, you might want to inspect these options; all I need to do is take these options and pass them along to the DbContext constructor, and my base class will take care of looking at those options and setting up the proper database connections; and this is all I need to store restaurants in a SQL Server database. At this point, I could create a new instance of an OdeToFoodDbContext inside of one of my controllers. A better approach would be to register OdeToFoodDbContext as a service so that I can inject instances of this class into the various components that need it, but I'm going to go even one step further. What I want to do is to hide this DbContext behind a service that we already have defined which is IRestaurantData. So I want an implementation of IRestaurantData that works with a DbContext to store and retrieve the information in SQL Server instead of the InMemory implementation that we currently have. So let's add a new class to the Services folder, and I will call this SqlRestaurantData, and SqlRestaurantData will implement IRestaurantData, which I'll select from the pick list here, and then use Ctrl+. to generate all the methods that I'm going to need to implement. And in order to implement these methods, I'm going to need an instance of my OdeToFood DbContext, let me save that file, come back to SqlRestaurantData, let's add a constructor to this class with the ctor code snippet, and this will be a constructor that takes an OdeToFoodDbContext, we will call that context. I will need to bring in the namespace OdeToFood.Data, and I'll need to use this context from the different methods that I have on the class. So let's save that context into a private field that I'll generate with Ctrl+., and now we can take a look at how to implement these different methods. First, let's implement GetAll. With GetAll, I can simply walk up to the context, and since I have a Restaurants property defined on that DbContext, I'll have access to restaurants. At this point, I could also add OrderBy statement, so I could say given a restaurant r, order by the name of the restaurant, and if I have a return statement here, what this will do is return an IEnumerable of restaurant. Now for larger applications, you do have to be a little bit careful with IEnumerable. If restaurants was a very large table, let's say there's 10,000 restaurants or 1 million or 10 million restaurants in this table, then we're trying to take all the restaurants and work with them in memory through an IEnmuerable, that can cause performance issues. You probably, in a real application, want to use IQueryable here if you're going to have a link statement that could potentially return all restaurants, and I have some more information in some of my link courses about the important differences between IEnumerable and IQueryable. But IEnumerable will work for our purposes. I also need a method that returns a single restaurant. So again, I can go to my context, go to restaurants, we'll use the same FirstOrDefault operator that we used in the InMemory representation, and just say that given a restaurant r, the Id property has to match the incoming Id, and then we need to implement the Add method. There's a few different approaches that we could use to implement this Add method. One thing I will need to do is go up to the context and go to the Restaurants property, and say that I want to add a new restaurant. The entity framework will then start tracking that restaurant so that at some later point, it can insert that restaurant into the database. At what point does the insert occur? The insert will not occur until I invoke SaveChanges on my DbContext. At that point, the entity framework will figure out everything that's been added or updated or deleted, and it will start issuing SQL commands to the database. In our application, I'm going to leave SaveChanges inside of the Add method. In other applications, you might need a different design where you do not immediately call SaveChanges. Instead, you might move the call to save changes out to another method that's defined on the interface; something like Commit, or SaveChanges, or Save; because that gives you the ability to batch together multiple changes to your database inside of a single transaction. We will keep the simple approach of just immediately calling Restaurants.Add, and then invoking SaveChanges, but if you want more information on how to treat your DbContext as a unit of work and the transactional nature of SaveChanges, there's other courses that are on link and design patterns, and the entity framework that you can refer to. For right now, we're going to return Restaurant, and I just want to point out another difference between our InMemory Restaurant data and this SQL restaurant data that's using a DbContext; with the InMemory data, we had to generate the Id for a restaurant. With the entity framework and SQL Server, the Id property of a restaurant will, by default, become an identity column inside of SQL Server, and with identity columns, the value can be auto-generated by SQL Server, by the database. And what the entity framework will do after it inserts with SaveChanges, the framework will automatically grab the generated ID value and place it into my restaurant. So at this point, my restaurant should have a new Id assigned to it, and we will just return the same object that was passed in, but the Id property will be populated. At this point, I will save all my files, and what we'll have to do next is check our configuration, make sure we're pointing to the right SQL Server database, tell our application to start using SQL restaurant data instead of InMemory restaurant data, and we'll need to make a few changes through the application, but we will not need to touch anything that already uses our IRestaurantData service interface. That's the beauty of hiding this functionality behind an interface. We'll be able to swap in the entity framework, and the application will behave just like it did before, only now it will be working with a real database.
Configuring the Entity Framework Services
To use our new DbContext derived class, I need to change the configuration of my application. I'll need to give the DbContext a connection string so that the DbContext knows what server to go to, and which database to work with. Since connection strings change all the time, we'll put the connection string in our JSON configuration file. I also need to add some more services during the configure services method of the startup class, because the entity framework, just like ASP.NET and the MVC framework, it relies on dependency injection, and for injection to work, the runtime needs to know about the various services that the framework uses. Fortunately, there is an easy configuration API that will add the default services that I need. Back in our project, let's swing over to the Startup class, because it is here where I manage all my configuration and my services. What I need to do is make a few changes under ConfigureServices. First of all, instead of using InMemoryRestaurantData, we want to use SqlRestaurantData; and now instead of injecting an InMemoryRestaurantData to my controllers and any other components that need an IRestaurantData, .NET Core will now be injecting a SqlRestaurantData. And I do want to change this back from being a singleton to being scoped to an Http transaction, because a DbContext is not thread safe, so having a singleton DbContext would be very dangerous, and by using AddScoped, I'll be sure that a DbContext is only used on a single thread, or at least a single, logical thread. In order for the DbContext that is behind the scenes of the SqlRestaurantData to work though, I'm going to also need to configure the entity framework services into my application. And the easiest way to do that is to use an extension method that's provided by the entity framework, named AddDbContext. This method takes a generic type parameter, so I would invoke this method once for each DbContext that I might be using in my application; in this case, that DbContext derive class is OdeToFoodDbContext, which will require me to bring in the namespace OdeToFood.Data, and one of the overloads of this method will allow me to configure the options for this DbContext. Now the options themselves can be configured using various methods that are available on this options object. The method that I want to use is a method UseSqlServer. This method, in order to gain access to this, I do need to bring in another namespace: Microsoft.EntityFrameworkCore, that's because this extension method is defined in that different namespace, and what I'm telling ASP.NET Core is I'm going to use this DbContext. Behind the scenes that's going to bring in the entity framework services that I need, and specifically, I'm telling the entity framework I'm going to be using this DbContext derived class, and these are the options that are required to initialize that class. As part of those options, I'm telling the entity framework to use Microsoft Sql Server because the entity framework does support other relational and even non-relational data sources, and the one bit of additional configuration that UseSqlServer will need is a connection string. It's the connection string that tells the entity framework what server to go to and what database to use. Now you might remember, at the beginning of the course, we created appsettings.json. It was inside of this file where we configured a greeting, and by default, when we created this new file with the Visual Studio template, we received a ConnectionStrings setting, and this can contain 0 or more connection strings. This is the name of a connection string; I'm going to change this to a more friendly name: OdeToFood. This connection string says that we're going to connect to the server; (localdb)\MSSQLLocalDB; the database name has not been set yet, it obviously wants to be changed. So let's change the database name to OdeToFood, and then I'll leave the rest of the connection string in place. This piece is telling the entity framework to establish a Trusted_Connection, that is use my Windows identity to connect to the database; that's going to be fine for development purposes, and MultipleActiveResultSets=true is a setting that entity framework requires when it's doing some complicated, nested entity querying. But what I need to do now is to bring this connection string into my application so I can pass this value to use SQL Server. You might remember earlier in the course, we were able to access information in appsettings.json by injecting an IConfiguration service into our configure method. So over in Startup, I'm going to need access to this IConfiguration service. What I cannot do is add that as a parameter to ConfigureServices. This is not what we call an injectable method, I cannot just add additional parameters here like I can with configure, but what I can do is add a constructor to Startup, because the constructor for this class is injectable. In other words, I can ask for services that have already been defined before we got to ConfigureServices, and I can receive those services as parameters. Again, going back to the beginning of the course, inside of Program.cs, when we are creating our web host environment using CreateDefaultBuilder, that will register some services upfront for us, including this configuration service that will use appsettings.json. So inside of Startup.cs, I want to inject IConfiguration, we will call this configuration. Since we'll need this later in the class, I will save configuration into a private field. Let me generate that field using Visual Studio and Ctrl+., and now when I'm telling the entity framework that we should be using SQL Server and here is the connection string, I can go up to configuration and there is an extension method defined for IConfiguration called GetConnectionString. This will make it really easy to pull out connection strings from appsettings.json; all they need to do is live in the ConnectionStrings property. I just need to specify the name of the connection string. We are using the name OdeToFood, and at this point, if I save all my files, I now have the entity framework configured, I'm going to use a specific DbContext with this connection string that's specified in my configuration file, and that DbContext is really going to be wrapped or hidden by my SqlRestaurantData, which is now configured into the application. All we need now is a database to talk to. Let's look at how to use entity framework migrations to create that database for us next.
Entity Framework Migrations
One way to get a database and a database schema set up is to use the entity framework tools from the .NET Command Line Interface, the CLI. This is a two-step process. The first step is to execute a command from the command line: dotnet ef migrations add, and this step will add migration code to my project. Migration code is C# code that I can execute to create a database and a database schema. The entity framework will generate this migration code by looking at an existing database, or if a database doesn't exist, it will start from scratch. Also, looking at my DbContext and my entities, and figuring out what schema changes are required to make the application work. So as I add additional entities or make changes to my existing entities, I can continue to add migrations to my project to make sure my database schema is in sync. I need a second command: dotnet ef database update, to apply any migrations to update a database. I do this once the migration code is in my project. Let me show you how. Let's open up our Command Prompt, and I again want to execute the command dotnet ef to show you that the available commands here are database, dbcontext, and a migration; and each of these commands will have some subcommands. For example, I can run dotnet ef dbcontext list to ask the tools to give me a list of all the DbContext classes I have in my project. This is just a good test to see if things are going to work, and I do see that the ef tools have detected my OdeToFoodDbContext, that's good. I'm going to re-execute this command dotnet ef dbcontext, but this time with the info subcommand to get some additional information about my DbContext, and here I can see the provider name is EntityFrameworkCore.SqlServer, that means we'll be using Microsoft Sql Server, that's correct. The database name is OdeToFood, so notice this command was able to detect the connection string that I'm going to use that knows the database name, it knows the data source, which is localdb; and what's not immediately obvious is that the dotnet ef command will do this by bootstrapping the application, essentially executing the code inside of my startup class so that the services and the injected configuration, all that available information is there to construct an instance on my DbContext class and actually take a look at what's going to happen at runtime. And since these commands worked, I'm fairly confident that if I do a dotnet ef migrations add, I'll be able to use this entity framework tooling to create migrations in my project. Now notice there's all sorts of additional options that I can pass to this particular command. This command is going to generate a new CS file into my project, a C# class, and I can specify options to modify the directory and the namespace to use, as well as a specific context when I'm using multiple DbContexts, and all sorts of other options that I can use in more complex scenarios. But all I need to do is a dotnet ef migrations add. I do need to provide the name of the migration. I'm going to call this my InitialCreate migration, because I am starting from scratch with no database, so this will be everything that I need to initially create the database, and I'm going to throw in -v to add some verbose output, and I can see as I scroll through the output, that this tool wrote a migration to an InitialCreate.cs file, there is a date prefix in front of this file. I can also see how to remove a migration if I might've made a mistake, but this action is done and complete, and now if I come over to my project, I will see there is a new migrations folder, and inside of this migration folder, there's two files; one is a ModelSnapshot. This is how the entity framework tools keep track of what your entities look like, what your database models look like, and then there is an initial create file. Inside of this file, we can see code that is written against a migration builder API that essentially allows me to use SQL DDL statements from C# code. So SQL data definition language. The language that you would use to create tables, and columns, and indexes. What the entity framework did was look at my data context derived class, see that it has a property, DbSet of restaurant, so it wants to create a table named Restaurants. The columns in this table were derived from the properties that are available on that restaurant entity, and I can see there's an Id column, a Cuisine column, a Name column. Notice that the Name column will be a type in SQL Server nvarchar 80, so at a maximum length of 80 characters, where did the 80 come from? Well the entity framework does respect these data annotations that we also use for validation to the entity framework saw that this Name property is required and it will have a maximum length of 80 characters, and that 80 characters will also be enforced at the database. The entity framework also took the Id column of a restaurant and made that a primary key, so the values have to be unique and this SqlServer:ValueGenerationStrategy, that is a fancy way of behind the scenes saying that I want this to be an identity column, that is, the values are automatically generated by SQL Server each time I insert a new record. And there is an API here that I can use if I want to add additional code. So for example, I can go up to migrationBuilder, and there is a SQL command. If I wanted to execute arbitrary SQL statements, maybe to do some sort of data insert or add some sort of special constraint, there's also a method available to create an index if I need additional indexes on a table because of the way that I search and query that table. But I'm just going to leave the default code inside of this migration, and now, let me open up the SQL Server Object Explorer again. Currently, if I look at the list of databases available in my localdb, I can see there's no databases of any significance, so let's come back to the command line, and this time I want to execute dotnet ef database, first let's pass in the help parameter. I can see there's a couple different commands here: drop and update; what I want to do is dotnet ef database update. Again, we'll add -v for some more verbosity. What dotnet ef database update will do is using my current DbContext and its connection string, examine the database that we're pointed to, examine all the migrations that are available, and then apply any migrations to that database that were not previously there; and I can actually see some of the DDL statements roll by. So I can see, for example, a little further up here there should be a command executed to create the database OdeToFood. The entity framework's also going to create a table: EFMigrationsHistory, with a few underscores in front. That's a table that the entity framework is going to use for bookkeeping and to keep the migrations in sync; and if I scroll down a little bit, we'll also see where we execute the command to create the table Restaurants. And so if I come back to my list of databases and refresh, I now see my OdeToFood DB, there is my Restaurants table. My Restaurants table has columns to store the Id, the Cuisine, and the Name, and at this point, we are all set up to start using the application and make sure that it works against this new SQL Server.
Up and Running
Now we're ready to run the application, and the beautiful thing is although we had to do a lot of work to set up our migrations and get started with the entity framework since we started from scratch, now we're in a position where I can easily add new entities, add new migrations, update my database, and everything'll work, and I don't need to change any of my existing application code. My HomeController, when it is listing all the available restaurants or creating a new restaurant, it's going to work through IRestaurantData, which is now ultimately backed by an entity framework DbContext. So before I run the application, let's actually open up SQL Server Object Explorer again. What I want to do is right-click on the Restaurants table and select View Data. From here, I can see the existing data in my table, which of course we don't have any. It might be nice to start the application with some initial data. So I can also use this interface to enter new data into SQL Server. Let's create a restaurant with a cuisine of 0, that would be a cuisine of none, and the name LJ's and the Kat. Let's create another restaurant. Let's go back to Tersiguels, and you'll notice as I enter this data, SQL Server is automatically generating identity values for me, so Id of one, Id of two. Now let's start the application running without debugging, and if I come over to my browser, I can see the two records that I have stored in SQL Server. That's a good sign. Let's also try to add a new restaurant. Let's add back Scott's Pizza Place. I'll select a cuisine of Italian, click Save, that appears to have worked. If I go to the homepage, all three restaurants are listed, and if I come back and click the refresh button in my data browsing window, I will now see indeed we have three restaurants in the database. So now that we have a database hooked up, we can start to look at more features of the MVC framework and razor views, and build out some more functionality into this application in the coming modules.
Summary
In this module, we saw how to work with the database using the entity framework. We verified installation of the entity framework NuGet package, specifically the package allowing ef to work with SQL Server. Then we wrote a class derived from DbContext. Once we configured the entity framework and the DbContext into our application services, we created migrations, and used those migrations to create a database. Now let's turn our attention back to the ASP.NET MVC framework, and see how to work with some more features of the razor view engine.
Razor Views
Introduction
Hi, this is Scott, and in this module we will be focused on features of the Razor view engine. Although we've already been using Razor to render HTML with our model data, we can make some improvements to the application using some features we haven't looked at yet and some features that are new for ASP.NET Core. The first feature we will look at is a feature to give us more consistent structure across the application, and this feature is the layout view feature.
Layout Views
Most websites and web applications will want to create pages that present some common elements. You typically have a top area on every page where you display a logo and a navigational menu, then you might have a sidebar with additional links and information, and probably a footer at the bottom of the page with some small print. Every page of the application should have these common pieces, but we don't want to duplicate these pieces in every view that we write, and this is where layout views are helpful. A layout view is a Razor view with CSHTML extension, just like the other Razor views, and you can name this layout view whatever you like. I'll be using a layout view with the name _Layout.cshtml. That's a common name for a layout view. And the leading underscore isn't required. That's just a convention that many developers follow to identify a view that is not a view that you render as a view result from a controller action. Instead, it's a special kind of view. Once we have a layout view, we can set up our controller views, like the index view for the home page. We can set up this view to render inside of the layout view at a specific location. With a layout view in place, Index.cshtml doesn't need to know about the logo or the top-level navigation menu. The index view only needs to render the specific content for the model the controller action gives this view. The layout view takes care of everything else. Let's try it out. In our project, we currently have three views that can render from the home controller, and two of these views, Details and Create, only render an HTML fragment. That is they don't render with a body tag and an HTML tag and a head tag. Only the Index view does that. But I want to be able to use a layout view so that all of my views in the application will render a full HTML payload, and I want to do that without adding HTML and head and body tags to the other views. So I'm going to create a new folder in the Views folder, and the name of this folder will be Shared. Earlier in the course, we saw that when the MVC framework goes looking for a view it will always look in the folder that has the same name as the current controller that we're rendering from, like the Home folder for the home controller, but it will also always look at the Shared folder. And any view and CSHTML file that we place into the Shared folder is available throughout the application, and this is where I want to create my layout view because it's not associated with a specific controller. I want to be able to use it from all sorts of different controllers. So let's Add a New Item, and this time I'm going to do a search for layout. The option I want is the MVC View Layout Page. The default name here, _Layout.cshtml, that will work for me. You can have multiple layout views if you want, and you can even change the name, but we'll stick with the default. And what that new item template will produce is a Layout.cshtml file that has the html, the head tag, body tag. It includes a DOCTYPE. This is a Razor view just like the Razor views that we have been working with, so I can include C# expressions in here. For example, if I wanted to write out the current year, I could write the expression @DateTime.Now.Year. Razor will evaluate that expression and produce some output inside of that div. What's special about a layout view though is this call to a method RenderBody. You need to have one call to RenderBody inside of a layout view, and it is this location where the framework will render your content view. In other words, if my home controller says please render the index view and my index view says it's going to use a layout page of Layout.cshtml, then the framework will use this as a template to produce the HTML response, and it's going to take everything that the index view renders and place it into this div where I have the call to RenderBody. And that means over in my Index view I can remove everything like the closing html tag and the closing body tag, and up here I can remove the opening body tag and the html tag. I do want to call your attention to the title though. If I wanted the title of this page to be Home Page, but I remove this information from my index view because it's going to be rendered by the layout view, the question is how will I be able to set the title? Let's look at that too. I'm going to first press Ctrl+K, Ctrl+D to reformat my view, remove the unnecessary space that was on the left-hand side, and the other expression I want to call your attention to inside of Layout.cshtml is this expression that renders ViewBag.Title inside of a title tag. ViewBag is a dynamic data structure, meaning I can add any property that I want onto the ViewBag, and it's common that we use the ViewBag to carry information between views. A minimal amount of information, but it is possible for me inside of my Index view to set a ViewBag.Title property and then have the layout view render those because the layout view is going to be used for every view in the application. I cannot hardcode a title here, so somehow my content views have to be able to specify the title for a particular page. And in this case, what I can do is inside of Index.cshtml I can create a C# code block, and inside of here I could say let's set the ViewBag.Title to Home page, and I just need to fix up my formatting here so this becomes a proper code block. But this piece of code will execute before the layout view and set this title property on the ViewBag so that when the layout view renders, my page will have a proper title. The other step that I need to take is to actually set a property that I inherit in this view, which is the Layout property, and this Layout property can specify the name of the layout page that I want to use. I could have some logic here to switch between two different layout pages if I wanted to depending on the user profile. Perhaps you use a different layout for anonymous users versus authenticated users, or spring versus summer, but what I can do here is using the tilde, which means go to the root of the website, I want to specify the file Views/Shared/_Layout.cshtml. This will set the layout view just for my index view, but I'll show you just a little bit later how we can set the layout view, the default layout view, for all the views in the application. At this point, I just want to save all of my files, and let's press Ctrl+F5 to run without the debugger and take a look at the application. This opened in a new browser window. Let's me bump it over here to the side of the screen, and I know I'm currently using the layout view properly. I can see the year here appear at the bottom of the page. So now all my index view has to worry about is rendering just the bit of the user interface that is required for the Home page, and all of the other common areas that I might have, the header, the footer, the sidebars, those can call be handled by this layout view. The content views will appear wherever I invoke RenderBody. There's another method I can invoke if I need a little more flexibility. Let's actually put an actual footer element here and make a call to RenderSection. What RenderSection allows me to do is give my content views a little more flexibility in where their output appears. So the primary output for a content view like Index.cshtml, all of this markup will appear where I have a call to RenderBody, but I can also define additional sections inside of a layout view that allow a content view to plug in content in other places inside the structure of the layout view. I do that by invoking RenderSection and say here's an opportunity to render a footer section. And the second parameter here is a parameter that tells the framework if this section is required or not. So if I set this section required to true, my content view must render a footer section; otherwise, there's an exception. I can also set this to false to tell the framework this section is optional. If I want to render into this section, all I need to do inside of my content view is to use an @section directive, give my section a name, and now the framework knows that any markup that I place inside of here will need to be placed into a section that is rendered for my layout view with the same name, so into footer. And to see this work, let's actually change our h1 around and move this current message into the footer. So I'm going to cut that expression, give this page an h1 that just says All Restaurants, and down here inside of the footer I will paste @Model.CurrentMessage. So we will now render that at the bottom of the page inside the footer section that the layout view defines. So let me save all the files, we can come back to our web browser, and I now have that message appear in the bottom footer. So now that we have a layout page in place, I want to be able to use this layout page from my other views that are defined in the application without going into every view and specifying the layout page. Let's see how to do that next.
_ViewStart
Before the Razor view engine will render any content view, the engine will go and look for a special file with the name of _ViewStart.cshtml. And if the engine finds this file, the engine will execute the code inside of ViewStart before executing the code inside of any view. Because this file always executes, you can use it to remove duplicate code from the code blocks inside of your other views. For example, if we want every view to use our layout view that we just created, I can place code to set the layout view inside of ViewStart instead of having that code inside of every view. Let's see how this works. Inside the project, I want every view to use the layout view, so I'm going to cut this code from Index.cshtml, let me right-click on the Views folder, I want to Add a New Item, and this time I want to search and find the MVC View Start Page. The name must be _ViewStart.cshtml, and it's important that I place this into the Views folder if I want to impact every view in the application. But the framework will use your folder hierarchy to find this ViewStart file. So if I took this ViewStart file and placed it in the Home folder instead of the Views folder, any code that I place inside of ViewStart would only execute for the views that are in the Home folder. But I will place this in the Views folder so it has an impact everywhere and paste in the code to set the Layout property for every view. We simply want to set the default Layout property for every view. And I can still override this. I can override this by creating a separate ViewStart file just in the Home folder and have a different layout view for those particular views. I can also still come into a specific view, and let me paste this code back in here again. I could also say for this particular view I want a layout file Layout2.cshtml. I can even set the Layout to null if I want to tell the framework well this particular view should not use a layout page. Just render it as is. Now that's not what I want, so let's remove that line, let me save all of my files, and I also want to fix up the Create and Details view. Currently, they will render without a title. So let me go ahead and place a code block here that will set ViewBag.Title for the Create view to create. Save that file, and let's also come to the Details view, and for the Details view, let's set ViewBag.Title to the name of the restaurant, so @Model.Name. I didn't need the at sign there since I'm already inside of a code block. And now saving everything and coming back to the browser and doing a refresh, I should be able to go to the details view and see the restaurant name up here, not only on the page, but also in the title for the page. Now there's one more special view with the Razor view engine; that's the ViewImports file. We already placed this file into the project earlier in the course, but let's talk a little more about it.
_ViewImports
At runtime when the Razor view engine needs to render a view, it needs to take all of the markup, and the C# expressions, and the C# code blocks and generate code that the engine can then compile and execute to produce the right output. And by default, when the engine compiles this code, there are only certain default namespaces that are in effect. Any custom type that I write, like Restaurant, the compiler will need to know what namespace this comes from, so I have to specify OdeToFood.Models.Restaurant. I either need to take that approach or, as we did in Create.cshtml earlier, I need to add a using statement to my view so I can specify types like Restaurant and CuisineType without the namespace. Another alternative is to use the magic ViewImports file. So just like _ViewStart, this view has a special name that the Razor view engine will look for. It is hierarchical, so by placing ViewImports into the Views folder itself, I'm influencing all the views in my application. And one of the special instructions that I can place inside of ViewImports is this addTagHelper directive. This gives me access to the tag helpers that we've been using, tag helpers like asp-for and asp-action. I can also place using statements inside of this file, and these using statements will then make a namespace available for all of the views in all of the subfolders. So if I add OdeToFood.Models, and let's go ahead and add OdeToFood.ViewModels, after I save this file, I no longer need to specify these namespaces in my individual views. So instead of OdeToFood.Models.Restaurant, I can just say the model is the Restaurant type, and I could go through my other views at this point and remove using statements and remove namespaces, but I think you get the idea. Any namespaces that you add a using for inside of ViewImports, now the types inside of those namespaces will be available by default when the Razor view engine compiles these files.
Razor Pages
A new feature in ASP.NET Core MVC version 2 is the Razor Pages feature. If you are building an application to primarily generate HTML, as opposed to a service that generally creates XML or JSON, then Razor Pages are an interesting new feature you should look into because Razor Pages are an alternative to the MVC pattern for generating HTML. With MVC, we have controllers that receive requests, that build a model, and select a view from the Views folder for rendering with that model. Razor Pages are a bit simpler. A request can go directly to a Razor Page that will live in the Pages folder by default, and this page, which is a Razor file with a CSHTML extension, this page can also build a model and render HTML while still maintaining a separation of concerns and using ASP.NET Core features like dependency injection. For me, moving forward with 2.0, MVC controllers are the best solution for building services, that is web services, RESTful services, and HTTP-based services that render XML, JSON, and other data formats for other programs and services to consume, while Razor pages will be my favorite mechanism for creating HTML. Let's see how Razor Pages work. A Razor Page will live in a Pages folder by default. Currently, we don't have a Pages folder, so let me Add a New Folder named Pages, and then inside of this folder I want to Add a New Item, and I'm going to select Razor Page. Now the name of the Razor Page is significant. When we're using Model-View-Controller and we render a view as the result of a controller action, we have to make sure that the view name matches the name of the view that we're rendering from a controller action, and that controller action can specify any name. But with Razor Pages, the name of the page by default will be a part of the URL because the MVC framework will look at a incoming request and see if the folder and the name in that URL maps to a corresponding Razor Page. In other words, if I create a .cshtml file with the name of Greeting, and I place that into the Pages folder, by default the MVC framework will look at a request to my server for /greeting and look for a greeting.cshtml file in this Pages folder. I can also have subfolders. For right now, let's take all of the code out of here except for the @page directive because it is this @page directive that makes the CSHTML file a Razor Page, and let's just add some markup, like place in the string Hello, save this file, come back to my browser, and go to /greeting, and that will render the string Hello. And just like other CSHTML files that I have, I can have a mix of HTML and C# code and C# expressions. So I could also output DateTime.Now.Year, save this file, and we should see the value 2017. So now instead of routing a request through a controller, I can send a request directly to a CSHTML file that is in the Pages folder, or a subfolder of that folder, and if all I have to do is put together some simple HTML, it's a very easy way to respond to that particular request. However, there's many more features here. For one, Razor Pages can also make use of ViewImports and ViewStart. In fact, let me make a copy of these files, and I'm going to copy them directly into my Pages folder and just fix up a few things. So first of all, inside of ViewImports, I want to be able to use TagHelpers for my Razor Pages. That's possible. I also want OdeToFood.Models, and ViewModels to be using statements by default so I don't have to provide the full namespace for types in those namespaces. I'm also going to add Pages here, and I'm going to add Services, Services because I want to show you how to work with services directly from a page, and also OdeToFood.Pages because we will have some C# code inside of the Pages folder. So it's in the namespace OdeToFood.Pages, and I want to use some of those types from a page without specifying the namespace. Let's say that I wanted to get the current message of the day from my IGreeter service. That's a very simple operation, and here's one way to do that. I can use and inject directive. I can use an inject directive in a Razor Page and also in a Razor view, and what the inject directive allows me to do is ask for a service from the service collection that's been registered with ASP.NET Core, all of those services that we registered in Startup.cs. Specifically, I want the service that is an IGreeter, and I'm going to be creating a property named Greeter that I can now access throughout the rest of the page. Now sometimes Visual Studio still gets a little bit confused with IntelliSense, so I'm going to close my Razor Page and reopen it, and hopefully some of the red squigglies will go away, which they did. I do have a red squiggly for IGreeter still, but I think that's because I didn't save ViewImports, which brings OdeToFood.Services into play. Now Visual Studio recognizes IGreeter, and I should be able to write a C# expression that is Greeter. That's the property I created of type IGreeter, and I want to tell the Greeter to get the message of the day, and we will just output that string inside of this div. At runtime when MVC instantiates this page, it will find the IGreeter service and set this property and make this available for me. So if I save this file and refresh, we now have the current greeting. The ViewStart page is also significant for Razor Pages. I can specify a Layout view for all of my Razor Pages to use. And in this case, I want the layout view that will be in the Pages folder itself. So let me copy the Layout view that is inside of the Shared folder and paste this into the Pages folder. We're not going to need to make any changes to that Layout file, but if I just make sure that all of my files are saved and refresh localhost/greeting, we will now be rendering inside of a full HTML tag, which is good. And I can use sections and all those other features that I described in the layout page. Is it good idea to have two copies of the same layout page? Not exactly. If you do decide to start rendering HTML with Razor Pages, you'll typically render all of your HTML with Razor Pages, and you won't have this mix of some things are rendered by controllers and some things by Pages. Since I'm trying to demonstrate different features of ASP.NET Core, we're going to have a mix in this project, and we're going to have two Layout pages, two ViewImports, and two ViewStarts. But now, back inside of Greeting.cshtml, one way to have access to this greeter is to simply inject an IGreeter service and save it in a property. For many scenarios, this becomes a little bit dangerous because you might end up doing too many things inside of your Razor Page. And that's where it is useful to specify a model for your page. So we're going to implement the same feature and display the current greeting, but this time using a model. And a model for a page is a little bit different than the model that we used for MVC controllers. In an MVC controller action, I can build a model, which can be of any type, and I pass that model to my view. With Razor Pages, my model is the code-behind class that lives behind this Razor Page, and it derives from a base class of PageModel. So GreetingModel will become a class that is instantiated and is available from Greeting.cshtml. I just need to specify a model of GreetingModel. And now, anything that I make available on this type on an instance of this class is available inside of my Razor Page. In other words, let's go back into the code-behind, and just like with a controller, what I want to do is add a constructor that will allow me to inject an IGreeter service. We'll call this greeter, and I do need to bring in the namespace OdeToFood.Services. We can then save that greeting service off into a private field, which I'll generate with Ctrl+period, and I now have access to this private field throughout the rest of the class. So what is this method OnGet? When this Razor Page receives an HTTP Get request, the MVC framework will instantiate this model, inject any services that are required, and then invoke the OnGet method. Inside of the OnGet method, I can put together all of the information that I need for the view to satisfy this request. So very similar to a controller action. But in this case, instead of building a model to pass to a view, my class represents the model. In other words, let's add a public property here of type string called CurrentGreeting, and I'll populate this property on an HTTP Get request. So CurrentGreeting = dear greeter service please get the message of the day, and now what I have is a little bit of a separation of concerns because it's really my model that has to understand how to talk to different services to grab the information that the view needs to render into the HTML. And this class becomes a class that I can instantiate and execute inside of unit tests, and it's going to take care of any additional heavy logic and service orchestration that I need so that a view again becomes just a simple place that just needs to render information. So after I save this file inside of the view, I can still go to my Model property and say I want to render the CurrentGreeting. Save this file, come back to the browser, I'll still have the same results, but things are a little bit simpler compared to building an entire controller just to satisfy this one little page that needs to display a greeting. And just like with controller actions, I can have parameters to my page that the MVC framework will try to satisfy by looking through the HTTP request. So it could look in the query string, it can look in route data, it could look in posted form values, and just like with route attributes that we looked at earlier in the course, I can say that this page expects a name parameter to arrive in the HTTP request. And since I don't have a question mark here, this name parameter is not optional, and that means if I save this file with the new @page directive that says I need to find a name in the URL and try to refresh, now we'll get a 404 Not found because the MVC framework will need to see /greeting/ some parameter that will be treated as a name, like Scott, and now I have A development Hello. That parameter I can receive as a parameter to my OnGet method, and then I could copy this name into a public parameter that is available for rendering from the view. I could also do something like use string interpolation here to include that name in the message and say name: and then greeter.GetMessageOfTheDay. Then make sure I save all of my files and refresh, and I now have the ability to pass a name as part of the data that is inside of my URL. And at this point, I could go back and implement other features on my application using Razor Pages. For example, I could have a list all restaurants model. That model could take a DbContext as a parameter to the constructor, our movie data source, or rather our movie data service, and could use that service during a Get request to retrieve a list of all the restaurants, make those restaurants available as a property that I can loop through in the Razor Page to display all the restaurants just like we currently have on the Home page. I will leave that as an exercise for you to try, implement the Home page using a Razor Page, but what I'm going to do next is look at adding an additional feature, which is provide the ability to edit an existing restaurant.
An Edit Form
Let's add the ability to edit an existing restaurant, and we'll do that using Razor Pages. There's not a significant difference between using Model-View-Controller to edit a restaurant and using a Razor Page to edit a restaurant, but I want to show you the Razor Page approach. For many things that you do in an application that generates HTML, Razor Pages has a tendency to keep all of your abstractions inside of the same place. It makes it a little bit easier to find things and to work on things. So let me show you what I mean. Let's add a folder to the Pages folder called Restaurants because it's inside of this folder where I can add all the pages that will work with restaurant data, for example, editing a restaurant. And inside of here, let's Add a New Item, which is a Razor Page, and we'll simple call this Edit.cshtml. So knowing what we know now, in order to reach this Razor Page, I would go to the website and go to /restaurant/edit to reach this Razor Page. We omit the file extension. And I also know that in order to edit a restaurant, I will need an id parameter, the ID of the restaurant that I want to edit. So using the syntax, this will require an ID at the end of the URL, so I'll need to go to /restaurant/edit/ and then an ID. Let me close this file and allow Visual Studio to reprocess that. And in the meantime, let me open up the Home Index view, which is the view that displays the list of restaurants. What I want to do is add another link so the user can click to reach that edit page. So let's add an anchor tag, and this time instead of asp-action, instead of that tag helper, I'm going to use the asp-page tag helper. I want this tag helper that will generate a link that will go to the Restaurants folder, find the Edit page, and just like we did with asp-action, I need to pass along some additional parameters, specifically the ID that's going to go into the route, and it will have the value restaurant.Id, and we will display the text Edit. All I need to do now is save this file, refresh this page, and I now have a link that I can click on to go to /restaurant/edit/2 for Tersiguels. Perfect. Let's come back into our Edit view, and one thing you'll notice is that EditModel, currently displaying with a red squiggly line, and that's because this EditModel is really in the namespace OdeToFood.Pages.Restaurants. I'm going to copy that namespace, add a using statement here, and say bring me in everything from OdeToFood.Pages.Restaurants. And now what we're about to see is that Razor Page is not significantly different than working with ASP.NET MVC. It's just where your abstractions are located. For example, inside of the code-behind, what I'm going to need to do is respond to a Get request and display a form where we will present information about the restaurant and allow the user to edit that restaurant, and that means I'm going to have to retrieve the restaurant from the database to display the initial name and cuisine type. So let's add a constructor to our PageModel and take an IRestaurantData service. I'll call this restaurantData, I will need to bring in the namespace OdeToFood.Services, and let's save that incoming parameter into a field called restaurantData. I'll use Visual Studio to generate that field, and now I can implement the OnGet method. Remember with MVC controller actions you can take parameters that the framework will satisfy by looking around in the request. So I'm going to ask for the id parameter that should be in the route or the URL for this page. And using that id parameter, I can go up to restaurantData and tell it to Get the restaurant with that particular ID. Now where do I place the restaurant information? Again, with MVC, you build a model and pass that model to the view, But in this case, my class is the model, and typically what I want to do is just expose fields or properties that make model information available to my view. So let me add a property to my Edit model, it is of type Restaurant, which means I'll need to bring in the namespace OdeToFood.Models, and we will give this property the name Restaurant. So inside of OnGet, I can say Restaurant = restaurantData.Get something by an id. Now you might remember earlier in the course when we were implementing the details method of the home controller we were trying to find a restaurant by ID, and we had to handle that situation where somehow some way we received an ID that we didn't find in the database. And in that case, our restaurantData service will return null. And again, this is a situation where I could implement my edit Razor Page in a way that it can avoid no reference problems, or I can check for this inside of my PageModel method OnGet and take action here. So I can check to see if the Restaurant is null, and then what do I do? Well it turns out that PageModel methods can also return an IAction result, and if I return an action result that tells the framework to redirect somewhere else, the framework will obey that instruction. So I can check to see if the Restaurant is null, and just like in our Details action, I can return a Redirect action. In this case, I want to redirect to an action. I want to go to the Index action of the Home controller because the user has just requested a restaurant that doesn't exist, but send them back to the list of all restaurants; otherwise, I still need to return something that represents everything is normal, and in that case, I can return a Page result that is built for me from a Page method I inherit from my base class. This is similar to saying return view. It's a way of saying go out and render this view that is associated with this model, in this case, Edit.cshtml. So this is our OnGet method. Let's see if we can display restaurant information in a form. What I'm going to do to save some of the time that I would spend otherwise typing, I'm going to come over to our Create view. And Create and Edit are very similar. I need to display the same information. So let me just copy everything, all the markup that is in Create.cshtml, bring it to my Edit view, and paste it in. I will change a few things around. So this is going to be editing, and now that we have a model, I should be able to say Model.Restaurant, go to that property that I exposed, we're editing this particular Name, and then I'll just need to change around a few things here inside of the form. Since I now need to go to the Restaurant property to get to the Name property, I'm going to have to add Restaurant.Name here, and I'll just need to copy Restaurant., and let me paste that in front of Name, as well as Cuisine so that we display and accept all the appropriate information in our form. And then there's actually one additional little bit of information that I need to include in this form that I didn't need to worry about when we were creating something from scratch, and that is I probably want to save the ID of the restaurant that we are editing inside of this form data. The way I can do that is just specifying input that is for Restaurant.Id. By default this will display a textbox that the user can edit. That's necessarily what I want, so I will override the type of this input to be hidden. Let's save this file, go out to the browser, I want to go to edit a specific restaurant, and I can see that information displays well. What happens when I click the Save button? Well by default, my form will issue a post back to the URL that it came from, but currently I don't have a handler in my page to handle a post method. So just like in the home controller we had the create method that responded to a Get and a create method that responded to a Post, here I need a method that responds to a Get request, as well as a method that returns an IActionResult that responds to a Post request. Now in this Post method I could accept a parameter that contains my new restaurant information just like I did inside of the create method that took a form post to create a restaurant, but it turns out I already have restaurant information as a property, and with MVC I can add a simple attribute here, BindProperty, to tell the framework to take information arriving in the incoming request, and I'm allowing that information to be bound to this property. So when the form posts a value for Restaurant.Name, that string value can arrive in the Name property of the Restaurant here. But just like in my MVC controller action, and we'll see a lot of similarities between these two, I need to check if ModelState.IsValid. If ModelState is valid, I can go about updating the Restaurant. If ModelState is not valid, I need to re-render the form and allow the user to fix any errors. I can write nearly the same code here. So I could say if ModelState.IsValid. Remember we have data annotations on our Restaurant to do some checks like make sure the Name is populated. I can say if it is valid we'll do some work; otherwise, what can we do? Again, return Page to re-render the form and allow the user to fix any errors. So let's just try that real quick. I'm going to refresh this form, let's try to blank out the name and click Save, and I do get the Restaurant Name field is required. So we could make the UI a lot prettier, but I do know that the validation works. So what happens when the value here is valid, I just want to save and update the Cuisine or the Restaurant Name. In that case, I'm going to need some new features on my restaurantData service because currently it does not have the ability to update any data. So let's come to the interface definition first, IRestaurantData. I'm going to define a new method that returns a Restaurant, it's called Update, and it takes new restaurant data, or we could just call this updated restaurant, or just restaurant. Save this file. I'm going to need to come over to InMemoryRestaurantData, and for right now, since we're not using this class, I'm going to comment this class out so I don't have to update this. But an update for InMemoryRestaurantData would just be copying data over. Here in SqlRestaurantData, let me use Ctrl+period in Visual Studio to implement this method. It should appear down here at the bottom of the class, which it does, and here's how we can use the DbContext to update a restaurant. What I do not want to do is use context.Restaurants.Add. That's telling the Entity Framework that this is a brand-new entity that it needs to insert. Instead, what I want to do is walk up to the context and tell it to Attach this information about a restaurant that should already exist, and then I want to set the state of that particular entity, this restaurant, to EntityState.Modified. And that enum EntityState is in Microsoft.EntityFrameworkCore. Could add a using statement for that. We'll leave that code as is. I'll just bring it down here on another line so we can try to keep everything on the screen. So actually, I will just go ahead and use Ctrl+period to add that using statement Microsoft.EntityFrameworkCore. And this is a way of telling the Entity Framework this is a restaurant you should already know about, it should already be in the database, but I'm attaching it to this context to manage, and I'm telling you that everything is modified, so you'll need to issue an Update statement. And then we can go to the context and again invoke SaveChanges, which might update some generated fields in that restaurant, so let's return that restaurant. So now our data service can update a restaurant, we now have an Edit page that will receive a post where the restaurant information is stored in this property for us, so I should be able to go to my restaurantData, tell it that I want to Update this Restaurant. The Entity Framework will use the ID property on that restaurant to update the appropriate record in the database. And if this works, again, just like we did in the home controller after we successfully saved a restaurant into the database, we want to redirect to an action. We don't want to sit on this page and display HTML that was the result of a Post request. We want to redirect to an action. This time we want to go to the Details action of the Home controller, and I need to pass along the ID of the restaurant that we want to see the details for, so a new anonymous type where id = Restaurant.Id. And now what we have is code that is not too significantly different from what we would write inside of an MVC controller action, but now this code is a little bit closer to our view. Physically it's in the same folder, so it's a little bit easier to work on this feature, and let's go ahead and try it out. Let me save all the files, come out; I want to refresh this form. Tersiguels should still be French when this refreshes from the database. Now let's try to change the Cuisine to German, call this Tersiguels 2, click Save, and it looks like those changes were successfully placed into the database.
Partial Views
Two other types of components that you can use with the MVC framework include partial views and view components. Let's talk about partial views first. As the name implies, a partial view, which is just another Razor file with a .cshtml extension, a partial view is a view that renders just a part of a full view. There's really two use cases for this scenario. One use for partial views is when you have some view code that you want to reuse in the application. Here's an example. Let's say you have a layout view, and the layout view is selected by an index view that renders in the body of that layout view. The index view receives from the controller a model object that is a list of restaurants, just like we have in our application. And now the index view has to display summary information about each restaurant, and this restaurant summary might require a block of Razor code that also appears in other views. If that's the case, I can take the common markup in C# code and place that into its own view. Let's call it _Summary. And then I can render that partial view from the index view using an HTML helper named Partial. With Partial, I pass the name of a view and optionally a model object for the partial view to work with. So you can use partial views to take a complex model object and break down the rendering into smaller views. But the key point here is that when using Html.Partial to render a partial view, the partial view can only rely on model data from the parent view. The partial view cannot go out and get an independent model, or at least it should not go out and get an independent model. If you need a different model for your partial view, this is where view components come into play. Imagine I have a layout page that is again selected by the index view, and the index view has a model, which is a list of restaurants. But my layout view also needs to display some advertisements about restaurants using information from the database. I don't want the index view to provide the advertisement data since the index view is only concerned with the restaurants themselves. This is where I can build a view component and render that view component anywhere on the page. I could use the component from the index view or the layout view or another view. And the wonderful part about view components is that they are separate objects which can be instantiated and perform their own data access, build their own model objects, and render their own partial views. It's like a mini MVC request. So unlike Html.Partial, a view component doesn't rely on the parent for any information or context. So let's first use a partial view in our application and then create a view component in the next clip. Back in the application, I have used tables so many times over the years to display information that I now actively look for ways to avoid tables. So instead of rendering our list of restaurants in a table, let's render each restaurant into an individual section that I'll render with a partial view. So I will still need a foreach statement to loop over each restaurant that I have in my collection of restaurants, but now instead of using a table tag, I'm going to use Html.Partial and tell the framework that I want to render a partial view _Summary, and the second parameter here is the model information that I can pass to that partial view, so I will pass an individual restaurant. And now I just need to create this partial view _Summary. I could place that into the Shared folder, that would make the partial view available to views throughout the Views folder, or I could place it in the Home folder and just use it with the views that are rendered by the home controller. That's where I'm going to place this view. So a New Item, an MVC View Page. I want this to be named _Summary.cshtml. Let's delete everything that's in here. My model will be an individual Restaurant, and the restaurant I want to render information inside of an HTML section. So first let's provide a heading where I can go and render the restaurant Name. So again, nothing different from regular Razor views. I receive a model object that I can access through the Model property. I can also now display the Cuisine, so I could save that as @Model.Cuisine, and then I should still display some links that will allow the user to get to more detailed information, as well as edit this restaurant. So let me come back to Index.cshtml, and let's copy out these two lines of HTML that already have the links built for me, and I will paste them into the Summary view. There's just one bit of information I need to change. I no longer have a local variable named restaurant. Instead, I can go to the Model property itself to pull out the Id value that I need. And that's all I need in this partial view. Back in the Index view, I can now remove the table that I was previously using, now we'll just render these sections, and I think I also want to surround this anchor tag with a div. I could do that from the context menu here. That will make this Create link a little bit easier to style in the future. And now that I have all the files saved, let's go back and refresh and see what the Home page looks like, and I know have each restaurant in its own section. And later, when I have a designer come along and apply some CSS classes and styles, we could make each of these sections a little bit fancier, have them offset a little bit, maybe a little shading. It can look really good. But the purpose here, the overall idea, was I now have an index view that takes a relatively complex object. It's a complex model that includes a list of entities, as well as a current message, but the view itself remains relatively simple. I delegate most of the work of displaying individual restaurant information to a partial view called Summary. Now I could also do something very similar with a view component, which we'll look at next, but a view component can be in many ways even more powerful because a partial view's always going to need to rely on model information that I provide that view. So it has to be information that is available to this parent view. A view component, as we're about to see, is almost like a child controller. It can go out and obtain its own data access components, build its own model, and render its own partial view. Let's look at that next.
View Components
What we want to look at next is how to build a view component. Again, a view component is a reusable piece of UI that you can use throughout the application. You can use it from partial views, content views, layout views, Razor Pages, from any other Razor view. And the powerful feature of a view component is that a view component is very encapsulated, and it's like a mini MVC controller. A view component can inject its own services, can do its own data access, build its own model, render from one of its own set of views, and it can do all this without any knowledge of what the parent view is or the parent controller. So let's use this scenario. Let's say that the current message that we display at the bottom of the Home page, let's say I want to take that message and display it on every page, including restaurant details. And if that's the case, I no longer want this markup inside of Index.cshtml. Ideally, I would place this into my Layout view so this information can display everywhere. But getting that current message of the day in a layout view can be a bit problematic. There's no way, for example, that I can really specify a model and use this approach easily because this layout view is rendered using all sorts of different controller actions and other views that require different types of models. I could also use @inject to inject the IGreeter service, and that's one relatively simple way to get access to the current message and display it here, just like I demonstrated earlier in a Razor Page. But let's try to forget about that approach and build something that's a little more encapsulated and reusable. What I'm going to use is a view component. And we'll see there's a lot of similarities between a view component and a controller. It's just that a controller I can reach with a URL, whereas a view component is something that I just use internally inside the application. I'm going to add a new folder to my project called ViewComponents, and inside of this folder I want to add a new class because a view component is a class that renders a Razor view, just like a controller is a class that has actions that render a Razor view. So I will call this the GreeterViewComponent. And that's a naming convention the framework follows. Just like the home controller is what we would call the home, space, controller, this is the Greeter. And if I ask the MVC framework for a view component named Greeter, it will go off looking for a class GreeterViewComponent. I can derive from a base class ViewComponent. That is in the Microsoft.AspNetCore.Mvc namespace. And now again, just like I would do with a controller, if I need access to that IGreeter service to get the current message of the day, or I need access to the restaurant database service, then I can define a constructor that defines my dependencies. So I want to have access to an IGreeter that will be in the OdeToFood.Services namespace. I'll call that variable greeter. Just like we've done many times now, I will save this injected service into a new private field _greeter. And now I can implement the one method that the MVC framework will invoke when I use this view component from another area of the application, like the layout view. This method will be a public method that returns an IViewComponentResult. So just like controller actions can return an IActionResult, I'll return an object that implements IViewComponentResult. And the name of this method will be Invoke. Yes, this method can accept parameters. I can pass parameters from the outside. I can also make this an async method if I want to perform some work asynchronously inside this method. We're going to keep things simple. I just want to build a model by going to my greeter service and saying get me the message of the day. And then just like with the controller action, I can return a View result, in this case a view View component result, and I can pass along my model. So this code will look very similar to the code that we've been writing for controller actions throughout the course. Now there is one catch here. When you have a simple model that is of type string and you pass that model as the first parameter to this View method, any time you pass a string as the first parameter here, the view method thinks you are passing the name of the view to render. So if my greeting is hello Scott, the MVC framework will go off looking for a view file hello, space, Scott.cshtml. That's not what I want. So any time you have a model that's a simple string type, you want to explicitly pass the name of the view and then the model as the second parameter. Now Default is the default name of a View component view result. I'm only explicitly specifying this because my model is of type string. So just like an MVC controller action, I can choose which view I want to render from a set of views that are available to this view component. Now the question is where do I create this view? Where do those views live? Well, I can create them in the Home folder. Then I could only use that view component from the Home folder. Since this is being used from the layout view, or will be used from the layout view, I want to create this view in the Shared folder. And there's a certain folder hierarchy that you have to follow, just like with the views for controller actions. What I need to do is Add a New Folder, Components. This is kind of like the Views folder, except this is for View components. And then I can add a directory for each view component that I have, so this is Greeter. And it's inside of here where I can have one or more views that I want to render from the GreeterViewComponent. The only one I need right now is Default. So let me Add a New Item. In this case, I just want to create an MVC View Page, give this page the name Default.cshtml, and now I'm in a Razor view that I can use just like any other Razor view. So I can provide a model directive. In this case, my model is a simple string type. I can have HTML. I can have C# expressions. All I want to do right now is just display my model value, that string value. And now I have my view in place, I have my view component in place, and I just need to be able to use this view component. One way to use this view component is to use a helper, the Component helper. This has an InvokeAsync method where I can say please render the Greeter view component for me. And this is where I could pass additional parameters. I don't have any additional parameters to pass. One thing to be aware of is InvokeAsync does return a task that I need to unwrap, and the easiest way to do that is to use the C# await keyword. So await for this asynchronous operation to complete and whatever that view rendered, place here inside of a footer. Let's make sure all the files are saved. Let's go back to the browser, I'm going to refresh the details for a specific restaurant, and I can now see that the message appears on the details, which is good. So it appears on the Home page, should appear on the details page. It will not, of course, appear on the Edit form because we used a Razor page for this, which is a different layout view, and that's okay. Now, this syntax isn't the prettiest syntax, await Component.InvokeAysnc. With a little more work, I can make this prettier. There is a tag helper that I can use vc:, and then the name of my view component, in this case it would be greeter. I can specify the view component there after the colon. It's always going to be lower case, and if you have other capital letters in the name, you can use what's known as kebab casing to specify the name of the view component that you want. For example, if I had greeter ONE, capital O-N-E, I could specify greeter-one here, but the name of our view component is just greeter. And in order for this to work, I do need to make a change to my ViewImports file. I need to add additional tag helpers. Essentially, I need to register the tag helpers which are in my own assembly, my own project, which is OdeToFood. So I want to say addTagHelper*, OdeToFood. And what that tells the Razor view engine is to go out through my project, look for any custom tag helpers that I have implemented, as well as view components that I have built. And with that change in place, I can now use my view component in a slightly friendlier way, should produce the exact same result though, A development Hello!!
Summary
In our application, we've now worked with layout views and partial views, as well as new features for this version of ASP.NET Core, new features like Razor Pages. Using all of these features at the appropriate time will help you create maintainable view code by avoiding duplication and still decomposing complex views into smaller pieces. That's the ultimate goal of many of these features. They give you the ability to create good, maintainable code.
ASP.NET Core Authentication and Authorization
Introduction
Hi, this is Scott, and in this module we will look at how to authenticate and authorize our application users. Authentication is how we establish the identity of a user, while authorization is how we make sure a given identity is allowed to perform some specific action in the application. There's many options to choose from for user authentication. You can use the ASP.NET Identity framework to manage user logins that are specific to your application, and you can also trust third parties to provide a user's identity. These are third parties like Microsoft and Google, as well as social providers like Facebook and Twitter. In this module, we'll take a quick tour of the ASP.NET Identity framework, but then focus on modern web authentication techniques using a protocol known as OpenID Connect, or OIDC for short.
An Overview of ASP.NET Identity
For this module, I just want to go into the File, New, Project dialog again to see the options that we have for user authentication. I'm going to go to the .NET Core templates, select ASP.NET Core Web Application again, and just create a temporary project that is based on the Web Application template for ASP.NET Core 2.0. Over here on the right-hand side of this dialog, you can see a button Change Authentication. When we created our OdeToFood project, we left this at No Authentication. We're going to come back and add authentication manually, but I just want to show you what you can get if you select this button. In addition to selecting No Authentication, I can also set up my project to require Windows Authentication. This is for internet applications, typically applications that are deployed inside of a company firewall where you're going to use your local Active Directory to authenticate users, and you're going to deploy your application under the Windows IIS web server. Your browser will coordinate with Windows and Active Directory to authentication users. Another option is to select Work or School Accounts. What we're going to do later in this module is set up this scenario manually. What we want to do is authenticate against a provider in the cloud, and since we'll be looking at how to do that later, I want to spend a little more time with individual user accounts. This is a scenario where you want users to be able to register for your site and perhaps create a local account with a user name and password that is dedicated to your site. One way to do this is to select the option to store the user accounts in your application. In this scenario, Visual Studio will set up the project to use ASP.NET Identity. And by default, ASP.NET Identity will store usernames and passwords and other user information in a local SQL Server database. And of course, you can configure the SQL Server connection string to point to any database. We'll take a look at what this project looks like here in just a moment. Another option here is to Connect to an existing user store in the cloud. This is very similar to the previous option in the sense that users can create logins that are specific to your application, but behind the scenes, instead of using the ASP.NET Identity framework and perhaps storing usernames and passwords in a SQL Server database, this option will use OpenID Connect, which we're going to look at in this module, OIDC, and connect to a special version of Azure Active Directory known as Azure AD B2C, business to consumer. So you might select this option if you're building an application for your company, and you want your customers to be able to create dedicated logins for your site, but you don't want the responsibility of managing those accounts, resetting passwords, sending out confirmation emails, or storing that information. All of that's going to happen inside of Azure with Azure AD B2C. And I should point out that this B2C option also supports third-party logins through Azure AD B2C, so logins through Google, Twitter, Facebook, LinkedIn, and so forth. But let's take a quick look at what happens if I select Store user accounts in-app, select OK, and allow Visual Studio to create this web application. Once everything is up and running and all of the NuGet dependencies are restored, I will want to run the application. So Start Without Debugging, and I'll receive a pop-up dialog that the project is configured to use SSL. And what Visual Studio can do is generate self-signed certificate so that I can use SSL during development without going out and purchasing or acquiring an SSL certificate. So would I like to trust the IIS Express SSL certificate? I will say Yes. Windows will tell me that I'm about to install a certificate, and this is from the certification authority localhost. I will go ahead and answer Yes to this dialog, and the application will be up and running. In this application, I can come in and say that I want to register as a user. Now I can fill out an email, a password, confirm my password, select Register, and that information will be stored in a SQL Server database. By default, it will be the same local DB instance that we've been using. After I do that, I'd have the ability to log out, I'd have the ability to log in, and inside of my application I would be able to restrict controllers and controller actions and Razor Pages to require user authentication, meaning the user must log in, and I'll be able to establish that user's identity. I will know what their email address is, for example. How does all this happen? Well back in Visual Studio, I'll just point out that the UI we were looking at is generated by Razor Pages. So in the Pages folder under Account, I would be able to find, for example, the Register page. Just like we've done previously, this page builds a form using asp- tag helpers and will have inputs and validations and labels for things like the email address and the password. And in the code-behind for Register, we'll see just like other Razor Pages this derives from PageModel. A little further down we can see that we're binding a property, the InputModel. This is where we receive those form inputs like the user's email and also their password and the ConfirmPassword. You can see this InputModel is just a nested class inside of my page. And we're using data annotations to make sure that the user enters the email address and to make sure the password displays as a password, so input type equals password. This particular page responds to OnGet to display the form, and also OnPost. It's during OnPost where we will actually try to put together a user given the information that the user entered in a form, and then we're going to use a few services that are provided by ASP.NET Identity to create this user account. So you can see we're using services like userManager. So back up at the top of this page, classes like SignInManager, UserManager, these are provided by ASP.NET Identity. I can use these services to create user accounts, delete user accounts, update accounts, list all the accounts, as well as log in a user when they provide me with their username and password. That would be in the Login Razor Page. And this ASP.NET Identity framework does use the Entity Framework. Inside the Data folder I can see a DbContext that was generated for me. This DbContext derives from an ASP.NET Identity DbContext, which is the IdentityDbContext. That's a base class that will be provided by default by that framework. There are interfaces that you can implement to use the Identity framework with various different data providers. So it's a very flexible framework. This is just how things are set up by default, and it's this ApplicationUser class that defines the information that I want to store for a user. Notice it has a base class IdentityUser. That will include all the fields that are required for identity, things like the username, the password, also links so I can set up claims and roles for a given user, and any additional properties that I add to this ApplicationUser class would be additional information that I want to store about a user. So if I wanted to store something like a user's favorite color, I could add that as a property to ApplicationUser, run my Entity Framework migrations for the application DbContext just like we've done previously in the course, and that will create the SQL Server schema that I use, and the application will be off and running. And this is just a really quick overview of the ASP.NET Identity framework, what the major players are, what the features are, and what you can have if you do File, New, Project and set up authentication to use individual user accounts that are stored in the application. What I want to do with our OdeToFood application, instead of using local accounts, I want to use accounts that are stored somewhere in the cloud, and I want to be able to authenticate users using OpenID Connect. So first, let's talk a little bit about OpenID Connect before we return to the OdeToFood solution.
An Overview of OpenID Connect
In our OdeToFood application, we only want authenticated users to be able to create new restaurants, which means we will need to know the user's identity. In this module, we will use OpenID Connect, OIDC, to establish a user's identity using a third-party identity provider. Since OIDC is an open protocol, you can use OIDC with Microsoft, Salesforce, Facebook, GitHub. There's many places where your users might already have an established identity, and if you trust one of those other parties, you can use OIDC to authenticate your users. And OIDC doesn't always mean that you use a third party. If your company uses Office 365 or Azure Active Directory, you can also use OIDC to authenticate users with the same credentials that they use to sign in to their local machine. Let's talk about how OIDC works from a high level. First, before you can authenticate a user with OIDC, you generally have to register your application with the identity provider. This experience and where you go to do this, that depends on the provider that you use, but most providers will require you to register a web application and provide some basic information, like the URL of your home page and a description and a callback URL, which is important for reasons that we'll see here. I'll show you how to register an application with Azure Active Directory, but the basic process is the same for GitHub and Facebook and the rest of the identity providers. Now what happens at runtime I want to illustrate with three swim lanes on the screen. On the far left is the customer, or client, with their web browser. The middle lane is the identity provider, like Azure AD or LinkedIn. And on the far right, that's our application. What I'm going to describe is the process for a web application that renders HTML on the server. If you're building an API or a service, or building a native app for a device, the OIDC flow that I'm describing will be different. Let's say our customer sends a request to create a restaurant. This request is an anonymous request and doesn't include any information that we can use to establish the identity of the user. Since we only want authenticated users to create a restaurant, we will challenge the user and force them to log in. To challenge the user, we will respond from our application with an HTTP redirect message. The redirect message will send the user's browser over to the identity provider that we are using. In this case, for example, Microsoft Azure. We have to send the user to the identity provider's website because that's the website that will take the username and password and verify the user credentials. One of the reasons we'll be using OIDC is because we don't want to manage usernames and passwords. We want someone else to implement all these features, like password reminders, and security questions, and two-factor authentication, all those features that are needed for identity these days. Now the exact URL that we will redirect to, that will be a URL specified by our identity provider. In this case, the login.microsoftonline.com URL is a URL that works for Azure Active Directory. But you're using Google or GitHub or some other provider, you'll have to look at their documentation for the correct URL. And this redirect that we are using will include a query string parameter with a name client_id. That client ID uniquely identifies our application, and you'll receive that client ID when you register your application with the identity provider. Now we have received an anonymous request, and we have redirected the user to another website, and it's on this other web page where the user will post their username, their password, their security code, whatever they need to log in. And if the identity provider verifies the user's login credentials and the user proves they are who they say they are, the identity provider will then redirect the user's browser back to our application, and this redirect will include a special token. The id_token is what the OIDC specification calls this token. This token contains all the information that our application needs to verify the user's identity. I'm leaving out some of the cryptographic details here, but the ID token passed from the identity provider will tell us in a secure manner the identity of the user. So now we've redirected the user off to someone else to log in, and the user has logged in, and then someone else has sent the user back to us with a token to tell us who the user is, but this token only exists for that one request. We need to remember who this user is so they don't have to log in with every request that they send. The way we can do that is to issue a cookie, what we call an authentication cookie or an authentication ticket. A web browser will automatically send this cookie back to our website on every request that the user makes to our site. So for the rest of the session at least, all we have to do is inspect this cookie to know who the user is, and they're effectively logged into our site for the rest of the session. If all of this sounds difficult, don't worry because ASP.NET Core includes services and middleware to perform the redirects that we need to verify tokens to manage cookies and take care of all this work that we see here. I just wanted to give you an idea, a simple overview, of what's happening behind the scenes. But now that we know a little more about how OIDC works, let's look at upgrading our OdeToFood application and authenticate users with OIDC.
Using Secure Connections and Registering the App
We are about to require authentication for our application, and any time you're working with authenticated users, you want to use SSL. And even if you're not authenticating users, you should consider using SSL these days. In Visual Studio, it's very easy to switch my development environment over to use SSL connections. All I need to do is right-click on my project, open up the Properties dialog, and here on the Debug tab, which we looked at before when we were looking at the ASPNETCORE_ENVIRONMENT variable, this is where I know I'm using IIS Express, and I'm going to switch things over so that IIS is listening on an SSL endpoint. So select the checkbox Enable SSL. This will give me a new URL that I can use to reach my development version of the application over SSL. I'm going to copy that value and paste it into the App URL so that the next time I run with or without debugging, Visual Studio will launch the browser to this secure version of the URL. And to make sure I'm always using SSL, I might even take an additional step, which is inside of Startup.cs I can configure some middleware to make sure all the connections are happening over SSL. What I can do is use the Rewriter middleware. The Rewriter is a very powerful piece of middleware. It can examine incoming requests and either redirect the browser or modify the URL internally in the middleware pipeline based on rules and conditions and regular expressions that you provide. And one of the rules that I can provide through the RewriteOptions class, which I don't want this fully namespace qualified, so let me back up, press Ctrl+period, and add a using statement for Microsoft.AspNetCore.Rewrite. One of the options I can provide here through a method call is to add a RedirectToHttpsPermanent, which means any time a client connects and is trying to reach a URL over regular HTTP, this piece of middleware will send back a permanent redirect status code and go to the same URL, but the SSL version of that URL, so over HTTPS. And now I can be sure that in development and in production all the connections to my application will be using SSL. And with that piece in place, it's now time to register our application with the identity provider. So remember I said that's the first step with OIDC. It doesn't matter if you're using Google or Microsoft or LinkedIn or GitHub, you need to register your application and obtain a client ID that uniquely represents your application. I want to use Azure Active Directory, so I'm going to come to portal.azure.com, but you can follow along with any identity provider that you want. I will say, however, that obtaining an Azure subscription is free, you do have to provide a credit card, but obtaining an Azure Active Directory instance, your own personal Azure Active Directory, is completely free with the Azure subscription. And what I'm going to do is only allow users that are registered in my Azure Active Directory to access my application. So I want to find, inside of the Microsoft Azure portal, a link to my Azure Active Directory. I can find that over here on the left-hand side. If it's not there for you, you might have to do some searching. But this is where I can see the users and groups that are in my Active Directory. You can see I only have two. And this is also a place where I can find App registrations. And it is here, in the Azure portal, where I need to register my application. You can see I have a number of existing application registrations. These are all set up for Visual Studio Team Services builds. I'm going to create a New application registration. And this is where I can provide the name of the application. Let's just call it OdeToFood. It is a web application as opposed to a native application. And I need to enter the Sign-on URL of the application. What I'll do is come back to Visual Studio, let's return to that Properties pages, let me copy out my HTTPS URL, and I'm going to paste that in as my Sign-on URL. So this registration's going to work for any developer who's working on OdeToFood that runs OdeToFood on localhost port 44361. When I go to production and I have a real URL, like OdeToFood.com, I would create another app registration and use my proper domain name here in the Sign-on URL. With that, I'm going to create the app registration. This is typically just how easy it is when you're setting up with an identity provider, but there is one more bit of information that I need to provide. I can now see OdeToFood listed as one of the applications that's registered with my Active Directory. I'm going to click on that. I'll point out a few things here. First of all, this is the application ID, and that's the unique client ID that I'm going to need to send to Azure when I ask Azure AD to sign in my user. The section over here for Keys, you'll typically want to create a key, or what's commonly known as a client secret, if you want to use Azure AD and OpenID to connect to your APIs. And the Reply URLs are important. I do need to add a proper reply URL. And with ASP.NET Core, although this is configurable, with ASP.NET Core, the reply URL is going to be signin-oidc. So what is the reply URL? Remember when we talked about the OIDC flow? The identity provider, in this case Azure AD, will redirect the user's browser back to my application and include a token. And this is just confirming with Azure AD that it is okay to redirect to localhost/signin-oidc. By default, that's where ASP.NET Core middleware is going to be listening, and it's where it's going to pick up that token and validate the token and figure out who the user's identity is. And these are all the settings that I will need inside of Azure. And if your company already has Azure subscriptions, you'll already have an Azure Active Directory. You might just need permissions to create an app registration, or you can ask your company to create your own test instance of Azure Active Directory. And if your company's using something like Office 365 or they're synchronizing their local Active Directory into Azure, you'll be able to sign in any of the users from your company using the approach that I'm about to show you. So let's turn back into the application in Visual Studio and configure our application to authenticate users against Azure AD.
OpenID Connect Configuration
Inside my application, I do not want to hard code things like the application ID and the OIDC endpoints that I'm going to use. I want those parameters to be configurable values inside of my application. So let's open up appsettings.json, and what I want to do here is add the configuration that will allow me to use the ASP.NET Core middleware for OpenID Connect and have that middleware coordinate with Azure Active Directory. And again, since OpenID Connect is an open protocol, it's implemented by various identity providers, the steps that I'm showing you should work for any identity provider. There might be little nuances here and there, and there might be some exploration as you try to discover that place where you register your application, but the core concepts are all the same. What I'm going to do is add a new configuration section to appsettings.jason, something that I could override if I needed to using environment variables, and I'm going to call this AzureAd. These are my connection parameters for Azure Active Directory. There's really just two pieces of information that I need. First is the ClientID, which is that unique application identifier that was assigned to my application when I just registered the application. So back in the portal, I want to take this Application ID, and click to copy this, and bring it over to Visual Studio and paste this value in. The second setting that I will need is known as the Authority. The Authority is really the base URL to the OpenID Connect endpoint for your identity provider. So again, you might have to dig around in the documentation for your particular identity provider, but for Azure AD, the Authority looks like this. It's going to be over https, and it's going to be login.microsoftonline.com, and then what's known as a tenantid, which we will come back and fill in here in just a second. Just make sure I spelled all of this correct, microsoftonline.com. And when we need to find out the identity of a user, this is the base of the URL that we will use to redirect that user. In other words, there's components in the ASP.NET Core that will take this value and append some additional information, we'll see that later, but if my authority is GitHub, this would probably have GitHub in the name or Facebook or Google, whatever identity provider you are using. With Microsoft, I just have to find out my tenantid. So coming back to the Azure portal, this one can be a little bit tricky to find sometimes, but if I come back to the primary blade for Azure Active Directory, let's reopen it, this is where I can, again, view App registrations and Users and groups, and I can also click to view some of the lower-level properties of my Active Directory. And this Directory ID, which Microsoft sometimes refers to as a tenant ID, this is a value that uniquely identifies my Azure Active Directory versus your company's Azure Active Directory and so forth. So I need to take this value, 11be8607 etc., and include this in my URL. So the full URL will look like login.microsoftonline.com/ and my directory ID. Now when my application redirects to that location, Microsoft Azure, which has to run millions of Azure Active Directories, it will know which directory the user can authenticate against. And the redirection, the token validation, the cookie management, this can all happen using some services and middleware already provided by ASP.NET Core, so the next step is to add and configure the services and middleware.
Authentication Services and Middleware
Now I'm inside of the Startup class for the application. This is where we configure our services. This is where we configure our middleware. And the way ASP.NET authentication and authorization works is that I need to install a number of services, services for every type of authentication scheme and authorization scheme that I want. And once I have the services configured, I can then add a single piece of middleware that will make use of those services. Let's add the middleware first. The middleware I will add after StaticFiles, but before the MVC framework gets involved. And that means when I'm serving static files, I'll never know the identity of a user, but that's okay. I'll serve static files to anyone. But inside of my Razor Pages and MVC controllers, if I have app.UseAuthentication in place here, I'll be able to find out the user's identity in any other middlewares that happen after app.UseAuthentication. We'll see how to examine the user's identity. But now app.UseAuthentication relies on us configuring authentication services with services.AddAuthentication. When I add authentication services, I'm given some options, and we're going to configure some options here. The first thing I want to configure is the DefaultScheme. An authentication scheme in ASP.NET Core is basically a friendly name for how do we authenticate users. Are we using cookies? Are we using tokens? Are we using some other approach? By default, what we will actually be using in this ASP.NET Core application is the cookie authentication scheme. If you remember when we talked about how OpenIDC is implemented, after the identity provider, like Azure AD, redirects the user back to our site with a token, we're essentially going to convert that token into a cookie that the browser will send along with every subsequent request, and that cookie, or authentication ticket, will provide enough information to authenticate the users. And here we basically have to tell ASP.NET Core what is the name of the default scheme, and that name we will get from the cookie authentication service, and there is a class, CookieAuthenticationDefaults, which is in the namespace Microsoft.AspNetCore.Authentication.Cookies. Once I bring that namespace in, I have access to a static member of this class, which is AuthenticationScheme, and that's just a simple string, the string cookies, but it's telling ASP.NET Core how we authenticate. I'm also going to set up a DefaultChallengeScheme. Challenging a user is forcing them to authenticate, and when we challenge a user to authenticate we will be using OpenIdConnectDefaults. And this class, which is in the namespace Microsoft.AspNEtCore.Authentication.OpenIdConnect, this class provides a name that we can use for DefaultChallengeScheme. And again, that name is in a static property, AuthenticationScheme. So now we've told ASP.NET Core about our defaults, but we still need to add the services that will actually implement those authentication schemes. So there are methods for this. There's a method AddOpenIdConnect, that will be one service, and there's a method AddCookie, which will add my cookie authentication services. And the middleware that we configured earlier, AddAuthentication, will be able to use these two services to challenge users and authenticate users. Now there's just one more step we have to take, and that is to configure our OpenID Connect services. The cookie services are fine. We're just going to take all the defaults there, the default cookie names, the default encryption schemes. There's nothing I have to change. But with OpenID Connect, this service will have no idea what identity provider I want to use, so I'm going to need to feed it information from appsettings.json. And the way I can do that is by using an overload of this method that takes some options, and what I need to do is set some properties on these options. So let me do some reformatting here, get rid of the spurious semicolon, and the type of options that I want to set are options like the Authority. Remember we specified an authority in appsettings.json. I also need to specify my ClientId because that's unique to my application. That's also in appsettings.json. An easy way to do this is to actually use the configuration service. So you might remember we injected a service that implements IConfiguration into the constructor of our Startup class, and we saved that off into a private field. So I can now access this configuration service and tell it to do a Bind. What Bind allows me to do is specify a section of my configuration by name, so let's say AzureAd, and Bind will take the values that are inside and try to match them up with properties on a C# object, so much like Model binding with the MVC framework. If I have an object that has properties Authority and ClientId, the Bind method will set those properties using my application configuration automatically, so I can say Bind AzureAd to these options that we were just looking at. And I showed you it has an Authority property and a ClientId property, so those values will also be set. And for some identity providers and some identity scenarios, you might find yourself implementing or setting other configuration options here. It all depends on the scenario that you are trying to achieve. But at this point, I should have all the configuration in place, all the services in place, and all the middleware in place. We should now be able to authenticate users against really any OpenID Connect provider that I have configured and then authorize those users when they try to perform specific actions. We'll look at the next.
User Authorization
Inside the application, I've re-launched the browser. It's now connecting to the application over SSL, we're on port 44361, and I want to prevent anonymous users from being able to get to the Add Restaurant form and submitting that form, as well as the Edit restaurant form. And now that we have our authentication middleware in place, this is easy to achieve. First, let me open up the HomeController, which contains the actions that we use to create a restaurant. I want to be sure that an anonymous use cannot get to this action. Now I do want to point out that there is a User property that you inherit from the base controller class, and one of the properties here is User.Identity where I can check imperatively is the user authenticated? But placing these checks throughout the code base becomes a little bit messy, and with ASP.NET Core there is a better way. The better way and more flexible way is to use the Authorize attribute. This is in the namespace Microsoft.AspNetCore.Authorization, so I need to bring this attribute in. And if I use the attribute without specifying any additional parameters, Authorize will just ensure that the user is authenticated. We have established some identity for the user. I can also pass in parameters that will check to see if the user is in a specific role, or for more flexibility, evaluate a policy against the information we have about this user. We won't have time to look at policies in this course, but there is documentation available on how to define policies. They're very flexible. And what I can do with a policy is I can define a named policy. Let's say we create a policy called IsAdmin, and this policy can contain and encapsulate all of the checks that I want to make against a user when we are authorizing them to reach a specific action. So not only could I check to make sure a user is authenticated, I could also check other information that is available for a user, like do they have the proper name, or do they live in the proper country, or are they in the correct department for my particular company. So policies can be very flexible. All we care about is just to make sure the user is authenticated. So I'm going to use just the Authorize attribute, and I can either place this on the action or the actions that I need to protect, or this is one of those attributes where if I place this attribute at the controller level, the framework will make sure that the user is authenticated before we can reach any of the actions inside of this controller. And generally speaking, I think this is the approach that you want to use. You want to just cover all the actions in a particular controller; otherwise, it becomes easy to miss one, and then you might have some particular actions that you do not want protected, like the Index action is the home page. We do want to allow anonymous users to reach this action, and there is an attribute that can override that outer Authorize attribute, and this attribute is AllowAnonymous. So at this point, a user will be able to view the home page, but won't be able to see the details for a restaurant or create a restaurant. We also need to protect the ability to edit a restaurant since we have a mix of controllers and Razor Pages in this application that edit functionality when into a Razor Page and to protect the edit functionality. I also want to place the Authorize attribute on my PageModel. So again, bring in the namespace for Authorize, and now an HTTP request will not be able to invoke OnGet or OnPost unless we have an authenticated user. So with those changes in place, let me save everything in the application, and let's do a refresh just to make sure the application recompiles successfully. And when this is complete, let me go to Add a restaurant. Now notice that I came into the Create action for the restaurant. How did that happen as an anonymous user? Well the fact is, in this browser session, I have already authenticated against Azure Active Directory. That happened when I signed into the Azure portal. And effectively, behind the scenes, when my application redirected the browser into Azure to have me authenticate, Azure Active Directory effectively said well this person is already signed in as scott@OdeToCode.com. Let's redirect back with a token. So what I want to show you is what happens if we come to the Home page of the application, and let's do this in a new instance of the browser, a new private window where I have not logged into Azure Active Directory. So now let's try to add a restaurant, and I will be redirected to login.microsoftonline.com/ my directory ID, and then you can see some additional information including that unique client ID that I have specified for the application. Now Azure Active Directory is telling me you're trying to sign into the application OdeToFood, so go ahead and enter a work, school, or personal Microsoft account. This will redirect me to the next page where I can enter my password for this account, and because I'm using two-factor authentication, I do need to open up my phone and approve this request, which I just did, and now Microsoft's website redirected to my application to that /signon-oidc endpoint, passed along the token that represents information about me, my application decoded that, the middleware that we installed for OpenID Connect, and I'm now able to access the create functionality, and I should also be able to edit a restaurant. If I were to turn on the developer tools in the browser and watch the network activity, I would see these redirects that take place between my application and Microsoft's login endpoint for OpenID Connect. And again, it is that Authorize attribute that will challenge the anonymous user and essentially say what services do we have available that can challenge a user. And through the configuration that we provided, the OpenID Connect service will come into play and redirect the user to log in. So now that we know we can force a user to log in, in the next clip we'll see how we can programmatically obtain some more information about a particular user.
Identities and Claims
I just want to point out that the User property that I showed you in the last clip that is available as a property you inherit from the base controller class with the MVC framework is also available in the code-behind for Razor Pages, so I can check User.Identity.IsAuthenticated. Although again, a better way to do this is to use the Authorize attribute or to define some custom policies. But this User property can be useful for other scenarios, and this User property is also available inside of a Razor view, or a Razor Page CSHTML file. So let's display some information about a user. Let's display all of the claims that are given to us from the identity provider. Claims represent information about the user, and claims are simple key-value pairs. So my identity provider, which in this case is Azure AD, when Azure AD redirects the user back to my application with a token, the token can include some claims about the user. A claim might be that the user's first name is Scott and that the user's email address is scott@OdeToCode.com. And I just want to show you a little code snippet that will dump out all of the claims that we know about a user. This could be useful for debugging, and I'm just going to place this information into the layout page so it's available everywhere, or at least for all of the Razor views that use this layout page. Let's first check if the User.Identity is authenticated. If so, we can display one bit of UI; otherwise, I can just throw in a div that says You are anonymous. This could also be where you provide a link that the user can click on to explicitly log in, so not wait until they hit something that has authorize, just go ahead and log in. I'm not going to show you how to do that in this course, but in the code that you download for this module, I'll include the code that shows you how to provide log-in and log-out links in the application. But if the user is authenticated, I want to write a foreach statement and say for each identity in User.Identities. It is possible that a user has logged in with multiple identity providers and we have multiple identities for a single user. It's not going to happen in this application because we can only log in the user through Azure, but it is possible to have multiple identities for a single user. So let's write out an h3 tag, the Name associated with this particular identity, and then let's write out a list of all the claims that we know about a user. So another foreach statement. For each claim in this identity.Claims collection, let's pre a list item, and in this list item we can write out the name of the claim, essentially the claim type, that would be the name of the key, and then the value associated with that particular claim. Now I do want to point out that the set of claims that you retrieve from an identity provider can depend on the identity provider. So different identity providers track different bits of information about a user. And generally speaking, every identity provider will give you access to an API if you need to obtain more information about a given user. But let me save all the files and see what information that we get back from Azure AD. I'm just going to refresh the Home page, and I can see my identity name is live.com#scott@OdeToCode.com. That's a fancy way of saying this is a Microsoft.com home account. And I can see this collection of 10 or so claims. These claims tell me that I logged in with a password, that this is my email address, my surname, my given name, and my friendly name. And I can even find out what tenantid or Azure Active Directory that I'm associated with. That can be useful if you're using OpenID Connect against Microsoft's common endpoint for Office 365 users. That would allows individuals from multiple Azure Active Directories to be able to log in. And so now programmatically if I wanted to display a friendly name or grab the user's email address to send a notification, I can inspect this claims collections and look for the specific claim types that I need, and I'll have that information, at least for the users that have authenticated against Azure Active Directory.
Summary
In this module, we saw how to hook up our application to authenticate a user using OpenID Connect, or OIDC. OIDC is the authentication protocol for the modern web, and you can use OIDC to authenticate users for HTML-based applications, as well as services and native apps. We also saw how to use the Authorize attribute in ASP.NET Core to protect controller actions and Razor Pages and saw how to inspect the claims available for an authenticated user. In the next module, we'll continue improving our application with some client-side libraries.
Front End Frameworks and Tools
Introduction
Hi, this is Scott, and in this module we will turn our attention to front-end JavaScript and CSS libraries. We're going to look at the tools that we can use to manage and install these libraries and frameworks that will allow us to add some style to the application and support client-side validation in development, as well as production. Let's get started with an overview of the tools that we're going to use.
Front End Tools
Let's start by talking about package managers. NuGet is the package manager for .NET. Behind the scenes, the dependencies that we installed into our application, dependencies like the Microsoft.AspNetCore.All package that we saw earlier in the course, these dependencies are managed by NuGet, which can download the software bits and place them onto the file system. For a long time in ASP.NET, we also used NuGet to download and install JavaScript libraries and CSS frameworks. For reasons that I'll explain later, however, I won't be using NuGet to install the front-end libraries that we need. Bower is another package manager, a package manager for front-end work that became popular, and at one time when you created a new ASP.NET project in Visual Studio, you could automatically receive a Bower file in the project that you could use to install and manage front-end libraries, JavaScript libraries, and CSS frameworks. However, I'm not going to use Bower in this module either. You'll find lots of Bower content on Pluralsight if you want more information. I'm going to show you what I think is a simpler approach. Bower is a Node.js tool that you install with npm, and npm is Node's package manager. So if you're using Bower, you're already using Node and npm. Now the traditional use of npm is to install libraries and tools that you're going to use inside of a Node.js application, but over the last few years npm has done some significant work and has become more popular for managing font-end libraries also. This means if you want to use jQuery in your application, for example, you can install jQuery with npm and skip using Bower and NuGet. Why would you want to do this? Well you might use npm instead of Bower because one tool with one configuration file is easier than using two tools with two configuration files. And you might use npm instead of NuGet because for any serious JavaScript work these days, some amount of Node tooling is almost required. All the best tools, compilers, and minifiers are tools that you can install with npm and run in Node. So npm is the approach that I will show you in this module. We'll use npm to download the front-end frameworks that we need and then use those frameworks and libraries, like Bootstrap and jQuery, inside the application. The next step then would be to understand how the set up Node and npm for our project.
Command Line vs. Visual Studio
There's a few different ways to work with npm in an ASP.NET project. One approach is to use the tooling and support provided by Visual Studio. This is the approach that I'll show you in this module. Another approach is to use Node and npm from the command line. The command line approach gives you flexibility and power beyond what the tooling in Visual Studio provides, but you may or may not need that power. There's other Pluralsight courses that can show you the command line interface. With the Visual Studio approach, we'll be using a version of Node and npm that Visual Studio installs by default with the rest of the web tools. If you're experienced with the command line, I recommend you download and install Node.js from nodejs.org so you have more control over your environment. But to show you how this works from Visual Studio as a first step, let's see if we can download the Bootstrap CSS framework.
Setting up npm
Inside of the application, we want to install Bootstrap using the Node package manager. Now just like NuGet maintains references by writing into our csproj file, specifically package references, npm also allows up to manage packages. But in npm's case, we need a JSON file with a specific name. That's package.json. So I want to right-click on the project and add a new item. I want to add an npm package.json file. And if I do a search for npm here in the file templates, I can find there's a template to give me a starting npm Configuration File. I do want this to be named package.json. Let me click Add to add this file to the root of the project, and I do want to change the name here. Npm can be a little bit finicky about the names. I want to select a name like odetofood. It's all lowercase, there's no punctuation, there's no white space in the name, and this name will make npm happy. And when I save this file, I want you to watch the Dependencies section here because when I save the file, Visual Studio is going to detect oh, you're using package.json, that's for the Node package manager; therefore, you must also have some npm dependencies. Currently, there's no dependencies listed here, but I want my first npm package dependency to be Bootstrap. In package.json, I can list dependencies in a devDependencies section or in a dependencies section. And I want to add Bootstrap to the dependencies section. DevDependencies are packages that you depend on at development time and build time. So typically your dev dependencies are tools like minifiers or compilers or transpilers, like the TypeScript transpiler. Bootstrap is a framework that I need when my application is running in production, so I will list bootstrap as a regular dependency. Any you can see that I get some IntelliSense here. I know the name of the package that I want is just Bootstrap, and I do have to select a version of Bootstrap, and I can select version, the latest version, 3.3.7. Now let me save package.json again, and I should be able to come under my dependencies and see that yes Bootstrap is installed as an npm dependency. So I know have Bootstrap on my file system. The question is where do the Bootstrap files live? By default, npm will install all of the packages that I list into a folder called node_modules. I won't see this folder inside of my Solution Explorer window by default, and that's because that folder is typically excluded from the project in the sense that by default we won't be checking node_modules into source control. I have my solution inside of a Git repository, and I explicitly exclude node_modules from my repository. All I need to check into source control is this package.json file. When any other developer clones this project and opens the project in Visual Studio, Visual Studio will automatically use npm to install all the dependencies I need. That why when I save this file Bootstrap just suddenly appeared. If I come up here into the Solution Explorer toolbar, there's a little icon I can click on to show all files on the file system. So now I can see the bin folder, which is where my build output is placed, and I can also see the node_modules folder. Inside of node_modules, there's a folder called bootstrap. This is where Bootstrap will live. The bootstrap package includes a dist folder, short for distribution, and it's inside of here where I will find the assets that I need including, under the css folder, the bootstrap.css file that I need to get into the browser to use the Bootstrap styles. Which brings us to the question how are we going to serve this file? I want to be able to request bootstrap.css from the web browser and use those Bootstrap styles, but we know that the only place where I can serve static content is from the wwwroot folder. Let's talk about how we can get this to work in the next clip.
Serving File from node_modules
There are various different approaches that I can use to take this bootstrap.css file and move it or copy it from the node_modules folder into my wwwroot folder. One approach would be to use a JavaScript task runner like Grunt or Gulp. These task runners would allow me to set up an entire build system. I could compile files, transpile files, minify files, bundle my JavaScript files together, bundle my CSS files together. Those tools integrate into Visual Studio, and they're very powerful. We have courses about those tools on Pluralsight if you're interested in learning more. I'm just going to take a much simpler approach. All I need to be able to do is get this bootstrap.css file into the browser, and another approach I could take for that is not to copy this file, but just to serve static files from the node_modules. Now I still want to serve files also from wwwroot, and this just means that inside of Startup.cs I'm going to need two instances of the static files middleware. The first instance here will serve up files from the wwwroot folder. I want to add a second instance of static files that will look into the node_modules folder, and that's going to require me to set some of the options on the second instance of static files middleware. And instead of cluttering up my Configure method here inside of Startup with all of these options, I just want to define a new extension method for IApplicationBuilder that will make it easy to read and very obvious that I'm going to serve up files from the node_modules folder. Let's build this extension method. Inside the project, I want to create a new folder called Middleware, and this can be a location where I build my own custom middleware, as well as build extension methods for IApplicationBuilder that make it easy to install middleware. So let's add a class inside of here where I can place extension methods, and I will call this ApplicationBuilderExtensions.cs. Since this class will have extension methods inside, it needs to be a static class, and I want a public static method that returns an instance of IApplicationBuilder so I can chain methods together. That requires me to bring in Microsoft.AspNetCore.Builder. The name of the method will be UseNodeModules, and this will be an extension method for IApplicationBuilder. So use the this keyword, take a parameter of type IApplicationBuilder; we'll just call that parameter app. Ultimately, this method will return that instance of IApplicationBuilder, but before it does, I want to add the static file middleware into the pipeline. By default, static files will look into wwwroot. I need to override those defaults, so I need to provide some options, and there is a StaticFileOptions object that I can instantiate. And then there's a few interesting properties on this class. One interesting property is the RequestPath. StaticFileOptions uses this RequestPath to filter incoming HTTP requests and decide which ones it should try to act on. So if I set a RequestPath of node_modules, what I'm telling the static files middleware is only try to respond to requests that start with node_modules; otherwise, just let it pass through, and something else will handle that request. So now the static files middleware knows what request to try to respond to, where does it go on the file system to look for a file to respond to that particular request? That's where I can set a FileProvider. FileProvider is an object that implements an IFileProvider interface. You can build your own FileProvider to look up files that might not be on the file system. They could be on the network or in a database, but Microsoft provides a fileprovider that will look on the physical file system, and that is the PhysicalFileProvider. I need to instantiate that class. That will make me bring in the namespace Microsoft.Extensions.FileProviders. And this PhysicalFileProvider constructor requires a parameter. It needs to know at what point on the file system should I start looking? What folder? And this needs to be an absolute path. You'll notice that in the comments in the IntelliSense. So I need to provide an absolute path to the node_modules folder for this PhysicalFileProvider to be able to look in that folder and all subfolders. Now inside of this method, I do not know where this application is located on the file system. I'm going to require the caller of this method to pass me that information. So what is the root of this project? Where should I start looking? And then I can build a path to the node_modules folder. I can do that using the Path class in .NET Core. That's in the System.IO namespace. The Path class contains some helpful static methods that I can use to do things like combine two strings into a proper path. And Path.Combine understands am I running on Linux or am I running on Windows. What is the path separator? Do I need to forward slashes or backslashes? I don't need to worry about any of that if Path.Combine builds my path for me. So take the root and combine that with node_modules, and I now have an absolute path that I can give to the file provider to tell it where to look, and then I can give this fileProvider to my StaticFileOptions so the static file middleware can use this provider to search on the hard drive, and all I need to do is pass my options into app.UseStaticFiles. Now back in Startup.cs, when I invoke UseNodeModules, I need to pass in the root of this web project. And that's the type of information that I can get from the object that implements the IHostingEnvironment interface. If we look at the properties that are available on environment, one of these properties is ContentRootPath. That name I think is sometimes a little misleading because it sounds like it's pointing to where your content lives, like the wwwroot folder, but there's a different property for the default location for static content, which is this WebRootPath property. I want to pass in the ContentRootPath. That will be the absolute location of my project. It's where the csproj file will live. And then one more small tweak that I'm going to make. Typically when you build an extension method for IApplicationBuilder, you will place that extension method into the Microsoft.AspNetCore.Builder namespace. This is optional. Some people don't like using a namespace that does not map to the same project name and folder that you're inside of, but this is an approach that Microsoft uses. Any extension method that they provide for IApplicationBuilder will always be in the Microsoft.AspNetCore.Builder namespace, once I fix up the syntax here, just to make that method more discoverable. And with this change in place, I can remove all the unnecessary usings to clean this file up a bit. And now when I come into Startup.cs UseNodeModules, it would appear in the IntelliSense list right from the start because that extension method is in the same namespace that defines the type IApplicationBuilder. So now I have everything in place, I want to save all my files and see if we can actually get bootstrap.css into the browser. To do that, let's go over to the Layout page. So all of the Razor views that use this Layout page can use Bootstrap. And I'm just going to come into Visual Studio, I can actually drag and drop bootstrap.css into my editor window, and Visual Studio is smart enough to create a link, and it even includes the correct path. So we're going to be serving this from node_modules, the bootstrap folder, distribution, css, bootstrap.css. Let me also clean up Layout.cshtml. I'm going to remove some of that code that we placed in here earlier to explore claims in ASP.NET Core. And there's actually, now that I'm thinking about it, one other little piece of code that I want to clean up. Inside of Startup.cs, we still have this app.Run middleware at the bottom of our processing pipeline that will respond to any request that reaches this bit of code with a Not found message, a content type of text/plain, but more importantly, an HTTP status code of 200 that everything went okay. Now that we're going to start serving some front-end files behind the scenes in the browser, I don't want a bad path or a typo to a CSS file to go to the server and receive a 200 OK response with the text not found. So I'm going to remove this app.Run. What I want to happen instead is to receive 404 errors if something isn't found. And now let's save all the files, come over into the browser, I'm hoping Bootstrap will be in the Layout page, so it should be in effect for this Home page, and if Bootstrap is in effect, we should see a change in the typography for this page because Bootstrap will manipulate the default fonts. And after I refresh, I can see a significant difference in the page. So I'm pretty confident that Bootstrap is being served up from node_modules, so it's in effect. And now what we want to do is to use some Bootstrap styles to try to make this Home page appear a little bit nicer.
Styling with Bootstrap
Let's use Bootstrap to improve the look of our Home page. I'll start by working inside of the layout view that is used to render the Home page. Let's add some navigation to the application. I'll use some Bootstrap classes here, like saying that I want to build a navigation bar. I will give this the default look to the navigation bar. I might want the content of this navigation bar to stretch across the screen, so a Bootstrap class of container-fluid will allow me to that. Inside of the navbar, I want a navbar-header, and inside the header, I want to display the text OdeToFood and have a link that if the user clicks on it they'll always be able to get back to the home page for the application. This anchor tag will have a class of navbar-brand, and all of these classes are documented on the Bootstrap website. This will have an href that just points to the root of the website, and we will give this the text OdeToFood. I can also take this login-logout component that I built offline after the previous module and that you can find in the exercise files that you can download, and I'm going to place that into the navigation bar. I'll do that inside of a div that has a class of navbar-right that will push this content over to the right side of the screen. And let's just pause here and save this file and refresh to see what changes have happened. So now I have a navigation bar; I have a login link. If I expand the browser, I can see there's now enough room to push that content out to the right, but it might be nice to turn this Login link into a button, something that looks like a button. That's easy enough to do. I'm going to come into the view for the LoginLogout component, and any place that I have an anchor tag here, I want to turn this into a button, and we'll just make it a default-looking button, so add this class. I'm going to copy that line, also paste it into the login link so there will always be a button here at the top of the screen that's either going to be the Login or the Logout button. Now let's try to work on some of the content that appears from the index view. So first, again going back to the layout, I want to place everything that we render as part of the body into a Bootstrap container, and that would include the footer. So I'm going to take this footer and bring this up a little bit so it renders inside of the container. Let's save the file and see what this looks like. This is looking better. I'm also going to take out the greeter here. We used the greeter to learn how to build view components and how to inject services, but it's not really needed for this user interface anymore. Now let's work on the display of the individual restaurants. Each restaurant, you might remember, was rendered from the Index view using a partial view called Summary, and then at the bottom of the page we had a Create link. Let's turn that Create link into a button. This time we'll make it a primary button. And now modify this Summary so the movies have a little bit better display. Inside of Bootstrap, there are classes that you can use to create panels, that is content that's a little bit offset from the other content around it. And all I need to do to create a panel is to come out to my enclosing HTML element, this section, and let's give it a style of panel and panel, a default-looking panel. I can take my h3, there's a special class panel-heading that will style this to be a little more outstanding. And then this particular div that displays information about the restaurant, let's turn this into the body of the panel. And the links that appear down here, let's use a class to turn these into the footer for our panel. I also want these links to appear as buttons instead of links, so I'm going to use Alt and select two lines here inside of Visual Studio that will allow me to type into both lines, and I'll give both of these anchor tags a class of btn btn-default. And once I save this partial view, I should be able to see a noticeable difference in the display of my restaurants. Still not perfect, and I think you could probably do better, and we still might want to add styling to the details for a restaurant and also for the form that we used to create a new restaurant. Of course, in order to reach this form, I'm going to need to log in. And there's many different Bootstrap classes that you can use to style forms. I'm going to leave that as an exercise for you to try. What I want to focus on next though is how to provide some client-side validation for these forms. Currently, if I come to this form and just click Save, there's a request that goes all the way back to the server where we execute our model validation logic, and then a response has to come back here to the client to say sorry, you didn't fill in the name field. With ASP.NET, there's support to do all of this validation, simple validations, on the client's side. Let's look at how to do that next.
Enabling Client-side Validation
On the create form, I want to right-click this element to inspect this element just to point out the data- attributes that are present on this input. When I'm using tag helpers like asp-for on input elements, the framework's going to look at the data annotations that are associated with a particular model property and render these data-validation attributes that describe the rules that apply to this particular input. So you should remember earlier in the course we added attributes to the Name property of a restaurant that says this property is required. So when the framework renders this input, it adds data-val-required attribute with an error message that we can display on the client if this validation does not succeed, if there is no restaurant name. We also had a maximum length specified, so you can see data-val-maxlength-max, 80 characters is the maximum that we'll take for a restaurant name. Now the browser itself does not know what to do with these data-val attributes, but Microsoft has built a validation library that builds on top of jQuery and the jQuery validation plugin that can look for these attributes in our HTML and help us enforce these validation rules on the client. In other words, right now, if I look at the Network tab and I click Save without entering a restaurant name, then we just sent off a request to the home create action where the web server itself had to do the validation. I'd like to do this validation on the client side. It's a very simple validation. And to do that, I just need to get three more JavaScript files loaded onto this page. There jQuery, there's the jQuery validation plugin, and then there's a Microsoft library known as jquery.validate.unobtrusive, which knows how to work with the special attributes that the MVC framework renders to create rules for that validation plugin that will perform this validation for me on the client side. So the first step here is installing those libraries. Let's open up our package.json file. In addition to Bootstrap, I also want to install jQuery, and I will take the latest version, 3.2.1. I also need the jQuery plugin, which is jquery-validation. We'll take the latest version here, 1.17. And then I need the library provided by Microsoft, which is jquery-validation-unobtrusive, and we'll take the latest version of this library also. Save this file, and these files should now appear in node_modules where I can serve them up. Now one approach I could take to enforcing validation here on the create action is to only add these scripts into the application when I'm rendering a form that needs validation. So I could go to the create view and add script tags that will load these scripts just for the create view. But I'm going to make things a little bit simpler. I'm just going to go ahead inside of the Layout view and just say right from the start let's always have these scripts in place, and then they'll be cached, and we don't have to load them on subsequent requests. So let's create a script tag and set the source to first of all we'll have to load jQuery. Now there is no official standard on how these packages will lay out on your file system. Sometimes you have to poke around a little bit to find the exact file that you want to load. And with jQuery, I can see, in fact, I can actually just drag this in instead of typing it out, but with jQuery, I can see that I go to the node_modules folder, jquery, there's a distribution folder, and then there's jquery.js. The other script files don't always follow that same approach with a distribution folder, but if I go into jquery-validation, there is a distribution folder, and here is jquery.validate.js. Let me drag this over. And finally, I need to load the unobtrusive bit of this from Microsoft, so jquery.validate.unobtrusive. Notice there's no distribution folder. That's okay; this will still work. Let's drag it into the bottom of the page, do some reformatting here, and I should now have the scripts in effect in the browser for every view that uses this Layout page. The one exception you might remember is the edit form, which we rendered with a different layout view for our Razor Pages. So if we want that form and that page to also render with Bootstrap styles and client-side validation, we'll also need to make these changes in that other layout view. But now what I should be able to do is refresh this page. I don't want to resubmit the form, so let's just go to /home/create and issue a GET request. I just want to do a quick check here, and I can see that my script files loaded successfully, 200 results on all of those network requests. And now if I try to save this form without first entering a restaurant name, there's no network request that was sent off. Instead, this jQuery validation system that I've now plugged into the application jumped in, hooked into that form, and said sorry, but we're not going to send this request off because you didn't meet all of the validation rules.
Using CDNs and Fallbacks
For applications that use a large amount of JavaScript, one of the best practices is to have a build process for your JavaScript. A build process uses a task runner typically like Gulp or Grunt, or a tool like webpack, and this build process will concatenate all of your JavaScript files together, minifiy your JavaScript, and try to minimize the size and number of script downloads that you have on a page. You might also consider putting your own custom scripts or using scripts from a content delivery network, a CDN. A CDN can typically deliver script files faster than from your own local servers, but you'll always have to do some testing to reveal the true numbers. Now if you have an application that just relies on a few popular third-party scripts, like jQuery, you might consider just going the CDN route. And that's the approach I'm going to show you. We're going to use a content delivery network. This is a content delivery network managed by Microsoft. You can find out more if you do a search for the Microsoft Ajax Content Delivery Network. On this page, you'll see a list of all the libraries that are hosted inside of the content delivery network, and I can click on something like the Bootstrap link here that shows me the different Bootstrap releases that are supported and provides me with the URLs that I can use to load files like bootstrap.css off of the content delivery network. So what I want to do is set up our application so that when we are running in production we're loading libraries like Bootstrap and jQuery off of the CDN. But when we're in development, it's sometimes easier to debug an application if we're just loading scripts from our local node_modules folder or wwwroot folder. And this is something that I can achieve with tag helpers. There's a tag helper that looks like a full HTML attribute that's known as environment. And what I can do is wrap my markup inside of an environment tag, and then I can tell the framework in which environment do I want this HTML to be rendered. I can do that with a parameter to the environment tag helper. Notice there's exclude and include and names. So I could say for this environment only render the following markup when I'm in the Development environment. So this could be script tags, it could be div tags, it could be link tags in the header, any HTML that you have inside the environment tag. This will only render when the ASP.NET Core _environtment variable is set to Development; otherwise, I want to load these scripts from the Microsoft CDN. And because this requires an inordinate amount of typing, what I'm going to do is copy and paste some code from off screen and then explain what this code is doing. So what I've pasted in is the following section, the following environment tag. We have one environment tag for Development. We have another environment tag that says exclude Development, so render this for any environment that is not development. And now, instead of having a script tag that points to jquery.js locally, I'll have a script tag that points to jquery, a minified version on the aspnetcdn. And then what are some of these additional attributes? Well the crossorigin and the integrity attribute, these allow the browser to ensure that you're loading a script tag that hasn't been maliciously modified on the CDN somehow. It's part of a web standard known as subresource integrity. So crossorigin and integrity are here for the browser to interpret while the asp-fallback source and asp-fallback-test are additional tag helpers that help me in the following scenario. Let's imagine that the CDN is unreachable for a moment when the user loads this page, and if a CDN in unreachable and jQuery doesn't load onto the page and Bootstrap doesn't load, my application can look strange and not behave correctly. What these tag helpers will do is inject additional JavaScript into the page to test to see if the load from the CDN was successful. Essentially, this fallback test can test for the presence of window.jQuery because when jQuery installs, it will create a jQuery property on the window object. So if this value does not exist after the request for the source is complete, then we know something has gone wrong, and the code will then fall back to the alternate source, which is to try to load jQuery from a node_modules folder. There's the same process here for jQuery validation plugin. We'll load it from the CDN. There's a fallback test to make sure that window.jQuery and window.jQuery.validator are present. And then the same process for jquery.validation.unobtrusive. We have a fallback test, a fallback source, and subresource integrity. Let's also load Bootstrap from the CDN. So back here at the top of the Layout page, I'm going to take this link, temporarily cut that link, and create an environment tag that will only render its contents for the Development environment, and then paste my link back into the page. And now let me go off screen and again grab some code because this a lot to type and paste this in. What we will do is when we are not in Development, we will have a link that points to Bootstrap on the CDN, Once again, we have a fallback that will go into the node_modules folder, and we also have some fallback tests implemented through some new tag helpers, asp-fallback-test-class and property and value that can ensure that Bootstrap was properly loaded. And with these changes in place, I can be sure that when I deploy my application and I'm running in production my users are receiving minified files from a CDN server that should be able to serve those files relatively quickly.
Summary
In this module, we learned how to use the node package manger from Visual Studio to install front-end frameworks like Bootstrap and libraries like jQuery. We added these resources to our page and saw how to use some Bootstrap classes and also enforced client-side validation rules. We learned about the environment tag helper and how it can allow us to use local scripts for debugging, but deliver scripts over a content delivery network for production. And that concludes my course on ASP.NET Core. I hope you enjoyed the course and build many successful applications on ASP.NET Core in the future.
Course author
Scott Allen
Scott has over 15 years of experience in commercial software
development and is a frequent speaker at national conferences,
and local user groups. Scott is a Microsoft MVP and...
Course info
LevelBeginner
Rating
(671)
My rating
Duration5h 49m
Updated9 Nov 2017
Share course