Treinamento WEB
Treinamento WEB
Treinamento WEB
20486D
Developing ASP.NET Core MVC Web
Applications
MCT USE ONLY. STUDENT USE PROHIBITED
ii Developing ASP.NET Core MVC Web Applications
Information in this document, including URL and other Internet Web site references, is subject to change
without notice. Unless otherwise noted, the example companies, organizations, products, domain names,
e-mail addresses, logos, people, places, and events depicted herein are fictitious, and no association with
any real company, organization, product, domain name, e-mail address, logo, person, place or event is
intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the
user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in
or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical,
photocopying, recording, or otherwise), or for any purpose, without the express written permission of
Microsoft Corporation.
Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property
rights covering subject matter in this document. Except as expressly provided in any written license
agreement from Microsoft, the furnishing of this document does not give you any license to these
patents, trademarks, copyrights, or other intellectual property.
The names of manufacturers, products, or URLs are provided for informational purposes only and
Microsoft makes no representations and warranties, either expressed, implied, or statutory, regarding
these manufacturers or the use of the products with any Microsoft technologies. The inclusion of a
manufacturer or product does not imply endorsement of Microsoft of the manufacturer or product. Links
may be provided to third party sites. Such sites are not under the control of Microsoft and Microsoft is not
responsible for the contents of any linked site or any link contained in a linked site, or any changes or
updates to such sites. Microsoft is not responsible for webcasting or any other form of transmission
received from any linked site. Microsoft is providing these links to you only as a convenience, and the
inclusion of any link does not imply endorsement of Microsoft of the site or the products contained
therein.
Released: 03/2019
MCT USE ONLY. STUDENT USE PROHIBITED
MICROSOFT LICENSE TERMS
MICROSOFT INSTRUCTOR-LED COURSEWARE
These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its
affiliates) and you. Please read them. They apply to your use of the content accompanying this agreement which
includes the media on which you received it, if any. These license terms also apply to Trainer Content and any
updates and supplements for the Licensed Content unless other terms accompany those items. If so, those terms
apply.
BY ACCESSING, DOWNLOADING OR USING THE LICENSED CONTENT, YOU ACCEPT THESE TERMS.
IF YOU DO NOT ACCEPT THEM, DO NOT ACCESS, DOWNLOAD OR USE THE LICENSED CONTENT.
If you comply with these license terms, you have the rights below for each license you acquire.
1. DEFINITIONS.
a. “Authorized Learning Center” means a Microsoft IT Academy Program Member, Microsoft Learning
Competency Member, or such other entity as Microsoft may designate from time to time.
b. “Authorized Training Session” means the instructor-led training class using Microsoft Instructor-Led
Courseware conducted by a Trainer at or through an Authorized Learning Center.
c. “Classroom Device” means one (1) dedicated, secure computer that an Authorized Learning Center owns
or controls that is located at an Authorized Learning Center’s training facilities that meets or exceeds the
hardware level specified for the particular Microsoft Instructor-Led Courseware.
d. “End User” means an individual who is (i) duly enrolled in and attending an Authorized Training Session
or Private Training Session, (ii) an employee of a MPN Member, or (iii) a Microsoft full-time employee.
e. “Licensed Content” means the content accompanying this agreement which may include the Microsoft
Instructor-Led Courseware or Trainer Content.
f. “Microsoft Certified Trainer” or “MCT” means an individual who is (i) engaged to teach a training session
to End Users on behalf of an Authorized Learning Center or MPN Member, and (ii) currently certified as a
Microsoft Certified Trainer under the Microsoft Certification Program.
g. “Microsoft Instructor-Led Courseware” means the Microsoft-branded instructor-led training course that
educates IT professionals and developers on Microsoft technologies. A Microsoft Instructor-Led
Courseware title may be branded as MOC, Microsoft Dynamics or Microsoft Business Group courseware.
h. “Microsoft IT Academy Program Member” means an active member of the Microsoft IT Academy
Program.
i. “Microsoft Learning Competency Member” means an active member of the Microsoft Partner Network
program in good standing that currently holds the Learning Competency status.
j. “MOC” means the “Official Microsoft Learning Product” instructor-led courseware known as Microsoft
Official Course that educates IT professionals and developers on Microsoft technologies.
k. “MPN Member” means an active Microsoft Partner Network program member in good standing.
MCT USE ONLY. STUDENT USE PROHIBITED
l. “Personal Device” means one (1) personal computer, device, workstation or other digital electronic device
that you personally own or control that meets or exceeds the hardware level specified for the particular
Microsoft Instructor-Led Courseware.
m. “Private Training Session” means the instructor-led training classes provided by MPN Members for
corporate customers to teach a predefined learning objective using Microsoft Instructor-Led Courseware.
These classes are not advertised or promoted to the general public and class attendance is restricted to
individuals employed by or contracted by the corporate customer.
n. “Trainer” means (i) an academically accredited educator engaged by a Microsoft IT Academy Program
Member to teach an Authorized Training Session, and/or (ii) a MCT.
o. “Trainer Content” means the trainer version of the Microsoft Instructor-Led Courseware and additional
supplemental content designated solely for Trainers’ use to teach a training session using the Microsoft
Instructor-Led Courseware. Trainer Content may include Microsoft PowerPoint presentations, trainer
preparation guide, train the trainer materials, Microsoft One Note packs, classroom setup guide and Pre-
release course feedback form. To clarify, Trainer Content does not include any software, virtual hard
disks or virtual machines.
2. USE RIGHTS. The Licensed Content is licensed not sold. The Licensed Content is licensed on a one copy
per user basis, such that you must acquire a license for each individual that accesses or uses the Licensed
Content.
2.1 Below are five separate sets of use rights. Only one set of rights apply to you.
2.2 Separation of Components. The Licensed Content is licensed as a single unit and you may not
separate their components and install them on different devices.
2.3 Redistribution of Licensed Content. Except as expressly provided in the use rights above, you may
not distribute any Licensed Content or any portion thereof (including any permitted modifications) to any
third parties without the express written permission of Microsoft.
2.4 Third Party Notices. The Licensed Content may include third party code tent that Microsoft, not the
third party, licenses to you under this agreement. Notices, if any, for the third party code ntent are included
for your information only.
2.5 Additional Terms. Some Licensed Content may contain components with additional terms,
conditions, and licenses regarding its use. Any non-conflicting terms in those conditions and licenses also
apply to your use of that respective component and supplements the terms described in this agreement.
a. Pre-Release Licensed Content. This Licensed Content subject matter is on the Pre-release version of
the Microsoft technology. The technology may not work the way a final version of the technology will
and we may change the technology for the final version. We also may not release a final version.
Licensed Content based on the final version of the technology may not contain the same information as
the Licensed Content based on the Pre-release version. Microsoft is under no obligation to provide you
with any further content, including any Licensed Content based on the final version of the technology.
b. Feedback. If you agree to give feedback about the Licensed Content to Microsoft, either directly or
through its third party designee, you give to Microsoft without charge, the right to use, share and
commercialize your feedback in any way and for any purpose. You also give to third parties, without
charge, any patent rights needed for their products, technologies and services to use or interface with
any specific parts of a Microsoft technology, Microsoft product, or service that includes the feedback.
You will not give feedback that is subject to a license that requires Microsoft to license its technology,
technologies, or products to third parties because we include your feedback in them. These rights
survive this agreement.
c. Pre-release Term. If you are an Microsoft IT Academy Program Member, Microsoft Learning
Competency Member, MPN Member or Trainer, you will cease using all copies of the Licensed Content on
the Pre-release technology upon (i) the date which Microsoft informs you is the end date for using the
Licensed Content on the Pre-release technology, or (ii) sixty (60) days after the commercial release of the
technology that is the subject of the Licensed Content, whichever is earliest (“Pre-release term”).
Upon expiration or termination of the Pre-release term, you will irretrievably delete and destroy all copies
of the Licensed Content in your possession or under your control.
MCT USE ONLY. STUDENT USE PROHIBITED
4. SCOPE OF LICENSE. The Licensed Content is licensed, not sold. This agreement only gives you some
rights to use the Licensed Content. Microsoft reserves all other rights. Unless applicable law gives you more
rights despite this limitation, you may use the Licensed Content only as expressly permitted in this
agreement. In doing so, you must comply with any technical limitations in the Licensed Content that only
allows you to use it in certain ways. Except as expressly permitted in this agreement, you may not:
• access or allow any individual to access the Licensed Content if they have not acquired a valid license
for the Licensed Content,
• alter, remove or obscure any copyright or other protective notices (including watermarks), branding
or identifications contained in the Licensed Content,
• modify or create a derivative work of any Licensed Content,
• publicly display, or make the Licensed Content available for others to access or use,
• copy, print, install, sell, publish, transmit, lend, adapt, reuse, link to or post, make available or
distribute the Licensed Content to any third party,
• work around any technical limitations in the Licensed Content, or
• reverse engineer, decompile, remove or otherwise thwart any protections or disassemble the
Licensed Content except and only to the extent that applicable law expressly permits, despite this
limitation.
5. RESERVATION OF RIGHTS AND OWNERSHIP. Microsoft reserves all rights not expressly granted to
you in this agreement. The Licensed Content is protected by copyright and other intellectual property laws
and treaties. Microsoft or its suppliers own the title, copyright, and other intellectual property rights in the
Licensed Content.
6. EXPORT RESTRICTIONS. The Licensed Content is subject to United States export laws and regulations.
You must comply with all domestic and international export laws and regulations that apply to the Licensed
Content. These laws include restrictions on destinations, end users and end use. For additional information,
see www.microsoft.com/exporting.
7. SUPPORT SERVICES. Because the Licensed Content is “as is”, we may not provide support services for it.
8. TERMINATION. Without prejudice to any other rights, Microsoft may terminate this agreement if you fail
to comply with the terms and conditions of this agreement. Upon termination of this agreement for any
reason, you will immediately stop all use of and delete and destroy all copies of the Licensed Content in
your possession or under your control.
9. LINKS TO THIRD PARTY SITES. You may link to third party sites through the use of the Licensed
Content. The third party sites are not under the control of Microsoft, and Microsoft is not responsible for
the contents of any third party sites, any links contained in third party sites, or any changes or updates to
third party sites. Microsoft is not responsible for webcasting or any other form of transmission received
from any third party sites. Microsoft is providing these links to third party sites to you only as a
convenience, and the inclusion of any link does not imply an endorsement by Microsoft of the third party
site.
10. ENTIRE AGREEMENT. This agreement, and any additional terms for the Trainer Content, updates and
supplements are the entire agreement for the Licensed Content, updates and supplements.
12. LEGAL EFFECT. This agreement describes certain legal rights. You may have other rights under the laws
of your country. You may also have rights with respect to the party from whom you acquired the Licensed
Content. This agreement does not change your rights under the laws of your country if the laws of your
country do not permit it to do so.
13. DISCLAIMER OF WARRANTY. THE LICENSED CONTENT IS LICENSED "AS-IS" AND "AS
AVAILABLE." YOU BEAR THE RISK OF USING IT. MICROSOFT AND ITS RESPECTIVE
AFFILIATES GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. YOU MAY
HAVE ADDITIONAL CONSUMER RIGHTS UNDER YOUR LOCAL LAWS WHICH THIS AGREEMENT
CANNOT CHANGE. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT AND
ITS RESPECTIVE AFFILIATES EXCLUDES ANY IMPLIED WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
14. LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. YOU CAN RECOVER FROM
MICROSOFT, ITS RESPECTIVE AFFILIATES AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP
TO US$5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL,
LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.
It also applies even if Microsoft knew or should have known about the possibility of the damages. The
above limitation or exclusion may not apply to you because your country may not allow the exclusion or
limitation of incidental, consequential or other damages.
Please note: As this Licensed Content is distributed in Quebec, Canada, some of the clauses in this
agreement are provided below in French.
Remarque : Ce le contenu sous licence étant distribué au Québec, Canada, certaines des clauses
dans ce contrat sont fournies ci-dessous en français.
EXONÉRATION DE GARANTIE. Le contenu sous licence visé par une licence est offert « tel quel ». Toute
utilisation de ce contenu sous licence est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie
expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection dues
consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties
implicites de qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues.
EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits
prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre
pays si celles-ci ne le permettent pas.
Acknowledgements
Microsoft Learning wants to acknowledge and thank the following for their contribution toward
developing this title. Their effort at various stages in the development has ensured that you have a good
classroom experience.
Masha Borchenko is a Senior Front-End Developer and Lecturer at Sela Group. She specializes in cross-
platform development with frameworks such as Angular, React, and Ionic and with the latest W3C
standards. Masha has more than 7 years of industry experience in various client technologies.
MCT USE ONLY. STUDENT USE PROHIBITED
xiii Developing ASP.NET Core MVC Web Applications
Contents
Module 1: Exploring ASP.NET Core MVC
Module Overview 01-1
Lesson 1: Overview of Microsoft Web Technologies 01-3
Lesson 2: Overview of ASP.NET 4.x 01-12
Lesson 3: Introduction to ASP.NET Core MVC 01-22
Lab: Exploring ASP.NET Core MVC 01-29
Module Review and Takeaways 01-31
MCT USE ONLY. STUDENT USE PROHIBITED
About This Course xix
Course Description
In this course, students will learn to develop advanced Microsoft ASP.NET Core Model-View-Controller
(MVC) applications. The focus will be on coding activities that enhance the performance and scalability of
the website application.
Audience
This course is intended for professional web developers who use Microsoft Visual Studio in an individual-
based or team-based scenario in small to large development environments. Candidates for this course are
interested in developing advanced web applications and want to manage the rendered HTML
comprehensively. They want to create websites that separate the user interface, data access, and
application logic.
Student Prerequisites
This course requires that you meet the following prerequisites:
• Experience with Visual Studio 2017.
• Experience with C# programming and concepts such as Lambda expressions, LINQ, and anonymous
types.
• Experience in using the Microsoft .NET Framework.
• Experience with HTML, CSS, and JavaScript.
• Experience with querying and manipulating data with ADO.NET.
• Knowledge of XML and JSON data structures.
Course Objectives
After completing this course, students will be able to:
• Describe the Microsoft Web Technologies stack and select an appropriate technology to use to
develop any given application.
• Design the architecture and implementation of a web application that will meet a set of functional
requirements, user interface requirements, and address business models.
• Configure the pipeline of ASP.NET Core web applications by using middleware and leverage
dependency injection across MVC applications.
• Add controllers to an MVC application to manage user interaction, update models, and select and
return views.
• Develop a web application that uses the ASP.NET Core routing engine to present friendly URLs and a
logical navigation hierarchy to users.
• Create views, which display and edit data and interact with models and controllers, in an MVC
application.
• Create MVC models and write code that implements business logic within model methods, properties,
and events.
• Connect an ASP.NET Core application to a database by using Entity Framework Core.
• Implement a consistent look and feel across an entire MVC web application.
MCT USE ONLY. STUDENT USE PROHIBITED
xx About This Course
• Write JavaScript code that runs on the client-side and utilizes the jQuery script library to optimize the
responsiveness of an MVC web application.
• Add client side packages and configure Task Runners.
• Run unit tests and debugging tools against a web application in Visual Studio 2017.
• Write an MVC application that authenticates and authorizes users to access content securely by using
Identity.
• Build an MVC application that resists malicious attacks.
• Use caching to accelerate responses to user requests.
• Use SignalR to enable two-way communication between client and server.
• Describe what a Web API is and why developers might add a Web API to an application.
• Describe how to package and deploy an ASP.NET Core MVC web application from a development
computer to a web server.
Course Outline
The course outline is as follows:
Module 1. Exploring ASP.NET Core MVC
This module describes the Microsoft Web Technologies stack and compares the various ASP.NET
programming models. It explores the structure of an MVC web application and identifies features such as
models, views, controllers, configuration files, style sheets, and script files.
Module 2. Designing ASP.NET Core MVC Web Applications
This module explains how to plan the overall architecture of an ASP.NET Core MVC web application. It
also explains how to plan the models, controllers, and views that are required to implement a given set of
functional requirements.
Module 3. Configure Middleware and Services in ASP.NET Core
This module explains how to use existing middleware to set up an ASP.NET Core application and how to
create your own middleware and use it to define custom behavior. The module also describes the basics
of Dependency Injection and how it is used in ASP.NET Core. It also describes how to create a custom
service, configure its scope, and inject it to middleware and ASP.NET Core MVC controllers.
Module 4. Developing Controllers
This module explains how to create controllers in an ASP.NET Core MVC web application. The module also
describes how to write code in action filters that executes before or after a controller action and how to
configure routes in an MVC application.
Module 5. Developing Views
This module explains how to create an MVC view and add Razor markup to it, use HTML helpers and tag
helpers in a view, and reuse Razor markup in multiple locations throughout an MVC application.
Module 6. Developing Models
This module explains how to add a model to an MVC application, display the model data in a view by
using helpers, and use forms with data annotations and validation.
Module 7. Using Entity Framework Core in ASP.NET Core
This module explains how to connect an application to a database to access and store data. It also
explains what Entity Framework Core is and how to use Entity Framework Core to connect to a database
from ASP.NET Core MVC applications.
MCT USE ONLY. STUDENT USE PROHIBITED
About This Course xxi
This module explains how to unit test the components of an MVC application and to implement
exception handling strategy for the application. It also describes how to log MVC applications.
Module 11. Managing Security
This module explains how to implement authentication and authorization for accessing an MVC web
application. It also explains how to build an MVC web application that resists malicious attacks.
Module 12. Performance and Communication
This module explains how to improve the performance of ASP.NET Core applications by using caching
and how to use state management technologies to improve the client experience by providing a
consistent experience for the user. It also describes how to implement two-way communication by using
SignalR.
Module 13. Implementing Web APIs
This module provides an overview of Web API and explains how to develop a Web API and call it from
other applications.
Module 14. Hosting and Deployment
This module explains how to host and deploy a completed MVC application on Internet Information
Services (IIS) and Microsoft Azure. It also describes how to use Azure to improve the capabilities of web
applications.
MCT USE ONLY. STUDENT USE PROHIBITED
xxii About This Course
Exam/Course Mapping
The following materials are included with your kit:
• Course Handbook is a succinct classroom learning guide that provides the critical technical
information in a crisp, tightly focused format, which is essential for an effective in-class learning
experience
You may be accessing either a printed course handbook or digital courseware material via the Skillpipe
reader by Arvato. Your Microsoft Certified Trainer will provide specific details, but both printed and digital
versions contain the following:
o Lessons guide you through the learning objectives and provide the key points that are critical to
the success of the in-class learning experience.
o Labs provide a real-world, hands-on platform for you to apply the knowledge and skills learned
in the module.
o Module Reviews and Takeaways sections provide on-the-job reference material to boost
knowledge and skills retention.
Module 1
Exploring ASP.NET Core MVC
Contents:
Module Overview 01-1
Lesson 1: Overview of Microsoft Web Technologies 01-3
Lesson 2: Overview of ASP.NET 4.x 01-12
Lesson 3: Introduction to ASP.NET Core MVC 01-22
Lab: Exploring ASP.NET Core MVC 01-29
Module Review and Takeaways 01-31
Module Overview
Microsoft ASP.NET Core MVC and the other web technologies of the ASP.NET Core can help you create
and host dynamic, powerful, and extensible web applications. ASP.NET Core, of which ASP.NET Core MVC
is part, is an open-source, cross-platform framework that allows you to build web applications. You can
develop and run ASP.NET Core web applications on Windows, macOS, Linux, or any other platform that
supports it.
ASP.NET Core MVC supports agile, test-driven development cycle. It also allows you to use the latest
HTML standard and Front-End frameworks such as Angular, React, and more.
To build robust web applications, you need to be familiar with the technologies and products in the
Microsoft web stack that we will cover in this module:
• You need to know how ASP.NET applications work with Web servers such as Kestrel and Internet
Information Services (IIS).
• It is recommended that you be familiar with Visual Studio 2017 but note that with ASP.NET Core you
can choose another Integrated Development Environment (IDE) such as Visual Studio Code.
• It is also recommended that you be familiar with Microsoft SQL Server, Entity Framework, Microsoft
Azure, and Microsoft Azure SQL Database to deliver engaging web pages to visitors to the site.
• To choose a programming model that best suits a set of business requirements, you need to know
how Model-View-Controller (MVC) applications differ from other ASP.NET programming models.
The variety of web applications that you will design and develop in the labs throughout this course will
help you develop web applications that fulfill business needs.
Objectives
After completing this module, you will be able to:
• Understand the variety of technologies available in the Microsoft web stack.
• Describe the different programming models available for developers in ASP.NET.
• Choose between ASP.NET Core and ASP.NET 4.x.
MCT USE ONLY. STUDENT USE PROHIBITED
01-2 Exploring ASP.NET Core MVC
• Describe the role of ASP.NET Core MVC in the web technologies stack, and how to use ASP.NET Core
MVC to build web applications.
• Distinguish between MVC models, MVC controllers, and MVC views.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-3
Lesson 1
Overview of Microsoft Web Technologies
Before you use ASP.NET Core MVC, you need to know how it fits into ASP.NET Core in general, and where
ASP.NET Core fits into the entire web technologies stack and particularly in the Microsoft web
technologies stack. You should know how the ASP.NET Core websites are hosted, how they run server-side
code on web servers and how the client-side code is run. You should also know which part of the code
belongs to the server side and which part belongs to the client-side.
Lesson Objectives
After completing this lesson, you will be able to:
• Introduce Microsoft web technologies.
• Provide an overview of ASP.NET.
• Provide an overview of client-side web technologies, including AJAX and JavaScript libraries.
• Provide an overview of hosting technologies and Microsoft Azure particularly.
Developer Tools
You can create simple websites with text and
images by using a text editor, such as Notepad.
However, most websites require complex actions
to be performed on the server-side, such as
database operations, email delivery, complex calculations, or graphics rendering. To create such complex,
highly functional, and engaging websites quickly and easily, Microsoft provides several tools. The main
ones are Microsoft Visual Studio 2017 and Microsoft Visual Studio Code.
• Visual Studio. You can use Visual Studio 2017, an IDE, to create custom applications based on
Microsoft technologies, regardless of whether these applications run on the web, on desktops, on
mobile devices, or by using Microsoft cloud services. Visual Studio 2017 has rich facilities for
designing, coding, and debugging any ASP.NET Core web application, including MVC applications.
• Visual Studio Code. It is a lightweight, free, and open-source code editor developed by Microsoft
that is available for Windows, macOS, and Linux. Visual Studio Code has built-in support for
JavaScript, HTML, cascading style sheets (CSS), Typescript, and Node.js and it has many extensions
that allow you to create websites by using the language of your choice including C#, C/C++, Python,
and more. It has a built-in integration with Git and powerful debugging tools for the web. You can
use Visual Studio Code to create ASP.NET Core applications quickly by using the C# extension
available on Visual Studio Code marketplace.
MCT USE ONLY. STUDENT USE PROHIBITED
01-4 Exploring ASP.NET Core MVC
Hosting Technologies
Regardless of the tool you use to build a web application, you need a web server to host the web
application. When users visit your site, the host server responds by rendering the HTML and returning it to
the user’s browser for display. The host server may query a database before it builds the HTML, and the
host server may perform other actions such as sending emails or saving uploaded files. When you finish
building the web application and make it ready for users to access on an intranet or over the internet, you
must use a fully functional web server such as:
• Microsoft Internet Information Services. IIS is an advanced website hosting technology. You can
install IIS servers on your local network or perimeter network or employ IIS servers hosted by an
internet service provider (ISP). IIS can host any ASP.NET, PHP, or Node.js websites. You can scale up IIS
to host large and busy websites by configuring server farms that contain multiple IIS servers, all
serving the same content.
• Microsoft Azure. Azure is a cloud platform that provides on-demand services to build, deploy, host,
and manage web applications through Microsoft-managed data centers. When you use Azure
services, you need to pay only for the data that your website serves. Also, you need not worry about
building a scalable infrastructure because Azure automatically adds resources as your website grows.
Most websites require a database to manage data such as product details, user information, and
discussion topics. You can choose from the following Microsoft technologies to manage your data:
• Microsoft SQL Server. SQL Server is a premium database server that you can use to host any
database from the simplest to the most complex. SQL Server can scale up to support very large
databases and very large numbers of users. You can build large SQL Server clusters to ensure the best
availability and reliability. Many of the world’s largest organizations rely on SQL Server to host data.
• Microsoft Azure SQL Database. Azure SQL Database is a cloud database platform and a part of
Azure. Using Azure SQL Database, you can deploy your database and pay only for the data that you
use. You need not worry about managing your database infrastructure because your database scales
up automatically as your website grows.
Server-Side Execution
Microsoft ASP.NET is a server-side web development framework that allows you to build server-based
web applications. It was first introduced in 2002 with the first release of .NET Framework. Today you can
choose between developing in ASP.NET 4.x and ASP.NET Core. ASP.NET 4.x is the latest version of the
original ASP.NET. ASP.NET Core is a redesign of the original ASP.NET, and it is a much leaner and modular
version.
You can write both ASP.NET 4.x and ASP.NET Core in Visual Studio 2017. The server-side code accesses
the database, renders HTML pages, and returns them to the web browser. The MVC programming model
is a part of both ASP.NET 4.x and ASP.NET Core. Other server-side technologies of choice include PHP and
Node.js.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-5
Client-Side Execution
Most web browsers can run code written in the JavaScript language. This code is sent to the browser as
text within a rendered HTML page or in a separate .js file. Because JavaScript is local to the browser, it can
respond very quickly to user actions such as clicking, pointing, or dragging.
Many JavaScript libraries are available that help accelerate client code development. For example, the
popular jQuery library makes it simple to access page elements and manipulate them by changing their
style or content. When you use such libraries, you can write functions with a few lines of code where
otherwise it might require much more lines of your own JavaScript code.
ASP.NET applications can also use the Asynchronous JavaScript and XML (AJAX) technology on the client
computer to interact with the web server. You can use AJAX to update a small section of an HTML page,
instead of reloading the entire page from the server. Such partial page updates help make web pages
more responsive and engaging.
One of the most popular types of AJAX-based web application is Single Page Applications (SPA). In those
applications, parts of the page change dynamically based on the user’s interaction with the page. There
are many client-side frameworks and libraries that help developers to create such applications easily.
Examples of SPA are the Angular framework and the React library. Both ASP.NET 4.x and ASP.NET Core
integrate easily with these client-side SPA frameworks and in ASP.NET Core there are built-in templates
for creating such applications.
Overview of ASP.NET
You can use ASP.NET to develop database-
driven, highly functional, and scalable dynamic
websites that use client-side and server-side
code. You can create many kinds of websites with
ASP.NET, for example, web portals, online
shopping sites, blogs, and wikis.
Programming Models
When you use ASP.NET to build an application,
you are not restricted to a single style of
programming; instead, you can choose from
several different programming models. Some
programming models are available only in
ASP.NET 4.x, some are available only in ASP.NET Core and some are available in both ASP.NET 4.x and
ASP.NET Core. Each programming model has a typical structure in the development environment and it
stores code in specific places in the web hierarchy:
• Web Pages. Available only in ASP.NET 4.x.
When you build a site by using Web Pages, you can write code by using the C# or Visual Basic
programming language. If you write C# code, these pages have a .cshtml file extension. If you write
Visual Basic code, these pages have a .vbhtml file extension. ASP.NET 4.x runs the code in these pages
on the server to render data from a database, respond to a form post, or perform other actions. This
programming model is simple and easy to learn and is suited for simple data-driven sites.
• Web Forms. Available only in ASP.NET 4.x.
When you build a site by using Web Forms, you employ a programming model with rich server-side
controls and a page life cycle that is not unlike building desktop applications. Built-in controls include
buttons, text boxes, and grid views for displaying tabulated data. You can also add third-party
MCT USE ONLY. STUDENT USE PROHIBITED
01-6 Exploring ASP.NET Core MVC
controls or build custom controls. To respond to user actions, you can attach event handlers
containing code to the server-side controls. For example, to respond to a click on a button called
firstButton, you could write code in the firstButton_Click() event handler.
As the name Web API suggests, the API exists over the web and can be accessed by using HTTP
protocol.
JavaScript
JavaScript is a simple scripting language that has a syntax similar to C#, and it is supported by all modern
web browsers. A scripting language is not compiled. Instead, a script engine interprets the code at run
time so that the web browser can run the code.
Note: Besides JavaScript, there are other scripting languages, but JavaScript is supported by
virtually every web browser. This is not true of any other scripting language. Unless your target
audience is very limited, and you have control over the browser used by your users, you should
use JavaScript because of its almost universal support.
You can include JavaScript code in your ASP.NET pages. JavaScript is a powerful language but can require
many lines of code to achieve visual effects or call external services. Script libraries contain pre-built
JavaScript functions that help implement common actions that you might want to perform on the client
side. You can use a script library, instead of building all your own JavaScript code from the start. Using a
script library helps reduce development time and effort.
Different browsers interpret JavaScript differently. When you develop your website, you don't know what
browsers your visitors will use. Therefore, you must write JavaScript that works around browser
compatibility.
jQuery
jQuery is one of the most popular JavaScript libraries. It provides elegant functions for interacting with the
HTML elements on your page and with CSS styles. For example, you can locate all the <div> elements on
a webpage and change their background color by using a single line of code. To achieve the same result
by using JavaScript only, you need to write several lines of code and a programming loop. Furthermore,
the code you write may be different for different browsers. Using jQuery, it is easier to write code to
respond to user actions and to create simple animations. jQuery also handles browser differences for you.
You can use jQuery to call services on remote computers and update the webpage with the results
returned.
MCT USE ONLY. STUDENT USE PROHIBITED
01-8 Exploring ASP.NET Core MVC
Note: There are many other JavaScript libraries and frameworks such as Underscore, D3,
Angular, and React. If you find any of these better suited for a particular task, or if you have
experience in developing web applications by using them, you can include them in your ASP.NET
pages, instead of jQuery or together with it.
AJAX
AJAX is a technology that enables browsers to communicate with web servers asynchronously by using
the XmlHttpRequest object without completely refreshing the page. You can use AJAX in a page to
update a portion of the page with new data, without reloading the entire page. For example, you can use
AJAX to obtain the latest comments on a product, without refreshing the entire product page.
AJAX is implemented entirely in JavaScript, and ASP.NET, by default, relies on the jQuery library to
manage AJAX requests and responses. The code is run asynchronously, which means that the web browser
does not freeze while it waits for an AJAX response from the server. Initially, developers used XML to
format the data returned from the server. More recently, however, developers use JavaScript Object
Notation (JSON) as an alternative format to XML.
Angular
Angular is a front-end framework that allows you to build robust web applications and solve common
development challenges easily. Modern web applications require us to implement complex scenarios such
as data-binding, routing, and internationalization. Angular enables you to do those and keep your code
maintainable, testable, and corresponding to the latest coding best practices.
Angular is written entirely in TypeScript. TypeScript is a superset of JavaScript that was created by
Microsoft allowing you to use the latest JavaScript standards and features without worrying about
backward computability. TypeScript is not processed by browsers and it needs to be transformed into
JavaScript to allow browsers to run your code. This transformation is called Transpiling and it happens
during the build process. Its output is pure JavaScript code. Typescript also has a static optional type
system and it allows you to write object-oriented code.
Angular is also called a client-side Component-Based framework which means that the code you write is
separated into small and functional pieces of code. Angular has a built-in separation of concerns and its
architecture is similar to MVC. It has dedicated code parts for models, controllers, and views. JavaScript
objects serve as models, components serve as controllers, and HTML templates serve as views.
React
React is a JavaScript library that is dedicated to building user interfaces. It focuses on making interactive
UIs and designing views while speeding up the development cycle. It is a Component-Based library where
each component includes its own logic and maintains its own state. React renders views quickly and
focuses on high performing UIs. React Native is a framework that is based on React and JavaScript and
allows developers to create high performing mobile applications.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-9
Hosting Technologies
Every website must be hosted by a web server. A
web server receives requests for web content
from browsers, runs any server-side code, and
returns web pages, images, and other content.
HTTP is used for communication between the
web browser and the web server.
IIS is a web server that can scale up from a small
website running on a single web server to a large
website running on a multiple web server farms.
IIS is available with Windows Server.
Scaling Up IIS
A single web server has limited scalability because it is limited by its processor speed, memory, disk speed,
and other factors. Furthermore, single web servers are vulnerable to hardware failures and outages. For
example, when a single web server is offline, your website is unavailable to users.
You can improve the scalability and resilience of your website by hosting it on a multiple server farm. In
such server farms, many servers share the same server name. Therefore, all servers can respond to browser
requests. A load balancing system such as Windows Network Load Balancing or a hardware-based system
such as Riverbed Cascade distributes requests evenly among the servers in the server farm. If a server fails,
other servers are available to respond to requests, and thereby, the website availability is not interrupted.
IIS servers are designed to work in such server farms and include advanced management tools for
deploying sites and managing member servers.
Perimeter Networks
Web servers, including IIS, are often located on a perimeter network. A perimeter network has a network
segment that is protected from the internet through a firewall that validates and permits incoming HTTP
requests. A second firewall, which permits requests only from the web server, separates the perimeter
MCT USE ONLY. STUDENT USE PROHIBITED
01-10 Exploring ASP.NET Core MVC
network from the internal organizational network. Supporting servers, such as database servers, are
usually located on the internal organizational network.
In this configuration, internet users can reach the IIS server to request pages and the IIS server can reach
the database server to run queries. However, internet users cannot access the database server or any other
internal computer directly. This prevents malicious users from running attacks and ensures a high level of
security.
IIS Express
Internet Information Services Express is a lightweight version of IIS, which is optimized for development
and testing. IIS Express does not provide all the features of IIS on Windows Server. For example, you
cannot create load-balanced server farms by using IIS Express. However, it has all the features necessary to
host rich ASP.NET websites and other websites on a single server. It also does not require administrator
rights to host websites, which means multiple developers can use the same server to host websites
without full control over the host machine.
Microsoft Azure
Azure is a part of Microsoft cloud services for hosting business-critical systems. When you run code on
Azure, the code runs on servers at Microsoft-managed data centers at locations around the globe. You
have several key advantages when you use Azure to host and deploy your web application:
• Flexible scaling. As the needs of your web application and database grow, extra server resources are
automatically allocated. You don’t need to plan for server farms or load balancing systems because
these are built into Azure. Scaling is instantaneous: it doesn’t require purchasing hardware, installing
it in racks, and configuring its software. With the correct architecture, your application can scale
indefinitely as demand increases.
• Flexible pricing. With Azure, you can choose a pay-as-you-go pricing model, which means that you
only pay for the data that you use. This makes Azure very cost-efficient for small websites. It also
makes costs predictable for large websites.
• Worldwide presence. Azure datacenters are spread around the world, and you can easily replicate
your application in multiple datacenters to bring it geographically closer to your customers. This also
improves your application’s availability in case of a catastrophic event that brings down an entire
datacenter in one region, because the application can automatically fail-over to another region.
• First-class security and reliability. Azure datacenters, computers, disks, networking devices, and
other facilities are managed with world-class security and reliability in mind. Hardware failures are
completely masked by having multiple redundant backups, which ensure your application’s reliability.
Some of the application and service types that you can host on Azure include the following:
• Web Apps. You can host an entire website on Azure. Azure supports websites developed in ASP.NET,
PHP, Java, Ruby, Python, and Node.js. You can also deploy websites to Azure directly from Visual
Studio 2017. The Web Apps feature of Azure App Service does not have to contain a user interface; it
can be an HTTP API designed to be called from other software or services, such as a mobile device or
a desktop application.
• Databases. When you use IIS to host a website, you can use SQL Server to store the website database.
When you host a website in Azure, you can use Azure SQL Database, which is a cloud-based database
service based on SQL Server, to host your database. Azure also offers Cosmos DB, which is a hybrid
database that can seamlessly scale across multiple datacenters around the world.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-11
• Virtual machines. You can provision entire virtual servers in Azure to run business-critical back office
software or use the virtual servers as test environments. Virtual servers in Azure can run Windows
Server or Linux.
• Mobile Apps. If you develop apps for mobile devices such as phones and tablets, you can use Azure
to host services that underpin them. Such services can provide user preference storage, data storage,
and other functions.
• Media Services. When you use Azure to host media such as audio and video, it is automatically
available to many types of devices such as computers, mobile phones, and tablets, and it is encoded
in various formats, such as MP4 and Windows Media formats.
MCT USE ONLY. STUDENT USE PROHIBITED
01-12 Exploring ASP.NET Core MVC
Lesson 2
Overview of ASP.NET 4.x
ASP.NET 4.x is a web framework that lets us build rich websites and web applications. ASP.NET 4.x helps
developers to create dynamic websites that use client-side and server-side code. In addition, with ASP.NET
4.x, you are not restricted to a single style of programming; instead, you can choose from several different
programming models: Web Pages, Web Forms, MVC, and Web API. These programming models differ
from each other, and they have their own advantages and disadvantages in different scenarios. ASP.NET
4.x also provides many features that you can use, regardless of the programming model you choose.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the Web Pages programming model.
• Describe the Web Forms programming model.
• Describe the MVC programming model.
• Describe the Web API programming model.
• Determine whether to build Web Pages, Web Forms, MVC, or Web API web applications, based on
customer needs.
• Describe the features that can be used in all ASP.NET 4.x applications, regardless of the chosen
programming model.
Note: If you want to write server-side code in Visual Basic, you use .vbhtml files, instead of
.cshtml files.
You can use Visual Studio 2017 to create Web Pages applications.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-13
The following code example shows HTML and C# code in part of a Web Pages file. The code displays data
from the item object, which is an MVC model class:
• Data display controls, such as grid views, form views, and charts.
• Validation controls, which check data entered by the user.
• Navigation controls, such as menus and tree views.
You can also create your own custom controls to encapsulate custom functionality.
MCT USE ONLY. STUDENT USE PROHIBITED
01-14 Exploring ASP.NET Core MVC
Note: If you write server-side code in Visual Basic, code-behind files have a .vb extension,
instead of a .cs extension.
Overview of MVC
MVC is another programming model available in
ASP.NET 4.x. MVC applications are characterized
by a strong separation of business logic, data
access code, and the user interface into models,
controllers, and views. It is a more advanced
programming model than Web Forms and Web
Pages and is suitable for creating large-scale
applications.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-15
Models
A model contains application business logic, validation, and database access logic. Each website presents
information about different kinds of objects to site visitors. For example, a publisher’s website may present
information about books and authors. A book includes properties such as the title, a summary, and the
number of pages. An author may have properties such as a first name, a last name, and a short biography.
Each book is linked to one or more authors.
When you develop an MVC website for a publisher, you would create a model with a class for books and a
class for authors. These model classes would include the properties described and may include methods
such as “buy this book” or “contact this author”. If books and authors are stored in a database, the model
can include a data access code that can read and change records.
Models are custom .NET classes and store code in .cs files.
Views
Each website must render HTML pages that a browser can display. This rendering is completed by views.
For example, in the publishing site, a view may retrieve data from the Book model and render it on a
webpage so that the user can see the full details. In MVC applications, views create the user interface.
Views are markup pages that store both HTML and C# code in .cshtml files. This means that they are like
Web Pages, but they include only user interface code. Other logic is separated into models and
controllers.
Controllers
Each website must interact with users when they click buttons and links. Controllers respond to user
actions, load data from a model, and pass it to a view so that it will render a webpage. For example, in the
publishing site, when the user double-clicks a book, he or she expects to see the full details of that book.
The Book controller receives the user request, loads the Book model with the right book ID, and passes it
to the Book Details view, which renders a webpage that displays the book. Controllers implement input
logic and tie models to the right views.
Controllers are .NET classes that inherit from the System.Web.Mvc.Controller class and store code in .cs
files.
• You can use the Routing engine to take precise control of URLs.
• Business logic, input logic, and user interface logic are separated into models, controllers, and views.
• Unit testing techniques and Test Driven Development (TDD) are possible.
Using an MVC site has some disadvantages:
• MVC is potentially more complex to understand than Web Pages or Web Forms.
• MVC forces you to separate your concerns (models, views, and controllers). Some programmers may
find this challenging.
• You cannot visually create a user interface by dragging controls onto a page.
• You must have a full understanding of HTML, CSS, and JavaScript to develop views.
MCT USE ONLY. STUDENT USE PROHIBITED
01-16 Exploring ASP.NET Core MVC
Database Front-End
Your organization has its own customer
relationship management system that stores data
in a SQL Server database. Your team of
developers wrote the user interface in Visual
Studio 2017 as a desktop application. The
directors now require that all computers should
be able to access the application even when the
desktop client application is not installed. Because all computers have a browser, you have decided to
write a web application in ASP.NET 4.x to enable this.
E-Commerce Site
You are a consultant for a large software organization. You have been asked to architect an e-commerce
website that will enable customers to browse the entire catalog of software packages, download the
packages, and purchase licenses. The company has a large team of developers who are familiar with .NET
object-oriented programming. The company policy is to use TDD for all software.
• Authentication and Authorization. Many websites require users to log on by entering a username
and password, or by providing extra information. You can use ASP.NET 4.x membership providers to
authenticate and authorize users and restrict access to content. You can also build pages that enable
users to register a new account, reset a password, recover a lost password, or perform other account
management tasks. Membership providers belong to the System.Web.Security namespace.
• Caching. ASP.NET 4.x may take some time to render a complex webpage that may require multiple
database queries or calls to external services. You can use caching to mitigate this delay. ASP.NET 4.x
caches a rendered page in memory so that it can return the same page to subsequent user requests
without having to render it again from the start. In a similar manner, .NET Framework objects can also
be cached. You can access cached pages by using the System.Runtime.Caching namespace and
configure the caches in Web.config.
This two-stage compilation process enables components written in different languages to work together
and enables many errors to be detected at build time. Note, however, that pages may take extra time to
render the first time they are requested after a server restart. To avoid this delay, you can pre-compile the
website.
When you use the default compilation model, delays can arise when the first user requests a page. This is
because ASP.NET 4.x must compile the page before serving it to the browser. To avoid such delays and to
protect source code, use pre-compilation. When you pre-compile a site, all the ASP.NET 4.x files, including
controllers, views, and models, are compiled into a single .dll file.
Additional Reading: For more information about ASP.NET 4.x compilation, see:
https://aka.ms/moc-20486d-m1-pg4
Configuration
When you configure an ASP.NET 4.x site, you can control how errors are handled, how the site connects to
databases, how user input is validated, and many other settings. You can configure ASP.NET 4.x sites by
creating and editing the Web.config files. The Web.config file in the root folder of your site configures the
entire site, but you can override this configuration at lower levels by creating Web.config files in sub-
folders.
MCT USE ONLY. STUDENT USE PROHIBITED
01-18 Exploring ASP.NET Core MVC
Web.config files are XML files with a set of elements and attributes that the ASP.NET runtime accepts:
If you need to access configuration values at runtime in your server-side .NET code, you can use the
System.Web.Configuration namespace.
Authentication
Many websites identify users through authentication. This is usually done by requesting and checking
credentials such as a username and password, although authentication can be done by using more
sophisticated methods, such as using smart cards. Some sites require all users to authenticate before they
can access any page, but it is common to enable anonymous access to some pages and require
authentication only for sensitive or subscription content.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-19
ASP.NET 4.x supports several mechanisms for authentication. For example, if you are using Microsoft Edge
on a Windows computer, ASP.NET 4.x may be able to use Integrated Windows authentication. In this
mechanism, your Windows user account is used to identify you. When you build internet sites, you cannot
be sure that users have Windows, a compatible browser, or an accessible account, so Forms
Authentication is often used. Forms Authentication is supported by many browsers and it can be
configured to check credentials against a database, directory service, or other user account stores.
State Management
Web servers and web browsers communicate through HTTP. This is a stateless protocol in which each
request is separate from requests before and after it. Any values from previous requests are not
automatically remembered.
However, when you build a web application, you must frequently preserve values across multiple page
requests. For example, if a user places a product in a shopping cart, and then clicks Check Out, this is a
separate web request, but the server must preserve the contents of that shopping cart; otherwise, the
shopping cart will be emptied and the customer will buy nothing. ASP.NET 4.x provides several locations
where you can store such values or state information across multiple requests.
Caching
An ASP.NET 4.x page is built dynamically by the ASP.NET 4.x runtime on the web server. For example, in a
Web Pages application, the runtime must run the C# code in the page to render HTML to return it to the
browser. That C# code may perform complex and time-consuming operations. It may run multiple queries
against a database or call services on remote servers. You can mitigate these time delays by using
ASP.NET 4.x caches.
For example, you can use the ASP.NET 4.x page cache to store the rendered version of a commonly
requested page in the memory of the web server. The front page of your product catalog may be
requested hundreds or thousands of times a day by many users. If you cache the page in memory the first
time it is rendered, the web server can serve it to most users very rapidly, without querying the database
server and building the page from scratch.
MCT USE ONLY. STUDENT USE PROHIBITED
01-20 Exploring ASP.NET Core MVC
Note: RESTful API, in general, is a programming model that can be implemented in many
programming languages and frameworks. You can choose to implement RESTful APIs in various
technologies such as Java, PHP, and Node.js, but Web API is the one to choose when working
with ASP.NET 4.x.
• Enables external systems to use your application's business logic and features.
• Can be accessed by various HTTP clients such as Windows, Android, IOs, and more.
• Helps obtain data in different formats such as JSON, XML, and custom formats.
• Supports create, read, update and delete (CRUD) actions since it works with HTTP verbs such as GET,
POST, PUT, and DELETE.
• Is ideal for mobile application integration.
• The data returned from Web API usually is not indexed by search engines such as Google, more work
needs to be done to make it crawlable.
MCT USE ONLY. STUDENT USE PROHIBITED
01-22 Exploring ASP.NET Core MVC
Lesson 3
Introduction to ASP.NET Core MVC
In this lesson, you are going to learn how models, views, and controllers work together to render HTML.
You will see the structure of MVC applications and how it determines the display of information in a
Visual Studio 2017 project. Also, you will examine various features of MVC projects, included in ASP.NET
Core, that help developers build rich and engaging applications.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe models, views, and controllers.
• Choose between ASP.NET 4.x and ASP.NET Core.
• Choose between .NET Core and .NET Framework.
• Describe the key features of an ASP.NET Core MVC web application in Visual Studio 2017.
ASP.NET Core includes three main programming models that can help you to develop your applications smoothly: MVC,
Razor Pages, and Web API.
MVC
When you build a website using ASP.NET Core MVC, you separate your code into three parts: model, view,
and controller. This separation of model, view, and controller code ensures that MVC applications have a
logical structure, even for the most complex sites. It also improves the testability of the application.
Note: MVC will be covered in greater depth in the rest of the modules in this course.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-23
Razor Pages
This programming model was first introduced with ASP.NET Core and it is an alternative to the MVC
programming model. While in MVC we have a controller that receive requests, a model and a view that
generates the response, with Razor Pages it is different. The request goes straight to a page that is
generally located in the Pages folder.
Each Razor Page file has an accompanying .cshtml.cs file that contains all the methods, model handlers,
and logic. It contains a class that inherits from PageModel that will initialize the model inside the OnGet
method. The OnGet method is also the place to get data from the database. Each public property inside
the model can be displayed on the page using Razor syntax.
This code example shows what the code-behind Razor Page looks like:
namespace RazorPagesExample.Pages
{
public class HomePageModel : PageModel
{
public string Description { get; set; }
public string MoreInfo { get; set; }
Razor Page syntax looks a lot like a Razor view, which will be covered in Module 5 "Developing Views".
What makes it different is the @page directive that must be at the first line of the .cshtml file. @page
converts the file into a Razor Page, which means that it handles requests directly without going through a
controller like in the MVC programming model.
Razor syntax can be used inside Razor Pages. It allows you to insert the properties from the model and
other sources into the HTML markup. Razor syntax starts with @ character. When you want to display
properties from the model inside the HTML markup, you will use @model. @model will have a reference
to the properties you initialized inside the OnGet method.
This code example shows how to edit Razor Pages and display values from the model inside the page:
<div class="row">
<div class="col-md-3">
<h1>@ViewData["Title"] </h1>
<ul>
<li>@model.Description</li>
<li>@model.MoreInfo</li>
MCT USE ONLY. STUDENT USE PROHIBITED
01-24 Exploring ASP.NET Core MVC
</ul>
</div>
</div>
Web API
Web API is very useful when creating SPA applications and other applications that use REST API. As with
the MVC programming model, the code is also separated into distinct parts:
• Controller. A Web API controller is a class that handles the client request that was sent to the server.
It accesses the database, retrieves information, updates the database, if needed, and returns the HTTP
response, including status code that indicates whether the action succeeded and data, if needed.
• Model. As with MVC, it is a set of classes that represent the object types that the web application
manages.
• Client. The client sends requests to the server to run specific actions in the Web API controller. On the
server side, there is an interface that consists of functions that can be accessed via HTTP. Those calls
are sent from the client to the server to retrieve specific information and perform read-write
operations.
As the name Web API suggests, the API exists over the web and can be accessed by using HTTP protocol.
Note: Web API will be covered in Module 13, "Implementing Web APIs".
Note: Note that Web Forms and Web Pages don't exist in ASP.NET Core. If you want to use
those programming models, you should use ASP.Net 4.x instead.
.NET Standard
.NET Standard is a formal specification of the
.NET APIs. It is intended to be available on all .NET implementations and its goal is to create a unification
of the .NET ecosystem. It enables you to create code that is portable across all .NET implementations. Each
.NET implementation is targeting a specific version of .NET Standard.
A higher version number means that the previous versions are also supported but it has some extensions.
For example, .NET Framework 4.6.2 implements .NET Standard 1.5. It means that it exposes all APIs
defined in .NET Standard versions 1.0 through 1.5. Similarly, .NET Core 2.0 implements .NET Standard 2.0
and for that reason it exposes all APIs defined in .NET Standard versions 1.0 through 2.0. This also means
that .NET Core 2.0 supports the code written in .NET Framework 4.6.2 because it implements .NET
Standard 1.5.
You want to use NuGet packages or third-party .NET libraries that are not supported
in .NET Core.
While many third-party libraries adopted .NET Core, there might be libraries and NuGet packages that are
not being updated or that do not support the .NET Standard that enables sharing code between all .NET
implementations. In that case, it is better to use .NET Framework.
You want to use .NET technologies that aren't supported in .NET Core
Some of the .NET Framework technologies aren't available in .NET Core. While some of them might be
available in the future, others are not compatible with the design patterns implemented in .NET Core and
will not be added. For example, ASP.NET Web Forms and ASP.NET Web Pages are only available in .NET
Framework. If you want to use any of those, you should choose .NET Framework over .NET Core.
Like any other .NET classes, model classes can include a constructor, which is a procedure that runs when
a new instance of that class is created. You can also include other procedures, if necessary. These
procedures encapsulate the business logic. For example, you can write a Publish procedure that marks the
product as ready-to-sell.
Most websites store information in a database. In an MVC application, the model includes code that reads
and writes database records. ASP.NET Core MVC works with many different data access frameworks. A
commonly used framework is Entity Framework Core, which will be covered in Module 7, “Using Entity
Framework Core in ASP.NET Core”.
<fieldset>
<legend>Comment</legend>
<div class="display-label">
@Html.DisplayNameFor(model => model.Subject)
</div>
<div class="display-field">
@Html.DisplayFor(model => model.Subject)
</div>
<div class="display-label">
@Html.DisplayNameFor(model => model.Body)
</div>
<div class="display-field">
@Html.DisplayFor(model => model.Body)
</div>
</fieldset>
Often, the view displays properties of a model class. In the preceding code example, the Subject property
and Body property are incorporated into the page.
2. The MVC routing engine examines the request and determines that it should forward the request to
the Product controller and the Display action.
3. The Display action in the Product controller creates a new instance of the Product model class.
4. The Product model class queries the database for information about the product with ID “1”.
5. The Display action also creates a new instance of the Product Display view and passes the Product
model to it.
6. The Razor view engine runs the server-side code in the Product Display view to render the HTML. In
this case, the server-side code inserts properties such as Title, Description, Catalog Number, and
Price into the HTML.
Demonstration Steps
You will find the steps in the “Demonstration: How to Explore an ASP.NET Core MVC Application“ section
on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD01_DEMO.md.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Core Web Applications 01-29
Objectives
After completing this lab, you will be able to:
• Describe and compare the three programming models — Razor Pages, Web API, and MVC.
• Describe the structure of each web application developed in the three programming models — Razor
Pages, Web API, and MVC.
• Select an appropriate programming model for a given set of web application requirements.
Lab Setup
Estimated Time: 90 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD01_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD01_LAK.md.
Review Questions
Question: What is the main difference between Razor Pages and MVC?
Question: Which of the application programming models will you recommend for the
organization`s customers: Razor Pages, Web API, or MVC?
Best Practices
• Use Web API when you want to create HTTP services and to allow developers access specific sets of
data or functionality of your application. If you would like to add a UI, you should combine Web API
with another programming model that has UI functionality.
• Use MVC when you want the most precise control over HTML and URLs, when you want to cleanly
separate business logic, user interface code, and input logic, or when you want to perform TDD.
Additional Reading: For more information about ASP.NET, including forums, blogs, and
third-party tools, visit the official ASP.NET site: https://aka.ms/moc-20486d-m1-pg5
MCT USE ONLY. STUDENT USE PROHIBITED
01-32 Exploring ASP.NET Core MVC
MCT USE ONLY. STUDENT USE PROHIBITED
02-1
Module 2
Designing ASP.NET Core MVC Web Applications
Contents:
Module Overview 02-1
Lesson 1: Planning in the Project Design Phase 02-2
Lesson 2: Designing Models, Controllers and Views 02-15
Lab: Designing ASP.NET Core MVC Web Applications 02-22
Module Review and Takeaways 02-24
Module Overview
Microsoft ASP.NET Core MVC is a programming model that you can use to create powerful and complex
web applications. However, all complex development projects, and large projects in particular, can be
challenging and intricate to fully understand. Without a complete understanding of the purposes of a
project, you cannot develop an effective solution to the customer’s problem. You need to know how to
identify a set of business needs and plan the Model-View-Controller (MVC) web application to meet those
needs. The project plan that you create assures stakeholders that you understand their requirements and
communicates the functionality of the web application, its user interface, structure, and data storage to
the developers. By writing a detailed and accurate project plan, you can ensure that the powerful features
of MVC are used effectively to solve the customer’s business problems.
Objectives
After completing this module, you will be able to:
• Plan the overall architecture of an ASP.NET Core MVC web application and consider aspects such as
state management.
• Plan the models, controllers, and views that are required to implement a given set of functional
requirements.
MCT USE ONLY. STUDENT USE PROHIBITED
02-2 Designing ASP.NET Core MVC Web Applications
Lesson 1
Planning in the Project Design Phase
Before you and your team of developers plan a MVC web application or write any code, you must have a
thorough understanding of two things: the business problem you are trying to solve and the ASP.NET
components that you can use to build the solution. Before designing a web application architecture and
its database, you should know how to identify the requirements of the potential users of the web
application.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the various project development models.
• Describe how to gather information about project requirements when building ASP.NET Core MVC
web applications.
• Determine the functional requirements and business problems when building web applications.
• Explain how to plan the database design when building a web application.
• Describe possible distributed application architectures.
• Describe the options for planning state management in a web application.
• Describe the options for planning globalization and localization of a web application.
• Describe the options for planning accessible web applications.
• Determine the critical aspects of web application design.
Waterfall Model
The waterfall model is a traditional methodology that defines the following phases of a project:
• Feasibility analysis. In this phase, planners and developers study and determine the approaches and
technologies that can be used to build the software application.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 02-3
• Requirement analysis. In this phase, planners and analysts interview the users, managers,
administrators, and other stakeholders of the software application to determine their needs.
• Application design. In this phase, planners, analysts, and developers record a proposed solution.
• Coding and unit testing. In this phase, developers create the code and test the components that make
up the system individually.
• Integration and system testing. In this phase, developers integrate the components that they have
built and test the system as a whole.
• Deployment and maintenance. In this phase, developers and administrators deploy the solution so
that users can start using the software application.
The waterfall model classifies the development project into distinct phases with the deliverables for each
phase clearly defined. The model also emphasizes the importance of testing. However, the customer does
not receive any functional software for review until late in the project. This makes it difficult to deal with
changes to the design in response to beta feedback or to manage altered circumstances.
Prototyping Model
The prototyping model is suitable for a project with few or meagerly defined business requirements. This
situation occurs when the customers or stakeholders have only a vague understanding of their needs and
how to solve them. In this approach, developers create a simplified version of the software application,
and then seek feedback from stakeholders. This feedback on the prototype is used to define the detailed
requirements, which developers use in the next iteration to build a solution that matches the needs of the
stakeholders to better help them perform their jobs.
After two or more iterations, when both stakeholders and developers reach a consensus on the
requirements, a complete solution is built and tested. The prototyping model, however, can lead to a
poorly-designed application because at no stage in the project is there a clear focus on the overall
architecture.
• Incremental development. Software is developed in rapid cycles that build on earlier cycles. Each
iteration is thoroughly tested.
• Emphasis on people and interactions. Developers write code based on what people do in their role,
rather than what the development tools are good at.
MCT USE ONLY. STUDENT USE PROHIBITED
02-4 Designing ASP.NET Core MVC Web Applications
• Emphasis on working software. Instead of writing detailed design documents for stakeholders,
developers write solutions that stakeholders can evaluate at each iteration to validate if it solves a
requirement.
• Close collaboration with customers. Developers discuss with customers and stakeholders on a day-to-
day basis to check requirements.
Extreme Programming
Extreme programming evolved from agile software development. In extreme programming, the
preliminary design phase is reduced to a minimum and developers focus on solving a few critical tasks. As
soon as these critical tasks are finalized, developers test the simplified solution and obtain feedback from
stakeholders. This feedback helps developers identify the detailed requirements, which evolve over the life
cycle of the project.
Extreme programming defines a user story for every user role. A user story describes all the interactions
that a user with a specific role might perform within the completed application. The collection of all the
user stories for all user roles describes the entire application.
In extreme programming, developers often work in pairs. One developer writes the code and the other
developer reviews the code to ensure that it uses simple solutions and adheres to best practices. Test
Driven Development (TDD) is a core practice in extreme programming.
Additional Reading: For more information about the extreme programming model, refer
to “Extreme Programming: A gentle introduction” at: https://aka.ms/moc-20486d-m2-pg1
• Structure diagrams. These diagrams depict the elements of an application that are independent of
time. This means they do not change through the lifetime of the application.
Gathering Requirements
When a project is commissioned, you need to
visualize the solution. The vision can often be
vague and require in-depth investigation before
you can add details and ensure that all the
stakeholders’ requirements are covered by the
web application. These requirements can be of
two types:
• Functional requirements. They describe how
the application behaves and responds to
users. Functional requirements are often
called behavioral requirements. They include:
o User interface requirements. They
describe how the user interacts with an application.
o Usage requirements. They describe what a user can do with the application.
o Business requirements. They describe how the application will fulfill business functions.
• Technical requirements. They describe technical features of the application and relate to availability,
security, or performance. These requirements are sometimes called non-functional or non-behavioral
requirements.
You usually gather requirements by interviewing stakeholders such as users, administrators, other
developers, board members, budget holders, and team managers. Each of these groups might have
different sets of priorities that the application needs to fulfill.
A use case is similar to a usage scenario, but is more generalized. Use cases do not include user names or
input values. They describe multiple paths of an interaction, which depends on what the user provides as
input or other values. The following is a simple example:
1. The user clicks the Add Photo link on the main site menu.
2. If the user is anonymous, the sign-in page appears and the user provides credentials.
3. If the credentials are correct, the CreatePhoto view appears.
4. The user types a title.
5. The user specifies the photo file to upload.
6. The user optionally types a description for the photo.
Note: Similar to verbal descriptions, you can use UML use case diagrams to record the use
cases for your web application.
By analyzing usage scenarios and use cases, you can identify functional requirements of all types. For
example, from the preceding use case, you can identify the following user interface requirements: The
webpage that enables users to create a new photo must include Title and Description text boxes, a file
input control for the photo file, and an Upload button to save the photo.
Logical Modeling
You can begin your data design at a high level by
creating UML domain model diagrams and Logical Data Model (LDM) diagrams.
A domain model diagram, also known as a conceptual data model, shows the high-level conceptual
objects that your web application manages. For example, in an e-commerce web application, the domain
model includes the concepts of customers, shopping carts, and products. The domain model does not
include details of the properties of each concept, but shows the relationships between the concepts. Use
the domain model to record your initial conversations with stakeholders.
In essence, an LDM is a domain model diagram with extra details added. You can use LDM diagrams to fill
in more details, such as properties and data types, for the concepts that you defined in the domain model.
Note that the objects in the LDM do not correspond to tables in the database. For example, the shopping
cart object might display data from both the customer database and product database tables.
• Stored procedures. These are common sequences of database operations that you define in the
database. Some operations are complex and might involve a complex transformation of the data. You
can define a stored procedure to implement such a complex routine.
• Security. You need to consider how the web application will authenticate with the database server
and how you will authorize access to each database table.
In UML, a physical data model is a diagram that depicts tables, columns, data types and relationships
between tables.
MCT USE ONLY. STUDENT USE PROHIBITED
02-8 Designing ASP.NET Core MVC Web Applications
• Business logic layer. Components in this layer implement high-level business objects such as products,
or customers. If you are building an MVC web application, models make up your business logic layer.
• Data access layer. Components in this layer implement database access operations and abstract
database objects, such as tables, from business objects. For example, a product business object might
include data from both the Products and StockLevels database tables. If you are building an MVC web
application, models often make up both business logic and data access layers. However, with careful
design and coding practices, it is possible to refactor code to separate these layers.
• Database layer. The database itself.
If you implement such a layered architecture in your web application, you can host each layer on separate
servers. Often, for example, the presentation layer is hosted on one or more IIS servers, the business logic
and data access layers are hosted on dedicated middle-tier servers, and the database is hosted on a
dedicated SQL Server. This approach has the following advantages:
• You can specify server hardware that closely matches each role. For example, a server that hosts
business logic components requires good memory and processing resources.
• You can dedicate multiple servers to each role to ensure that failure of a single server does not cause
an interruption in service.
• Only the web servers must be on the perimeter network. Both middle-tier servers and database
servers can be protected by two firewalls without direct access from the Internet.
• Alternatively, you can host middle-tier layers and databases on a cloud service, such as Microsoft
Azure.
in query strings because it is visible to the user, anyone observing the session, or anyone monitoring
web traffic.
• Session state. This is a state storage location that you can use to store information for the lifetime of a
single browser session and values stored here are specific to a single user session; they cannot be
accessed by other users. Session state is available for both authenticated users and anonymous users.
By default, session state uses cookies to identify users, but you can configure ASP.NET to store session
state without using cookies.
• Database tables. If your site uses an underlying database, like most sites do, you can store state
information in its tables. This is a good place to store large volumes of state data that cannot be
placed in server memory or on the client computer. For example, if you want to store a large volume
of session-specific state information, you can store a simple ID value in a session state and use it to
query and update a record in the database.
Additional Reading: The World Wide Web Consortium (W3C) has a project called the Web
Accessibility Initiative (WAI) that promotes accessible web content. This project has published the
Web Content Accessibility Guidelines. These guidelines are accepted by the web publishing
industry as definitive. To read the full guidelines, go to: https://aka.ms/moc-20486d-m2-pg5
Note: In ASP.NET Core MVC, the developer has complete control over the HTML that the
web server sends to the web browser. However, you must understand accessibility principles to
write accessible MVC web applications. Ensure that your entire development team is familiar with
the requirements of accessibility and the related best practices.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 02-15
Lesson 2
Designing Models, Controllers and Views
Models, controllers, and views are the fundamental building blocks of an ASP.NET Core MVC web
application. In a complex site, there might be hundreds of models, views, and controllers. You need to
manage these objects and plan your application well, so that it is easy to manage the organization and
internal structure during development. A thorough plan ensures that you detect any incorrect code and
debug problems rapidly.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe how to design models.
• Describe how to design controllers.
Designing Models
A fundamental activity in the MVC design
process is designing a model. Each model class
within the model represents a kind of object that
your application manages.
2. If the user is anonymous, the sign-in page appears, and the user provides credentials.
3. If the credentials are correct, the CreatePhoto view appears.
4. The user types a title.
5. The user specifies the photo file to upload.
6. The user optionally types a description for the photo.
7. The user clicks the Upload button.
8. The web application stores the new photo and displays the photo gallery to the user.
This example includes the following objects, each of which requires a model class:
• User. The User model class must include properties to store credentials, such as the user name and
the password.
MCT USE ONLY. STUDENT USE PROHIBITED
02-16 Designing ASP.NET Core MVC Web Applications
• Photo. The Photo model class must include the Title and Description properties.
Other use cases similarly help you add new model classes or new properties to the User and Photo model
classes.
Using Diagrams
You can use diagrams to analyze the information your website manages and suggest a physical data
model or database design for the site. You can use these diagrams to plan model classes. Each object in
your diagram should be implemented as an MVC model class. The diagram can include not only the
names of those classes, but also data types.
Entity Framework
Entity Framework is an Object-Relational Mapping (ORM) framework for .NET Framework-based
applications. An ORM framework links database tables and views to classes that a developer can program
against, by creating instances or calling methods.
When you use Entity Framework in your MVC web application, it links tables and views to the model
classes that you have planned. You do not need to write SQL code to query or update database tables
because Entity Framework does this for you. Entity Framework is well integrated with the Language-
Integrated Query (LINQ) language.
Note: Entity Framework will be covered in Module 7, “Using Entity Framework Core in
ASP.NET Core”.
Designing Controllers
In an ASP.NET Core MVC web application,
controllers are .NET Framework-based classes
that inherit from the
Microsoft.AspNetCore.Mvc.Controller base
class. They implement input logic—this means
that they receive input from the user in the form
of HTTP requests and select both the correct
model and the correct view to use, to formulate a
response.
Photo AddPhoto (GET) The AddPhoto action for GET requests creates a new
instance of the Photo model class, sets default values
such as the created date, and passes it to the correct
view.
AddPhoto (POST) The AddPhoto action for POST requests calls the Photo
model class methods to save the photo values to the
database and redirects the browser to the
DisplayGallery action.
User Logon (GET) The Logon action for GET requests displays a view into
which an anonymous user can enter credentials.
Logon (POST) The Logon action for POST requests checks user
credentials against the membership database. If the
credentials are correct, the Logon action authenticates
and redirects the user to the originally requested page.
Designing Views
The user interface is a vital component of any
system because it is the part with which the
users, budget holders, and other stakeholders see
and interact. Users are most interested in getting
this part of the application right and frequently
have the most to say about its design. As a
developer, you have a chance to impress your
users by designing and implementing a
sophisticated user interface, which might result in
more business.
In an ASP.NET Core MVC web application, the
user interface is created by building views. There
is a many-to-one relationship between MVC controllers and views. For example, a controller might use
one view to display a single photo, another view to display several photos, and a third view to enable
users to upload new photos. Each view corresponds to a webpage that the application can display to the
user, although it can display different data. For example, the PhotoDetails view can display different
photos, depending on the ID parameter it receives.
As you plan views, you should also consider parts of the user interface that appear on all pages. For
example, the company logo, main site menu, links to legal information, and sign-in controls might need
to appear on every page. You can place these user interface components in a layout to create a consistent
look and feel across pages.
Note: Layouts will be covered in Module 8, “Using Layouts, CSS and JavaScript in ASP.NET
Core MVC”.
Some user interface components do not appear on all pages, but are re-used on several pages. For
example, comments might be displayed in a single photo display, on the gallery, or on other pages. By
creating a partial view or a view component, you can create a re-usable user interface element that can
appear in many locations in this manner, without duplicating code.
Note: Partial views and view components will be covered in Module 5, “Developing Views”.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 02-19
Information Architecture
When the information your web application
manages is complex and multi-faceted, it is easy
to present objects in a confusing way. Unless you
think carefully about the way users expect
information to be structured and how they
expect to navigate to useful content, you might
unintentionally create an unusable web
application. During development, when you work
with a limited amount of data, this confusion
might not become apparent. Then, when real-
world data is added to your database at the time
of deploying to production, it becomes clear that
the web application is confusing. You can avoid this eventuality by planning the information architecture.
Information architecture is a logical structure for the objects your web application manages. You should
design such architecture in a way that users can find content quickly without understanding any technical
aspects of your web application. For example, users should be able to locate a technical answer about
their product without understanding the database structure or the classes that constitute the model.
• Category. Categories organize the furnaces by type. For example, you can have categories such as oil-
fired, gas-fired, and solid fuel.
• FAQ. Each FAQ relates to a single furnace. Each furnace can have many questions. The class includes
both the Question and the Answer properties.
• Installation Manual. Each furnace has a single installation manual in the form of a PDF document.
• User Manual. Each furnace has a single user manual in the form of a PDF document.
The types of navigation controls you build in your web application depend on how you expect users to
find information.
• http://site/FAQQuestion/Details/234: This URL links to the FAQQuestion controller and the Details
action, and it displays the FAQ with the ID 234.
• http://site/InstallationManual/Details/3654: This URL links to the InstallationManual controller and
the Details action, and it displays the manual with the ID 3654.
As you can see, the web application user is required to understand how controllers, actions, and action
parameters work, to formulate URLs themselves. Instead, users can use URLs that are easier to understand,
such as the following, because they reflect the information hierarchy:
• http://site/OilFired/HotBurner2000: This URL links to a furnace by specifying the fuel category and the
product name. Customers and engineers can understand these values.
Objectives
After completing this lab, you will be able to:
• Design an ASP.NET Core MVC web application that meets a set of functional requirements.
• Record the design in an accurate, precise, and informative manner.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD02_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD02_LAK.md.
Review Question
Question: You want to support both English and Spanish in your web application. You have both
Spanish-speaking and English-speaking developers and want to ensure that views remain
readable as easily as possible. Should you use multiple view files or multiple resource files to
globalize your site?
Tools
You can use Microsoft Office Visio and Visual Studio 2017 to create design diagrams.
Best Practice
In agile development and extreme programming projects, developers discuss with users and stakeholders
throughout development to ensure that their code will meet changing requirements. Even if you are not
formally using these methodologies, it is good practice to regularly communicate with users.
When you create a very detailed project Use agile development and extreme programming
plan, much of your work is wasted when methodologies. Such methodologies, which are
requirements change late in the project. based on the real-world assumption that
requirements will change frequently, are proven to
prevent the time wastage that often occurs when
complete designs are altered during the
development phase.
MCT USE ONLY. STUDENT USE PROHIBITED
03-1
Module 3
Configure Middleware and Services in ASP.NET Core
Contents:
Module Overview 03-1
Lesson 1: Configuring Middleware 03-2
Lesson 2: Configuring Services 03-14
Lab: Configuring Middleware and Services in ASP.NET Core 03-28
Module Review and Takeaways 03-30
Module Overview
ASP.NET Core is a framework that allows us to build many different kinds of applications. In this module,
you will learn how to leverage the ASP.NET Core framework to handle requests and responses via existing,
and custom middleware, and how to configure services for use in middleware and throughout other parts
of the application, such as controllers.
A middleware is a segment of code that can be used as part of the request and response pipeline that
allows us to handle them according to any relevant parameter. This potentially allows multiple separate
requests to be handled in a completely different fashion and receive separate responses.
Services are classes that expose functionality which you can later use throughout different parts of the
application, without having to keep track of scope manually in each individual location and instantiate
any dependencies. This is done by using Dependency Injection.
Dependency Injection is a technique used by ASP.NET Core that allows us to add dependencies into the
code without having to worry about instantiating objects, keeping them in memory, or passing along
required dependencies. This allows the application to become more flexible and to reduce potential
points of failure whenever you change a service.
Objectives
After completing this module, you will be able to:
• Use existing middleware to set up an ASP.NET Core application.
• Create your own middleware and use it to define custom behavior.
• Understand the basic principles behind Dependency Injection, and how it is used in ASP.NET Core.
• Know how to create a custom service, configure its scope, and inject it to both middleware and
ASP.NET Core MVC controllers.
MCT USE ONLY. STUDENT USE PROHIBITED
03-2 Configure Middleware and Services in ASP.NET Core
Lesson 1
Configuring Middleware
Middleware is code, which can be attached to the request and response pipeline and is used to perform
manipulations on all the requests and responses occurring throughout the server. Without any
middleware, you are unable to actually handle any client requests, and you will simply get an error page.
By using middleware, you can inspect the request, and make important decisions on how to handle the
current step. Middleware can be used to perform any operation that you can possibly see as relevant to
the current request, including writing to the response. However, not all middleware will directly influence
the response. At any time, that should you decide it is required, you can short-circuit the middleware
pipeline and return a finalized response.
In this lesson, you will learn about the importance of the Startup class in configuring the middleware that
you wish to use, cover the basics behind the concept of the middleware, learn how to create custom
middleware, and how to implement them. Finally, you will be introduced to the UseStaticFiles
middleware, as an example for built-in middleware, which allows us to serve static files such as HTML, CSS
and JavaScript files to the end users.
It is important to understand that the flow of the middleware and their order determines how the
application is run, and that understanding how middleware function can help us build more consistent
behavior for the application while avoiding undesirable edge cases.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the basic purpose of the Startup class, in relation to middleware.
• Describe the usage of middleware, and the correct order to manage their pipeline.
• Use existing middleware to extend an ASP.NET Core web application’s capabilities.
• Configure your own custom middleware, thereby extending the capabilities of ASP.NET Core to suit
your requirements.
• Serve static files to users of the application.
Application Startup
At the basis of every ASP.NET Core application is
the Startup class. This class is created by default
in every new project and is used to help us
configure the application. It is used for
configuring middleware, which is performed in
the Configure method, and for configuring
services, which is performed in the
ConfigureServices method.
The Startup class is called and run from within
the program.cs file.
As part of startup, the ConfigureServices
method will be called first if it is present, before
calling the Configure method, which may have dependencies on the services that were declared in
ConfigureServices.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 03-3
Best Practice: By default, the startup class will use the name Startup. While this can be
changed, and you can use any name you wish, it is a best practice to use the name Startup. This
allows people unfamiliar with the application to find it easily and to add required middleware
and services.
In this example, the string Hello World! is always displayed in the browser, no matter which relative URL
the user requests.
The following code shows a custom service being added to the application service collection:
A ConfigureServices example
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IService, Service>();
}
Note: Note that some services are predefined in the framework and will not need to be
declared. For example, ILogger, and IHostingEnvironment, both of which will be covered in
Module 10, “Testing and Troubleshooting”.
MCT USE ONLY. STUDENT USE PROHIBITED
03-4 Configure Middleware and Services in ASP.NET Core
A Configure example
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IService, Service>();
}
Middleware Fundamentals
Middleware allows us to add code to manipulate
the request and response pipeline in ASP.NET
Core applications. Every middleware in the
pipeline can interact with the provided request,
and write to the response, creating the desired
result.
A middleware example
public void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
In this example, the string Hello World! will be displayed in the browser on every request, no matter
which relative URL the user requests.
Middleware is intended to work in a pipeline with each item inspecting the request, performing an action,
or a manipulation on the response object and on completing its run, either calling the next middleware in
the pipeline or if appropriate for the scenario, short-circuiting the pipeline and sending the finalized
response. Short-circuiting should be done whenever the handler deems the request ended.
Note: Scenarios where you may want to short circuit the request:
The following table shows the requests and responses for the following relative URL paths using the
previous code.
Request Response
Middleware always run in the order they are defined. Additionally, all middleware operate in sequence,
and are of several potential types:
• Use. Middleware added with Use, support a parameter named next, which is a reference to the next
middleware in the pipeline. You can short-circuit the pipeline by not invoking next, although all Use
middleware should preferably support at least one flow in which they call next.Invoke() to proceed
to the next middleware in the pipeline.
• Run. Unlike Use, the Run middleware will always be the final middleware in the pipeline. It does not
support the next parameter, and any middleware appearing after it will never be called.
• Map. A third variation is the Map middleware. It does not continue with the current pipeline, instead,
if the relative path is matched to the Map middleware, it will continue down a new pipeline, which is
provided as a delegate to the Map middleware. Note that since Map middleware creates its own
pipeline it is not affected by the Run middleware which occurs after it and can support a call to Run
at the end of its own pipeline.
The following code demonstrates a Configure method with only a Run middleware:
In this example, the Inside run middleware string will be displayed in the browser on every request, no
matter which relative URL the user requests.
In this example the Inside use middleware => Inside run middleware string will be displayed in the
browser on every request, no matter which relative URL the user requests.
The following example demonstrates a Use middleware that can short-circuit the middleware pipeline:
If a user requests a URL with a query string parameter, which has the name shortcircuit (value isn’t
required) the Inside use middleware string will be displayed in the browser. On any other request, the
Inside run middleware string will be displayed in the browser.
The following code demonstrates a Map middleware:
If a user requests a URL with a relative path starting with /Map, a new pipeline will be entered in which
the Run middleware inside of map middleware string will be displayed in the browser. If the relative
path does not begin with /Map, the Inside run middleware string will be displayed in the browser.
Best Practice: It is a good idea to always end the Configure method with a Run
middleware. This allows us to always ensure that the client receives a response from the server,
even if it is a generic response. Otherwise, the user may encounter a generic error page.
MCT USE ONLY. STUDENT USE PROHIBITED
03-8 Configure Middleware and Services in ASP.NET Core
Order of Middleware
It is important to remember that middleware runs in the order in which they were added to the pipeline,
therefore it is important to keep the following in mind:
• A Run middleware should always be present at the very end of the pipeline. All middleware defined
after the Run middleware will never be run.
• Every application should only have a single Run middleware inside a specific pipeline. Remember that
by using Map you create a new pipeline, which means the Run middleware inside of a Map pipeline
is separate from the main pipeline.
• Whenever multiple middleware share the same condition, it is important to order them to handle the
pipeline in the desired way and be mindful of the possibility of the pipeline being short-circuited.
The following code demonstrates three middleware operating in a linear order:
In this example the First middleware => Second middleware => Final middleware string will be
displayed in the browser on every request, no matter which relative URL the user requests.
The following code demonstrates the detrimental effect of the Run middleware running before other
middleware:
In this example, only the Final middleware string will be displayed in the browser on every request, no
matter which relative URL the user requests.
The following code shows an example of the Use middleware short-circuiting the pipeline:
If a user requests a URL with a query string parameter with the name shortcircuit, the First middleware
=> Second middleware => string will be displayed in the browser. If the parameter is not present in the
request, the First middleware => Second middleware => Final middleware string will be displayed in
the browser.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Create Custom Middleware“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD03_DEMO.md#demonst
ration-how-to-create-custom-middleware.
MCT USE ONLY. STUDENT USE PROHIBITED
03-10 Configure Middleware and Services in ASP.NET Core
Best Practice: It is important to note, that what you learned about the order of middleware
in Topic 2, “Middleware Fundamentals”, is still relevant when working with UseStaticFiles. It is
generally considered a best practice to call UseStaticFiles early in the middleware pipeline.
However, you must take care that the paths served in UseStaticFiles do not overlap with paths
you expect to handle in other middleware since this can result in unexpected behavior.
If the user requests a URL with a relative path that matches the directory structure under wwwroot, the
specified file will be sent back as the response. Otherwise, the Not a static file. string will be displayed in
the browser.
Best Practice: It is a best practice to keep wwwroot as the folder from which static files
will be will hosted. However, should the need arise to host static files for a third-party
component, the option exists.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 03-11
The following code is an example of hosting static files from wwwroot and from a folder named
StaticFolder:
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new
PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "StaticFolder"))
});
If a user requests a URL with a relative path that matches with a path under wwwroot or StaticFolder,
the specified file will be sent back as the response. Otherwise, the Not a static file. string will be
displayed in the browser.
In this example, if an attempt is made to access any files under either wwwroot or StaticFolder, they will
both be accessible. An important note is that if the same file path exists in both, the one in wwwroot
takes precedence as the UseStaticFiles middleware that configures wwwroot is called first.
Note: Path.Combine is a method that is designed to combine multiple file system paths
into a single valid path string.
Directory.GetCurrentDirectory is used to retrieve the current working directory of the
application at run time.
The following code demonstrates serving static files on separate relative paths:
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new
PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "StaticFolder")),
RequestPath = "/StaticFiles/MyFolder"
});
MCT USE ONLY. STUDENT USE PROHIBITED
03-12 Configure Middleware and Services in ASP.NET Core
The following table shows the requests and responses for the following relative URL paths using the
previous code:
Note: Note that this will allow us to potentially have the same paths under different
directories which are served without conflicts.
• Depending on the hosting operating system, these files may or may not be case sensitive.
Therefore, it is important that any file you wish to protect should not be inside folders you are serving. In
particular, any cs code, or cshtml pages (cshtml pages will be covered in Module 5, “Developing Views”)
should never be placed under a folder designated for serving to the client.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 03-13
Demonstration Steps
You will find the steps in the section “Demonstration: How to Work with Static Files“ on the following
page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD03_DEMO.md#demonst
ration-how-to-work-with-static-files.
MCT USE ONLY. STUDENT USE PROHIBITED
03-14 Configure Middleware and Services in ASP.NET Core
Lesson 2
Configuring Services
Services are classes that can be reused easily in multiple locations without having to worry about
instantiating their various dependencies and sub-dependencies. By doing this, it is possible to greatly
decrease the overhead involved in developing code by reducing management and potential points of
failure. This is facilitated via a technique known as Dependency Injection. As a result, large segments of
code can easily be reused in different controllers, and even inside the Configure method of the Startup
class.
In this lesson, you will learn about how to create and use services by leveraging the strengths of
Dependency Injection to create versatile code, which does not require frequent upkeep. You will begin by
delving into Dependency Injection, what it means and how it works, and then go into a demonstration on
how to put it into effect, allowing the addition of custom services into the Configure method, and how to
use them in other services that will be created. You will then take a look at ASP.NET Core MVC controllers
(Controllers will be covered in Module 4, “Developing Controllers”), and learn how by using middleware
and services it is possible to run an ASP.NET Core MVC application, and how you can reuse services inside
controllers. Finally, you will then also learn how to best manage the service lifetime and maintain a logical
lifetime for the various services used by the application.
It is important to understand the importance of how Dependency Injection can help to create a cleaner,
more easily maintainable code, while providing us with relevant tools for managing them, and making the
overall code easier to test and to learn how you can use the services at any point in the application.
Lesson Objectives
After completing this lesson, you will be able to:
• Explain the basics behind Dependency Injection and explain its purpose.
Dependency chain
public class MyClass
{
private MyDependency _myDependency;
}
}
Instantiating MyClass
public class SomeClass
{
private MyClass _myClass;
public SomeClass()
{
MySubDependency subDependency = new MySubDependency();
MyDependency dependency = new MyDependency(subDependency);
_myClass = new MyClass(dependency);
}
}
As you can see, normally when you try to instantiate this chain of dependencies, you need to also take
care of its sub-dependencies and if the class you have defined is required in other places, you will need to
instantiate it in there as well.
By implementing Dependency Injection, you are able to simplify the process and adopt a more loosely
coupled approach. By explicitly defining interfaces you wish to work within the constructor, you are able
to specify only the requirements for the current class and you don’t need to worry about any possible
sub-dependencies or making any changes should any sub dependency code change.
MCT USE ONLY. STUDENT USE PROHIBITED
03-16 Configure Middleware and Services in ASP.NET Core
The following code is a more loosely coupled variant of the dependency chain seen previously:
}
}
This code demonstrates how the previous code segment could function if Dependency Injection was used.
As you can see rather than needing to instantiate the various sub-dependencies, both SomeClass and
MyClass only require their own direct dependencies, creating a far more loosely coupled relationship.
Additionally, if you wanted to give any of these classes additional dependencies, it will not affect anything
else.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 03-17
By using interfaces, you are able to declare the behavior that you desire to implement, rather than a
specific implementation. By using a Dependency Injection container, you can allow the application to
support Dependency Injection, freeing us from having to manage multiple components whenever a
change occurs and allowing us to work on a more abstract level.
The Dependency Injection container is a class that lies at the heart of the mechanism. This class is a factory
class with the sole purpose of instantiating classes as they are needed and providing them to other classes
as needed. This encompasses not only the Configure method but also extends to controllers (Controllers
will be covered in Module 4, “Developing Controllers”), views (Views will be covered in Module 5,
“Developing Views”), and even other services. Whenever a class in the application is instantiated by using
a Dependency Injection container, it will request any of its other dependencies, allowing us to work
exclusively with the class itself without having to worry about having to manage any of its dependencies.
By default, ASP.NET Core uses a simple built-in Dependency Injector container, which gives us the support
of constructor injection. You can use the IServiceProvider interface to interact with the container, and
the most prominent interaction is through the ConfigureServices method of the Startup class, which
allows you to register additional services that you wish to run throughout the application.
Note: One of the biggest advantages granted us by Dependency Injection is that due to
the code being implemented via services, you are able to very easily test each individual
component. Services, controllers, and more are all easily tested when you can easily provide mock
data via the interface, rather than having to support tests covering multiple components. This
ensures that testing is more thorough and more extensive while being easier to maintain.
A basic service
public interface IMyService
{
string DoSomething();
}
{
public string DoSomething()
{
return "something";
}
}
In this example, a class called MyService is created, which implements the IMyService interface. This class
implements a DoSomething method, which when called returns the something string. This example will
be used in the demos throughout this topic.
After creating the service, you will need to register it in the ConfigureServices method of the Startup
class. This can be done by using the services parameter, which implements the IServiceCollection
interface. This method exposes to us several methods that can be used to add the service to the
Dependency Injection container. The main methods for adding custom services are AddSingleton,
AddTransient, and AddScoped. You will learn about them and their various differences in the Topic 5,
“Service Lifetime”. But for now, you will use AddSingleton.
To register the service, you will call the AddSingleton method. You will provide the type parameters for
the interface that you want to declare and the type of the class that you want to instantiate.
The following code is an example of registering a custom service:
In this example, you can see that in the ConfigureServices method you are registering a service with the
MyService class, which implements the IMyService interface. Later, you can see that in the Configure
method, you have injected the service and used it inside of the middleware. In this example, the
something string will be displayed in the browser on every request, no matter which relative URL the user
requests.
Best Practice: It is a best practice to name interfaces with the same name as the class with
an I at the beginning. However, it would be possible to register a service with completely
different class name and interface name, as long as the class implements the interface. Doing so
will create confusing code that is difficult to maintain.
This example demonstrates the usage of AddMvc, as well as, adding the service that was configured
earlier.
Note: If you look into IServiceCollection, you will see that it exposes both the AddMvc
and AddMvcCore methods. It is important to note that AddMvcCore contains only the very
basic components required to run an ASP.NET Core MVC application and lacks several additional
services that are used for various additional functionalities, such as Cross Site Scripting handling
and the Razor View engine. For this course, you will mainly use AddMvc.
In this example, you can see that SecondService stores a reference to FirstService, and whenever
GoSecond is called, the Going First – Going Second string will be returned.
The following code demonstrates registration and usage of services with injection:
In this example, you can see registration for both services from the previous example, alongside injecting
SecondService into the Configure method and using it inside the middleware. For all requests, Going
First – Going Second will be displayed in the browser.
Best Practice: Services inside ConfigureServices can be registered in any order, unlike
middleware, and are resolved by the Dependency Injection container. However, it is a good idea
to keep them in order of dependencies, as it can provide a quick visual reference to the order in
which services will be raised.
However, it is important to note that Dependency Injection cannot help us resolve circular
references, and they will cause the application to crash.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use Dependency Injection“ on the following
page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD03_DEMO.md#demonst
ration-how-to-use-dependency-injection.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 03-21
Note: Adding MVC to the application can be done inside the Configure method by calling
either the UseMvcWithDefaultRoute or UseMvc middleware. The differences between them
will be further explained in Module 4, “Developing Controllers”. For now, you will use
UseMvcWithDefaultRoute, as it does not require additional configuration.
Best Practice: Ideally, the call to UseMvcWithDefaultRoute or UseMvc will appear as one
of the later middleware in the pipeline. This is due to wanting custom behavior and static files to
be loaded first and not to be accidentally directed to ASP.NET Core MVC controllers. However,
should the need arise, the order may be changed.
MCT USE ONLY. STUDENT USE PROHIBITED
03-22 Configure Middleware and Services in ASP.NET Core
Once you have set up the ASP.NET Core MVC environment, you will want to configure a controller for
your use. First of all, you should create a folder named Controllers inside of the application root. After
that, you will want to add a new controller to that folder. Currently, this topic will not be covering how to
set up any specific controllers and actions. Instead, you will use a controller called HomeController and
use the Index action. This controller and action will be navigated to by default when you use
UseMvcWIthDefaultRoute middleware.
Best Practice: While not required to be placed inside of a Controllers folder, it is generally
a good practice to do so. A developer working on controllers, will usually look in the Controllers
folder and placing them outside of that folder can be a source of confusion.
A basic controller
public class HomeController : Controller
{
public IActionResult Index()
{
In this example, you can see a basic controller. Whenever the user requests an empty relative URL or
alternatively requests the relative URL /home/ or /home/index, the Hello from Controller string will be
displayed in the browser.
Note: The Content method allows us to return a string response. Additional methods
which return data that implements the IActionResult interface will be covered in Module 4,
“Developing Controllers”.
Once you have the controller set up, you will be able to inject services through the constructor. This
behaves in a similar way to Dependency Injection into services, where you can add support for a service
by explicitly adding a reference to the interface inside the constructor. Inside the constructor, you will be
able to save the instance for use in the specific methods.
The following code is an example of a service that will be injected into the controller:
Startup configuration
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMyService, MyService>();
services.AddMvc();
}
In this example, you can see a controller. Whenever the user requests an empty relative URL or
alternatively requests the relative URL /home/ or /home/index, the something string will be displayed
in the browser.
Note: The built-in Dependency Injection container in ASP.NET Core does not support
multiple constructors. Should the need arise to support multiple constructors alongside
Dependency Injection, you will need to use a different dependency injector.
MCT USE ONLY. STUDENT USE PROHIBITED
03-24 Configure Middleware and Services in ASP.NET Core
Service Lifetime
Now that you have learned how to create
services, the concept of service lifetime will be
covered. In general, while running the
application, you want to be able to correctly
preserve the service state in the application. If
you keep all of the services in the application
running constantly, you run the risk of creating
deadlocks while dealing with external resources
as well as potential issues with threading, as each
singleton service is locked to one thread since it
is first instantiated. On the other hand, keeping
all services completely stateless can deprive us of
the ability to retain data temporarily and prevent us from being able to manage data correctly.
AddSingleton
To handle persistent data, which you want to persist inside the memory rather than in an external source
(such as a database, file, or another source), you will require the service instance to persist throughout the
entire application lifespan. This can help you keep the same data consistent and persistent. To handle this
case, you will register the service using AddSingleton. This tells the Dependency Injection container to
create this service once and then to inject the same instance as long as the application remains running.
As an important note, should the application be stopped for any reason, this service will be stopped as
well.
The following code is an example of a service that generates a random number on instantiation and a
wrapper service that utilizes the first service:
public RandomService()
{
Random random = new Random();
_randomNumber = random.Next(1000000);
}
public int GetNumber()
{
return _randomNumber;
}
}
_randomService = randomService;
}
This is an example of a service that generates a random number on instantiation between 0 and 999,999,
as well as a service that provides a wrapper service to the random service. The wrapper service is designed
with the intention of creating a separate instance of Dependency Injection from the controller. It will be
used in all the other examples in this topic.
The following code demonstrates a controller that receives the services by injection:
This is an example of the controller you will use in conjunction with the random service and the wrapper
service. It will be used in the other examples in this topic.
The following code is an example of configuring the services as singleton:
AddSingleton usage
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IRandomService, RandomService>();
services.AddSingleton<IRandomWrapper, RandomWrapper>();
services.AddMvc();
}
In this example, upon a request to the Index action the The number from service in controller: x, the
number from wrapper service: x string will be displayed in the browser. Both the wrapper service and
the random service will provide the same value of x. It will remain constant no matter how many times
you request the Index action. Only by restarting the application will the value of x change.
AddScoped
Sometimes you will want to maintain data throughout the lifetime of a single request without affecting
data for other requests. This can be useful for dealing with particular parameters for the specific request
such as query string parameters or data which is retrieved due to a specific request, such as user
information relating to the user that made the request. For this purpose, you will use AddScoped. When a
service is registered by using AddScoped, all instances where it is injected as a result of the current
request being processed will receive the same instance. However, for any other request, a new instance
will be created.
The following code is an example of configuring services as scoped:
AddScoped usage
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IRandomService, RandomService>();
services.AddScoped<IRandomWrapper, RandomWrapper>();
services.AddMvc();
}
In this example, upon a request to the Index action the The number from service in controller: x, the
number from wrapper service: x string will be displayed in the browser, with the values of both the
random service and the wrapper service being identical. The value will change each time you make a
request to the Index action since the services are instantiated per request.
AddTransient
Some services will end up being stateless and will not need to store data. In that case, you will want to use
AddTransient. AddTransient is instantiated individually every time it’s injected, with each instance being
completely separate from all others. Due to this, data should never be stored on a transient service.
The following code is an example of configuring services as transient:
AddTransient usage
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IRandomService, RandomService>();
services.AddTransient<IRandomWrapper, RandomWrapper>();
services.AddMvc();
}
In this example, upon a request to the Index action the The number from service in controller: x, the
number from wrapper service: y string will be displayed in the browser, with the values of x and y being
different. These values will change whenever you make a request to the Index action, since each time the
service will be injected into a new controller instance, and a new wrapper instance.
MCT USE ONLY. STUDENT USE PROHIBITED
03-28 Configure Middleware and Services in ASP.NET Core
Objectives
After completing this lab, you will be able to:
• Use Microsoft ASP.NET Core static files such as HTML, CSS, and image files.
• Create and use a custom middleware and use its context information.
• Create and use services with ASP.NET Core built-in dependency injection.
• Inject a service to an ASP.NET Core MVC controller.
Lab Setup
Estimated Time: 75 Minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD03_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD03_LAK.md.
Review Question
Question: What are the advantages of using Dependency Injection over instantiating references?
Best Practice
Remember that the order of middleware in the Configure method is crucial to the application’s behavior.
While building the middleware pipeline it is important that you think of any potential clashing issues that
may occur inside the middleware and design it accordingly.
While running an application with UseMvc or Verify that there is a call to AddMvc in the
UseMvcWithDefaultRoute in the Configure ConfigureServices method.
method you get an error:
System.InvalidOperationException: 'Unable
to find the required services...'
You navigate to an action in an MVC controller Ensure that all services are correctly registered in
but get an HTTP 500 error page. the ConfigureServices method.
MCT USE ONLY. STUDENT USE PROHIBITED
04-1
Module 4
Developing Controllers
Contents:
Module Overview 04-1
Lesson 1: Writing Controllers and Actions 04-2
Lesson 2: Configuring Routes 04-10
Lesson 3: Writing Action Filters 04-26
Lab: Developing Controllers 04-32
Module Review and Takeaways 04-34
Module Overview
ASP.NET Core MVC is a framework for building web applications by using the Model-View-Controller
(MVC) architectural pattern. The controller is essentially responsible for processing a web request by
interacting with the model and then passing the results to the view. The model represents the business
layer, sometimes referred to as the domain, and may include data objects, application logic, and business
rules. The view uses the data that it receives from the controller to produce the HTML or other output that
is sent back to the browser.
In this module, you will learn how to develop controllers. Controllers are central to MVC applications.
Understanding how controllers work is crucial to being able to create the appropriate model objects,
manipulate them, and pass them to the appropriate views.
A controller is a class. It contains several methods. These methods are called actions. When an MVC
application receives a request, it finds which controller and action should handle the request. It
determines this by using Uniform Resource Locator (URL) routing.
URL routing is another very important concept necessary for developing MVC applications. The ASP.NET
Core MVC framework includes a flexible URL routing system that enables you to define URL mapping
rules within your applications.
To maximize the reuse of code in controllers, it is important to know how to write action filters. You can
use action filters to run code before or after every action in your web application, on every action in a
controller, or on other combinations of controller actions.
Objectives
After completing this module, you will be able to:
• Add a controller to a web application that responds to user actions that are specified in the project
design.
• Add routes to the ASP.NET Core routing engine and ensure that URLs are user-friendly in an MVC
web application.
• Write code in action filters that runs before or after a controller action.
MCT USE ONLY. STUDENT USE PROHIBITED
04-2 Developing Controllers
Lesson 1
Writing Controllers and Actions
A controller is a class that usually derives from the Controller base class. Controllers contain methods,
known as actions, which respond to user requests, process the requests, and return the results to the
response stream. The results they return can take several forms, though they are usually represented by
objects that implement the IActionResult interface. The most common object is a ViewResult object that
instructs the MVC to render a particular view. However, actions might also yield other types of results,
such as string content and instructions to redirect to other actions.
To process incoming user requests, manage user input and interactions, and implement relevant
application logic, it is important to know how to create controllers and actions. It is also important to
know about the parameters that an action receives.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe how a controller responds to user actions in an MVC web application.
• Write controller actions to respond to web browser requests.
• Explain how to pass information to views.
• Create controllers and actions.
Best Practice: Most simple examples pass model objects to the view. However, for large
applications, this is generally not a best practice. For large applications, we recommend that you
use ViewModels to separate the presentation from the domain.
The following code shows a controller that creates a model and passes it to a view:
The following code shows a view that receives the model and uses it to generate the response:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
@Model.Value
</body>
</html>
Note: You can modify the preceding behavior in several ways. For example, you can create
routes that interpret the preceding URLs differently. These examples are true only when the
default route exists. Routes will be covered in Lesson 2, “Configuring Routes”.
MCT USE ONLY. STUDENT USE PROHIBITED
04-4 Developing Controllers
Let us consider a request whose URL is http://www.contoso.com/photo. By default, ASP.NET Core MVC
will extract the word photo from the URL and instantiate a controller named PhotoController, if one
exists. You should follow this convention when you create and name controllers. Otherwise, the
application will return unexpected 404 errors and the controllers will not work as intended.
The Microsoft Visual Studio project templates include a folder named Controllers. Programmers should
create their controllers in this folder or in its subfolders. MVC relies on convention over configuration.
Therefore, we recommend adhering to the conventions. By default, Visual Studio places controllers in the
ProjectName.Controllers namespace.
For every action that returns a ViewResult object, there should be a corresponding view template. The
view will generate the output sent to the browser.
Actions usually use ActionResult as the return type. The ActionResult class is an abstract class, and you
can use a range of derived classes that inherit from it to return different responses to the web browser. An
example of a class that derives from the ActionResult class is the ContentResult class, which can be used
to return text to the web browser. You can return plain text, XML, a comma-separated table, or other text
formats. This text can be rendered by the web browser or processed by client-side code.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 04-5
The following code shows how to return a ContentResult object from an action:
The following code shows how to use the RedirectToRouteResult object to redirect to an action in
another controller:
Using Parameters
When users request webpages, they often specify
additional information besides the name of the
webpage itself. For example, when they request a
product details page, they might specify the
name or catalog number of the product to
display. Such extra information is referred to as
parameters. The powerful model binding
mechanism helps make these parameters more
flexible and concise.
The model binders obtain parameters from a
user request and pass them to action methods.
The model binders can locate parameters in a
posted form, routing values, a query string, or posted files. If the model binders find a parameter declared
in the action method that matches the name and type of a parameter from the request, the action
method is called and the parameter is passed from the request. This arrangement enables you to obtain
and use parameters in your actions.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 04-7
For example, assume that an MVC web application contains a route in the Startup.cs file. The model
binder can use this route to find the parameters and then pass them to an action.
The following code shows a route that appears in the Startup.cs file:
Example of a route
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
});
}
The following code shows how the model binder locates parameters in routing values:
When a user types the URL http://host/Home/Index/1, the Index action method of the HomeController
class is called. The id parameter of the method gets the value 1. The browser displays 1 as the output in
this case.
In a similar way, the model binder locates parameters in a query string. For example, in case a user types
the URL http://host/Home/Index?title=someTitle, the Index action method of the HomeController class
is called. The title parameter of the method gets the value someTitle. The browser displays someTitle as
the output in this case.
MCT USE ONLY. STUDENT USE PROHIBITED
04-8 Developing Controllers
The following code shows how the model binder locates parameters in a query string:
However, in some cases you might want to augment the information in the model class with some extra
values. For example, you might want to send a title, which needs to be inserted in the page header, to the
view. Furthermore, some views do not use model classes. The home page of a website, for example, often
does not have a specific model class. To help in these situations, you can use two other mechanisms to
provide extra data: the ViewBag and ViewData properties. These are almost identical.
The following code shows how to obtain and use the same properties in a view by using Razor:
To obtain and use the same values in a view, you can use the following Razor code:
Demonstration Steps
You will find the steps in the section “Demonstration: How to Write Controllers and Actions“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD04_DEMO.md#demonst
ration-how-to-write-controllers-and-actions.
MCT USE ONLY. STUDENT USE PROHIBITED
04-10 Developing Controllers
Lesson 2
Configuring Routes
You can use ASP.NET Core to control the URLs that a web application uses. You can link the URLs and the
content by configuring routes. A route is a rule. Because routes are configurable, you can link URLs and
content more effectively. ASP.NET Core uses routes to parse a requested URL to determine the controller
and action to which the request must be forwarded.
You need to know how to add a new route to your application. You also need to know how the routing
engine interprets a route so that a requested URL is handled by the appropriate controller and action, and
your application can expose meaningful URLs.
Lesson Objectives
After completing this lesson, you will be able to:
4. The action is then invoked. Often, the action creates a new instance of a model class, perhaps by
querying the database with the parameters passed to it by the invoker. This model object is passed to
a view to render the results and send them to the response.
Routing governs the way URLs are formulated and how they correspond to controllers and actions.
Routing does not operate on the protocol, server, domain, or port number of a URL, but it operates only
on the directories and file name in the relative URL. For example, in the URL,
http://www.contoso.com/photo/display/1, routing operates on the /photo/display/1 relative path.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 04-11
• To formulate URLs in webpage links and other elements. When you use helpers such as
Html.ActionLink in the MVC views, the helpers construct URLs according to the routes in the routing
table. These are called outgoing URLs.
• Configure routes by using convention-based routing. In this case, the routes are configured in the
Startup.cs file and have an impact on the entire application.
• Configure routes by using attributes. As the name implies, attribute routing uses attributes to define
routes.
You can combine convention-based routing and attribute-based routing in the same MVC application.
The default route is simple and logical. It works well with many web applications. The default route
examines the first three segments of the URL. Each segment is delimited by a forward slash:
• The first segment is interpreted as the name of the controller. The routing engine forwards the
request to this controller. If a first segment is not specified, the default route forwards the request to a
controller called HomeController.
• The second segment is interpreted as the name of the action. The routing engine forwards the
request to this action. If a second segment is not specified, the default route forwards the request to
an action called Index.
• The third segment is interpreted as an id value, which is passed to the action as a parameter. This
parameter is optional, so if a third segment is not specified, no default value is passed to the action.
For example, if a user requests the URL, http://www.contoso.com/photo/display/1, the id parameter 1 is
passed to the Display action of the PhotoController controller.
MCT USE ONLY. STUDENT USE PROHIBITED
04-12 Developing Controllers
• To improve search engine rankings. Search engines do not prioritize webpages that have GUIDs or
long query text in the URL. Some web bots do not even crawl such links. In addition, some search
engines boost a page’s ranking when one or more of its keywords appear in the URL. User-friendly
URLs are therefore a critical tool in Search Engine Optimization (SEO).
Information architecture is a subject that is closely related to SEO. This is because both information
architecture and SEO are relevant to the structure, hierarchy, and accessibility of the objects in your web
application. Users click links on menus to navigate to the pages that interest them. Web bots use the same
links to navigate the web application and crawl its content. Users prefer URLs without GUIDs and long
query text because they are meaningful. Web bots often ignore links with GUIDs and long query text in
them. In addition, when keywords appear in URLs, web bots prioritize a webpage in search results.
As an ASP.NET Core MVC developer, you must understand SEO principles and use them whenever you
write code to ensure that you do not impact the search engine positioning of your web application.
Routes and the configuration of the routing engine are also critical, because by using routes, you can
control the URLs that your web application generates.
The ConfigureServices method is used to add services to the container. The Configure method is used
to add MVC to the request pipeline. As you can see, no routes have been configured in the Startup class
yet.
Note: The Startup class was covered in Module 3, “Configure Middleware and Services in
ASP.NET Core”.
Assume that there is a controller in the MVC application. Will it be possible to navigate to the controller?
If a user requests the / relative URL, the request will fail. Also, if a user requests the /Some/Display
relative URL, the request will fail.
The reason for this is that no routes are configured in the Startup class. In this case, you must add a
convention-based route. Before adding a route, it is important to understand the properties of a route.
This is to ensure that these properties match the URL segments correctly and pass the requests and
parameters to the right location.
Properties of a Route
The properties of a route include name and template, both of which are string properties. The name
property assigns a name to a route. It is not involved in matching or request forwarding. The template
property is a URL pattern that is compared to a request URL to determine if the route should be used. You
can use segment variables to match a part of the URL. Use braces to specify a segment variable.
For example, if you set the template of a route to {controller}/{action} and then a user requests the
/Some/Display relative URL, the request will succeed. The {controller} segment variable maps to
SomeController, and the {action} segment variable maps to Display. If a user requests the / relative URL,
the request will fail because no default values were set.
The following code shows the Configure method in the Startup class with a route:
You can change the value of the name property. For example, you can change it to MyRoute, as long as
the name property is unique in the routes collection. You also have the flexibility to change the order of
the segment variables. If you change the route that is written in the template to {action}/{controller} and
then a user requests the /Some/Display relative URL, the request will fail. However, if a user requests the
/Display/Some relative URL, the request will succeed.
The following code shows a route with different segment variables order and a different name:
If you use the {controller}/{action} template, only URLs with two segments will be considered legal. For
example, if a user requests the /Some/Display/1 relative URL, the request will fail.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 04-15
Furthermore, assume that there is an action that gets a parameter named param. In this case, if there is a
route with the template {controller}/{action}/{param}, the model binding mechanism will map the value
entered in the URL to the value of the param parameter in the action. For example, assume that there is
an action named ShowParam that gets a parameter named param and sends its value to the browser. In
this case, if a user requests the /Some/ShowParam/hello relative URL, the param parameter gets the
value hello, which in turn is sent to the browser.
The following code shows an action that gets a parameter named param and sends its value to the
browser:
If you use the above route, the ShowParam action of the SomeController controller will be invoked
when a user requests the /, /Some, and /Some/ShowParam relative URLs. In all these cases, the value of
the param parameter will be val.
MCT USE ONLY. STUDENT USE PROHIBITED
04-16 Developing Controllers
You can set the default values of a route directly in the template property.
For example, you can replace the template and default properties of the previous example with a
template property with the following value: {controller=Some}/{action=ShowParam}/{param=val}.
These two examples create equivalent routes:
If a user requests the /Some/ShowParam/1 relative URL, the request will succeed. However, if a user
requests the /Some/ShowParam/a relative URL, the request will fail.
It is possible to specify route constraints directly in the template property.
For example, assume that you want to ensure that the route value param must be convertible to an
integer. For this, you can set the following route:
For example, refer to the following code that shows a route with the dataTokens property:
routes.MapRoute(
name: "secondRoute",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
});
}
Alternatively, to fix a route to a single action, you can specify a value for the action variable in the
defaults property.
Segment variables or default values with other names have no special meaning to MVC and are passed to
actions. You can access the values of these variables by using one of two methods: by using the
RouteData.Values collection or by using model binding to pass values to action parameters.
Example of a route
app.UseMvc(routes =>
{
routes.MapRoute(
name: "someRoute",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Example", action = "Print" });
});
If a user requests the /Example/Print relative URL, the Controller: Example. Action: Print string is
displayed in the browser.
In a similar way, you can use the RouteData.Values collection to access other segment variables.
The following code shows a controller that uses the RouteData.Values collection to access the id
segment value:
If a user requests the /Example/Print/1 relative URL, the following string appears on the screen: id: 1.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 04-19
The question mark in {id?} specifies that id is optional. If the relative URL has three segments, the route
matches and the third segment is passed to the RouteData.Values["id"] value. If the relative URL has
only two segments, the route matches as well, because the {id} is optional. In this case, the
RouteData.Values["id"] value is null, and the string id: displays on the screen.
If a user requests the /Example/Print/1 relative URL, the action returns the following string: id: 1.
The parameters of the action method don’t have to be of the string type. For example, you can change
the Print action to get an int as a parameter.
The following code shows an action that gets an int as a parameter:
If a user requests the /Example/Print/1 relative URL, the string id: 1 will be returned. If a user requests
the /Example/Print/a relative URL, the string id: 0 will be returned. If a user requests the
/Example/Print relative URL, the string id: 0 will be returned. This is because int is a value type, which
cannot store null values.
You can allow value types to have null values if you mark them as Nullable. For example, if you change
the Print action to get a parameter of type int? and then a user requests the /Example/Print/a relative
URL, the null value will be assigned to id.
The following code shows an action that gets a parameter of type int?:
A nullable parameter
public class ExampleController : Controller
{
public IActionResult Print(int? id)
{
return Content("id: " + id);
}
}
MCT USE ONLY. STUDENT USE PROHIBITED
04-20 Developing Controllers
If a user requests the /Example/Print/a and /Example/Print relative URLs, the string id: 444 will be
returned. If a user requests the /Example/Print/1 relative URL, the string id: 1 will be returned.
You can set several segments in the template of a route, and all of them can be matched to parameters of
an action.
The following code shows a template with several segments:
By using the above route, you can write an action that gets two parameters, id and title. Both parameters
can be matched with the segments in the request URL.
The following code shows an action that has two parameters:
If a user requests the /Example/Print relative URL, the request will fail because the id segment in the
route is mandatory. If a user requests the /Example/Print/1 relative URL, the request will succeed
because the title segment in the route is optional. In this case, the id parameter will have the value 1 and
the title parameter will have the value null. If a user requests the /Example/Print/1/SomeTitle relative
URL, the request will succeed. In this case, the id parameter will have the value 1 and the title parameter
will have the value SomeTitle.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 04-21
You can use the Route attribute to specify the route for a specific action. For example, consider a
controller named MyController that contains an action named SomeMethod. If a user requests the
/Some relative URL, the request will fail. However, if you add [Route("Some")] above the declaration of
the SomeMethod action, the request will succeed.
The following code shows how to configure a route by using an attribute:
Attribute-based routing
public class MyController : Controller
{
[Route("Some")]
public IActionResult SomeMethod()
{
return Content("Some method");
}
}
The following code shows how to configure an attribute route with a parameter. Note that the
components of the route are named differently from the name of the action method:
The Route attribute can be used to get several segment variables. The Route attribute can also be used
to ensure that a segment variable is of a specific type. For example, setting the attribute
[Route("My/{param1}/{param2:int}"] configures a route in which two segment variables should exist,
param1 and param2. param2 must be of type int.
The following code shows how to configure an attribute route with two parameters, where the second
parameter must be of type int:
Based on the above example, if a user requests the /My/a relative URL, the request will fail. Also, if a user
requests the My/a/b relative URL, the request will fail because b cannot be converted to an int. However,
if a user requests the /My/a/1 relative URL, the request will succeed, and param1 will have the value a
and param2 will have the value 1.
Optional Parameters
You can configure a segment variable to be optional by using a question mark. For example, to specify
that param2 should be optional, use {param2?}.
The following code shows how to configure an attribute route to be optional:
Based on the above example, if a user requests the My/a relative URL, the request will succeed because
param2 is optional. In this case, the value of param2 will be null. On the other hand, if a user requests
the My relative URL, the request will fail because param1 is not optional.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 04-23
[Route("My/Method2")]
public IActionResult Method2()
{
return Content("Method2");
}
}
Best Practice: All the routes in a controller class should start with the same prefix.
You can set a common prefix for an entire controller class by using the [Route] attribute above the
controller class.
The following code shows how a Route attribute can be applied to a controller class:
[Route("Method2")]
public IActionResult Method2()
{
return Content("Method2");
}
}
Based on the above example, if a user requests the /My/Method1 relative URL, the request will succeed.
However, if a user requests the /Method1 relative URL, the request will fail.
Convention-based route
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}");
MCT USE ONLY. STUDENT USE PROHIBITED
04-24 Developing Controllers
});
Consider a controller class named MyController that has two actions, an action named Method1 that has
the [Route("SomeRoute")] attribute route and an action named Method2 with no Route attribute.
The following code shows a controller class with two actions, one with a Route attribute and the second
without a Route attribute:
When a user requests the /SomeRoute relative URL, the Method1 action runs. This is because of the
attribute route that is configured above the method. When a user requests the /My/Method2 relative
URL, the Method2 action runs. This is because of the centralized route that is configured in the Startup
class.
When a user requests the /cities/1 relative URL, the GetCity action runs. This action only matches the
HTTP GET verb because it is decorated with the HttpGet attribute.
To make the attribute routing less repetitive, you can apply the Route attribute to a controller, and apply
the Http[Verb] attribute to an action. In such cases, the Route attribute is used to set the common prefix
for an entire controller class, and its template is prepended to the template of the action.
The following code shows how you can combine the Route attribute with the Http[Verb] attributes:
[HttpGet("{id}")]
public ActionResult GetCity(int id) { ... }
}
When a user requests the /cities relative URL, the ListCities action runs. When a user requests the
/cities/1 relative URL, the GetCity action runs.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Add Routes“ on the following page:
https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD04_DEMO.md#demonst
ration-how-to-add-routes.
MCT USE ONLY. STUDENT USE PROHIBITED
04-26 Developing Controllers
Lesson 3
Writing Action Filters
In some situations, you might have to run specific code before or after controller actions are run. For
example, before a user runs any action that modifies data, you might want to run code that checks the
details of the user account. If you add such code to the actions themselves, you will have to duplicate the
code in all the actions where you want the code to run. Action filters provide a convenient way to avoid
code duplication. You also can use action filters to write thinner controllers and separate responsibilities.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe action filters.
• Create action filters.
• Exception Filters. Exception filters run only if an action method or another filter throws an exception.
These filter classes are used to handle errors. Exception filters implement either the IExceptionFilter
interface or the IAsyncExceptionFilter interface.
• Result Filters. Result filters run before and after an action result is run. Result filters implement either
the IResultFilter interface or the IAsyncResultFilter interface.
You can create your own filter classes or use existing filter classes. The following are examples of existing
filter classes that you can use:
• The ResponseCacheAttribute filter class. Annotating an action with the ResponseCache attribute
will cache the response to the client. The ResponseCache attribute contains several properties. One
of them is the Duration property, which gets or sets the duration, in seconds, for which the response
is cached. Caching will be covered in detail in Module 12, “Performance and Communication”.
• The AllowAnonymousAttribute filter class. Annotating an action with the AllowAnonymous
attribute will allow users to access an action without logging in. Authentication and authorization will
be covered in Module 11, “Managing Security”.
• The ValidateAntiForgeryTokenAttribute filter class. Annotating an action with the
ValidateAntiForgeryToken attribute will help prevent cross-site request forgery attacks. Defending
from attacks will be covered in Module 11, ”Managing Security”.
When you navigate to the Index action in the MyController controller, the Index started and Index
finished lines are displayed in the Visual Studio Output Window.
You can use the ResultExecutedContext parameter to get the content that will be returned to the
browser.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 04-29
The following code shows how the content returned to the browser can be retrieved in the
OnResultExecuted event handler:
When you navigate to the Index action in the MyController controller, the Result: some text text is
displayed in the Visual Studio Output Window.
Writing to the Visual Studio Output Window might be insufficient in many cases. Writing the output to
other targets, for example a log file, may be a better choice in most situations.
The following code shows how you can change the content of the SimpleActionFilter class so that it
writes the output to a log file that is located at c:\logs\log.txt:
When you navigate to the Index action in the MyController controller, a file named log.txt will be
created.
The following code shows the content of the c:\logs\log.txt log file:
In case you need your filters to get dependencies using their constructor, you can add filters by type. To
add a filter by type, you can use the ServiceFilter attribute.
For example, assume your filter needs to receive in its constructor an instance of IHostingEnvironment:
}
}
To apply the LogActionFilter filter to an action named Index, you can use the ServiceFilter attribute:
Demonstration Steps
You will find the steps in the section “Demonstration: How to Create and Use Action Filters“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD04_DEMO.md#demonst
ration-how-to-create-and-use-action-filters.
MCT USE ONLY. STUDENT USE PROHIBITED
04-32 Developing Controllers
Objectives
After completing this lab, you will be able to:
• Add an MVC controller to a web application
• Write actions in an MVC controller that respond to user operations
• Add custom routes to a web application
• Write action filters that run code for multiple actions
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD04_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD04_LAK.md.
Review Question
Question: An MVC application contains many actions that have no parameters. You want to set a
routing rule to match these actions. What should you do?
Best Practice
The Visual Studio project templates include a folder named Controllers. Programmers should create their
controllers in this folder or its subfolders. MVC relies on convention over configuration. Therefore, we
recommend adhering to the conventions. Visual Studio places controllers in the
ProjectName.Controllers namespace, by default.
You navigate to an existing action, but Verify that the routes are configured correctly in the
get an HTTP 404 Not Found error. MVC application.
A configuration-based route never takes You must ensure that you add configuration-based
effect. routes in the correct order. In general, the most
specific routes should be added first and the least
specific should be added last.
MCT USE ONLY. STUDENT USE PROHIBITED
05-1
Module 5
Developing Views
Contents:
Module Overview 05-1
Lesson 1: Creating Views with Razor Syntax 05-2
Lesson 2: Using HTML Helpers and Tag Helpers 05-12
Lesson 3: Reusing Code in Views 05-20
Lab: Developing Views 05-29
Module Review and Takeaways 05-31
Module Overview
Views are one of the three major components of the Model-View-Controller (MVC) programming model.
You can define the user interface for your web application by creating views. A view is a combination of
HTML markup and C# code that runs on a web server. Therefore, to create a view, you need to know how
to write the HTML markup and C# code and use the various helper classes that are built into MVC. You
also need to know how to create partial views and view components, which render sections of HTML that
can be reused in your web application.
Objectives
After completing this module, you will be able to:
• Create an MVC view and add Razor markup to it to display data to users.
• Use HTML helpers and tag helpers in a view.
• Reuse Razor markup in multiple locations throughout an application.
MCT USE ONLY. STUDENT USE PROHIBITED
05-2 Developing Views
Lesson 1
Creating Views with Razor Syntax
When an MVC web application receives a request, a controller action processes the request. Often, the
controller action passes a model object to a view. The view builds the data by inserting the properties
from the model object and other sources into the HTML markup. Then, the view renders the completed
data to the browser. The browser displays the data it receives from the view.
To create views, you must understand how MVC interprets the code you place in views and how a
completed HTML page is built. By default, the Razor view engine performs this interpretation. The Razor
view engine lets you control the rendered HTML.
Lesson Objectives
After completing this lesson, you will be able to:
Adding Views
In an MVC application, controllers handle all the
application logic. Each controller contains one or
more action methods, and an action handles the
incoming web requests. Views handle the
presentation logic and should not contain any
application logic. Views are used to render a
response to the browser.
In an MVC application, there is usually one controller for every model class. For example, a model class
named Product usually has a controller called ProductController that contains all the actions relevant to
products. There might be some controllers that do not correspond to any model classes. However, each
controller can have multiple views. For example, you might want to create the following views for
Product objects:
• Details. The Details view can display a product, its price, catalog number, and other details.
• Create. The Create view can enable users to add a new product to the catalog.
• Edit. The Edit view can enable users to modify the properties of an existing product.
• Delete. The Delete view can enable users to remove a product from the catalog.
• Index. The Index view can display all the product objects in the catalog.
By convention, all the views in an MVC application reside within the top-level Views folder. Within this
folder, there is a folder for each controller that is used in the application. In the preceding example, the
Views folder would contain a Product folder and this folder would contain the Details, Create, Edit,
Delete, and Index views.
If you use the Razor view engine and the C# language, views files are created with a .cshtml extension.
Other view engines might use different extensions.
Property Description
View name This is the name of the view. The view file name is this name with
the appropriate extension added. The name you choose should
match the name returned by the corresponding controller action. If
the action controller does not specify the name of the view to use,
MVC assumes the name of the view matches the name of the
controller action.
Model class If you create a strongly-typed view, you need to specify the model
class to bind to the view. Visual Studio will use this class when it
formulates IntelliSense prompts and checks for compile-time errors.
Template A template is a basic view that Visual Studio can use to create the
view. If you specify a model class for the view, Visual Studio can
create simple templates for Create, Edit, Details, Delete, and List
views. If you don’t specify the model class for the view, you may
choose to build a view from the Empty (without model) scaffold
template.
Reference script libraries When you select this check box in MVC 5, references to common
client-side script files are included in the project. In ASP.NET Core
MVC, this check box has no effect.
Create as a partial view A partial view is a section of Razor code that you can re-use in
multiple views in your application.
Use a layout page A layout page can be used to impose a standard layout and
branding on many pages within the web application.
Within a controller, you can use the View method to create a ViewResult object. The view renders data
to the browser.
MCT USE ONLY. STUDENT USE PROHIBITED
05-4 Developing Views
The following code shows a simple controller class that calls the View method to create a ViewResult
object:
The following code shows the content of a file named Index.cshtml that is located in the Views/Product
folder:
A view
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
Hello from the Index view
</body>
</html>
If a user requests the relative URL, /Product/Index, the text, “Hello from the Index view,” will be sent to the
browser.
A section of code marked with the @ symbol is referred to as a Razor code expression. In the Razor
syntax, you mark the start of a Razor code expression with the @ symbol. However, there is no expression
ending symbol. Instead, Razor infers the end of a code expression by using a fairly sophisticated parsing
engine.
In the following code example, the text “Result: ab Razor knows that I’m an html!!! ab I’m a regular text –
Razor is smart enough to know I’m a server-side code. Ab” is sent to the browser:
Using Razor
Result: @String.Concat("a", "b") <span>Razor knows that I'm an html!!!</span>
@String.Concat("a", "b") I'm a regular text - Razor is smart enough to know I'm not a
server-side code. @String.Concat("a", "b")
In the above example, the Razor engine translates the @i expression to the value of i variable. The value
of i is sent to the browser. You can even use expressions such as @i*2, which multiplies the value of i with
2.
Razor has sophisticated logic to distinguish code from content and often the @ symbol is all that is
required. However, occasionally, you might find that an error occurs because Razor misinterprets content
as code. To fix such errors, you can use the @: delimiter, which explicitly declares a line as content and not
as code.
The following code shows how you can use the @: delimiter. The text “iiiii” is sent to the browser:
Razor easily differentiates content from code. For example, even within a Razor code block, when you add
an HTML element such as <span>, Razor interprets the text as content. Usually, you do not have to use
the @: delimiter to make this explicit.
In the following code the text “i i i i i” is sent to the browser:
If you want to send the value of the i variable to the browser, you can place an @ symbol before i.
In the following code, the text “0 1 2 3 4” is sent to the browser:
Use @@ in a view
@for (int i = 0; i < 5; i++)
{
<span>@@</span>
}
If you want to declare several lines as content, use the <text> tag instead of the @: delimiter. Razor
removes the <text> and </text> tags before returning the content to the browser.
The following code shows how you can use the <text> tag. The text “i i i i” is sent to the browser:
Razor Comments
You might want to include comments in your Razor code to describe it to other developers in your team.
This is an important technique that improves developer productivity by making the code easy to
understand.
You can declare a Razor comment by using the @* delimiter, as shown in the following code example:
A Razor comment
@* This text will not be rendered by the Razor view engine because this is a comment. *@
A Controller class
public class ProductController : Controller
{
[Route("Product/Index")]
public IActionResult Index()
{
ViewBag.Price = 2;
return View();
}
}
In the following code, the Razor view engine renders the text “Price Including Sales Tax: 2 * 1.2” to the
browser:
You can control and alter this behavior by using parentheses to enclose the expression so that Razor can
evaluate the expression.
For example, in the following code, the Razor view engine renders the text “Price Including Sales Tax: 2.4”
to the browser:
@if (i < j)
{
@i
}
}
Razor Loops
Razor also includes code blocks that loop through collections. You can loop through all the objects in an
enumerable collection by using the foreach code block.
For example, consider the following controller class that sets the value of the ViewBag.Names property
to contain a list of strings:
A Controller class
public class NameController : Controller
{
[Route("Name/Index")]
public IActionResult Index()
{
ViewBag.Names = new List<string>() { "name1", "name2", "name3" };
return View();
}
}
You can loop through all the strings in the ViewBag.Names property by using the foreach code block, as
shown in the following code. The text “name1 name2 name3” is sent to the browser:
Razor loops are useful for creating index views, which display many objects of a particular model class. A
product catalog page, which displays many products from the database, is a good example of an index
view. To implement an index view, a controller action passes an enumerable collection of objects to the
view.
A Model class
namespace Models
{
public class Product
{
public string Name { get; set; }
// Other fields are omitted
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 05-9
The following controller class creates a list of products and passes it to a view:
A Controller class
public class ProductController : Controller
{
[Route("Product/Index")]
public IActionResult Index()
{
Product p1 = new Product() { Name = "Product1" };
Product p2 = new Product() { Name = "Product2" };
List<Product> products = new List<Product>() { p1, p2 };
return View(products);
}
}
The following view gets the products from the controller. The Razor view engine sends the text “product1
product2” to the browser:
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use the Razor Syntax“ on the following
page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD05_DEMO.md#demonst
ration-how-to-use-the-razor-syntax.
Despite its many advantages, some developers might prefer to use a different a view engine. The reasons
can include:
• View Location Logic. Razor assumes that all views are located in the Views top-level folder. Within
this, Razor assumes that each controller has a separate folder. For example, views for
ProductController are located in the Product folder within Views. Razor also looks for some views
in the Shared subfolder. These views can be used by more than one controller. If you do not want to
use this view location logic, you can use an alternate view engine.
• View Syntax. Each view engine uses a different syntax in view files. For example, in Razor the @
symbol delimits the server-side code. Other view engines can use different delimiters. You might
prefer one syntax over another based on the technologies that you or your team previously worked
with.
You can create your own view engine to implement custom logic for locating views or to implement
custom syntax for view files. To create a custom view engine, you should create a class that implements
the IViewEngine interface. The IViewEngine interface defines the contract for a view engine. It contains a
FindView method, which finds a specified view by using a specified action context. The FindView method
returns a ViewEngineResult object.
Custom view engines are rarely created because they require the developers to write sophisticated string-
parsing code. The default view engine, Razor, is powerful and flexible. The default logic for finding view
files is also simple and rarely needs modification.
Formatting service
namespace Services
{
public interface IFormatNumber
{
string GetFormattedNumber(int number);
}
public class FormatNumber : IFormatNumber
{
public string GetFormattedNumber(int number)
{
return string.Format("{0:n0}", number);
}
}
}
The following code is an example of a view that calls the FormatNumber service:
@formatNumber.GetFormattedNumber(1234535334)
This example presents a simple service for formatting numbers as string. Specifically, it will add commas to
numbers every third digit. In the second snippet, the service is injected into a view. If this view is called,
the string “1,234,535,334” will be displayed in the browser. In particular, take note that the service
namespace, Services, matches up with the namespace in the @inject directive.
Note: As an additional note, you can also add a using directive with the @ symbol. This
can allow you to specifically apply a namespace to the view, just as you would do in a C# class.
For example, in this example, if you used @using Services, you would be able to update the
@inject directive to @inject IFormatNumber formatNumber.
Best Practice: Generally, you will want injected services to be placed at the top of the
HTML. This is mainly to improve the view legibility. While the Razor engine can handle injection
at the end of a page, using it can lead to unclear code.
MCT USE ONLY. STUDENT USE PROHIBITED
05-12 Developing Views
Lesson 2
Using HTML Helpers and Tag Helpers
ASP.NET Core MVC includes many tag helpers and HTML helpers that you can use in views. Helpers
include common logic that makes it easier for you to render HTML in MVC applications. In this lesson,
some tag helpers and HTML helpers will be introduced.
Lesson Objectives
After completing this lesson, you will be able to:
• Use HTML helpers in a view.
• Use tag helpers in a view.
• Use the Html.ActionLink and Url.Action helpers to call a controller action.
A Controller class
public class HomeController : Controller
{
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
[Route("Home/AnotherAction")]
public IActionResult AnotherAction()
{
return Content("AnotherAction result");
}
}
In the following code example, the Html.ActionLink helper is used in the Index view to render an <a>
element:
If a user requests the relative URL /Home/Index, the following HTML will be sent to the browser:
<a href="/Home/AnotherAction">Press me</a>.
If the user clicks the Press me link, the AnotherAction action of the HomeController controller will be
called, and the text “AnotherAction result” will appear on the browser.
Using Html.ActionLink to Call Actions in Other Controllers
In the previous example, the @Html.ActionLink helper is used to call an action in the same controller.
You can also use the @Html.ActionLink helper to call actions in other controllers.
MCT USE ONLY. STUDENT USE PROHIBITED
05-14 Developing Views
The following code example shows two controllers. Each controller contains one action. The action of the
first controller returns a ViewResult object and the action of the second controller returns a
ContentResult object:
The following code example shows how the Html.ActionLink helper is used in the Some view to render
an <a> element. If the user clicks the link in the browser, MVC will direct the request to the MyAction
action in the SecondController controller:
If a user requests the relative URL, /First/Some, the following HTML will be sent to the browser:
If the user clicks the Press the link link, the text Second Controller appears in the browser.
Passing Parameters to Html.ActionLink
You can pass parameters to the Html.ActionLink helper.
The following code example shows a controller class with two action methods. The second action method,
Display, gets a parameter named id of type int:
A Controller class
public class PhotoController : Controller
{
[Route("Photo/Choose")]
public IActionResult Choose()
{
return View();
}
[Route("Photo/Display/{id}")]
public IActionResult Display(int id)
{
string res = $"Photo number {id} was chosen";
return Content(res);
}
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 05-15
In the following code example, the Html.ActionLink helper is used to render an <a> element. If the user
clicks the link, the Display action in the PhotoController controller will be called, and an integer value
will be passed to the id parameter:
If a user requests the relative URL, /Photo/Choose, the following HTML will be sent to the browser:
If the user clicks the Click here to view photo 1 link, the text “Photo number 1 was chosen” appears in
the browser. If the user clicks the Click here to view photo 2 link, the text “Photo number 2 was chosen”
appears in the browser.
You can use the Url.Action helper to populate the src attribute of an <img> element or a <script>
element. You also can use this helper whenever you want to formulate a URL to a controller action
without rendering a hyperlink.
The following code example shows how to use the Url.Action helper within the src attribute of an img
element:
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use HTML Helpers“ on the following page:
https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD05_DEMO.md#demonst
ration-how-to-use-html-helpers.
The following code example shows a controller class with two action methods:
A Controller class
public class HomeController : Controller
{
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
[Route("Home/AnotherAction")]
public IActionResult AnotherAction()
{
return Content("AnotherAction result");
}
}
The following code example shows the Html.ActionLink HTML helper followed by a tag helper:
If a user requests the relative URL /Home/Index, the following HTML will be sent to the browser:
Notice that the HTML produced by the HTML helper and the tag helper are identical.
The following code example shows the Html.ActionLink HTML helper followed by a tag helper:
Using a HTML Helper and a Tag Helper to call actions in other controllers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
If a user requests the relative URL, /First/Some, the following HTML will be sent to the browser:
Notice that the HTML produced by the HTML helper and the tag helper are identical.
A Controller class
public class PhotoController : Controller
{
[Route("Photo/Choose")]
public IActionResult Choose()
{
return View();
}
[Route("Photo/Display/{id}")]
public IActionResult Display(int id)
{
string res = $"Photo number {id} was chosen";
return Content(res);
}
}
In the following code example, the AnchorTagHelper tag helper is used to with an asp-route-{id}
attribute. If the user clicks the link, the Display action in the PhotoController controller will be called,
and an integer value will be passed to the id parameter:
If a user requests the relative URL /Photo/Choose, the following HTML will be sent to the browser:
If the user clicks the Click here to view photo 1 link, the text “Photo number 1 was chosen” appears in
the browser. If the user clicks the Click here to view photo 2 link, the text “Photo number 2 was chosen”
appears in the browser.
You can add the @addTagHelper directive to the _ViewImports.cshtml file in the Views folder:
In this case, you don’t need any more to add the @addTagHelper directive to the Index.cshtml file:
AnchorTagHelper is one of the built-in tag helpers. There are other built-in tag helpers such as
InputTagHelper, which is associated with the input HTML element, and ValidationMessageTagHelper,
which is used to display validation error messages. You also can create custom tag helpers. All tag helpers
implement the ITagHelper interface, and many tag helpers inherit from the TagHelper class.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use Tag Helpers“ on the following page:
https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD05_DEMO.md#demonst
ration-how-to-use-tag-helpers.
MCT USE ONLY. STUDENT USE PROHIBITED
05-20 Developing Views
Lesson 3
Reusing Code in Views
In a web application, you frequently render similar blocks of HTML content in different webpages. For
example, in an e-commerce web application, you might want to display the most popular products at
multiple locations such as the home page, the front page of the product catalog, and the top of the
product search page. To render such HTML content in an MVC application, you can copy and paste code
from one Razor view to other Razor views.
A better practice is to use a partial view. A partial view renders only a section of HTML content, which you
can then insert into several other views at run time. Because a partial view is a single file that is reused in
several locations in a web application, if you implement a change in one location, the change is updated
in other locations. Partial views increase the manageability of MVC web applications and facilitate a
consistent presentation of content throughout a web application.
A view component is another way to reuse code in views. View components are similar to partial views.
However, view components have many benefits when compared to partial views. They can be treated as
mini controllers because they render a chunk of data instead of rendering the whole response. A view
component consists of a class and a view. The class is usually inherited from the ViewComponent class
and the view uses Razor to call the methods in the class.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe how partial views enable you to reuse Razor code.
• Create partial views in an MVC web application.
• Use partial views in an MVC web application.
• Create a view component in an MVC web application.
• Use a view component in an MVC application.
3. Then, create another controller action that uses the article ID to find comments that are specific to
that article. The controller action then passes this collection of comments to the partial view.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 05-21
4. Finally, call the appropriate actions from the home page and the article view.
Because the partial view renders a collection of comments, you can reuse it in various situations by
passing different collections to it from the controller actions.
The following code shows a simple controller, which calls a view that contains partial views:
A Controller class
public class HomeController : Controller
{
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
}
The following lines of code show how to call the partial view that is located in the
_MyPartialView.cshtml file:
If a user runs the application and requests the relative URL, /Home/Index, the following text will display in
the browser:
A Model class
namespace Models
{
public class SimpleModel
{
public int Value { get; set; }
}
}
The following code shows a controller that creates an instance of a model and passes it to a view:
A Controller class
public class HomeController : Controller
{
[Route("Home/Index")]
public IActionResult Index()
{
SimpleModel model = new SimpleModel() { Value = 5 };
return View(model);
}
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 05-23
@await Html.PartialAsync("_MyPartialView")
Value received in index view from model is @Model.Value
@await Html.PartialAsync("_MyPartialView")
The following code shows how a model can be used in a partial view:
If a user runs the application and requests the relative URL, /Home/Index, the following text will display in
the browser:
A Controller class
public class HomeController : Controller
{
[Route(“Home/Index”)]
public IActionResult Index()
{
ViewBag.Message = “message”;
return View();
}
}
The following code shows how a view can read the data that is passed to it by using the ViewBag
property:
The following code shows how a partial view can read the data that is passed to it by using the ViewBag
property:
If a user runs the application and requests the relative URL, /Home/Index, the following text will display in
the browser:
Demonstration Steps
You will find the steps in the section “Demonstration: How to Create and Use Partial Views“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD05_DEMO.md#demonst
ration-how-to-create-and-use-partial-views.
example, if the name of the view component class is MyViewComponent, the view can be in a folder
named Views\Shared\Components\My.
The following code shows a view component class named MyViewComponent. The name of the partial
view that will be rendered is Default:
namespace ViewComponents
{
public class MyViewComponent : ViewComponent
{
public Task<IViewComponentResult> InvokeAsync()
{
return Task.FromResult<IViewComponentResult>(View("Default"));
}
}
}
The following code shows the Default view component view. It is located in the
Views\Shared\Components\My\Default.cshtml file.
The following code shows a simple controller that calls the Index view:
A Controller class
public class HomeController : Controller
{
[Route("Home/Index")]
public IActionResult Index()
{
MCT USE ONLY. STUDENT USE PROHIBITED
05-26 Developing Views
return View();
}
}
If a user runs the application and requests the relative URL, /Home/Index, the following text will display in
the browser: “some text”.
Invoking a View Component using a Tag Helper
In addition to invoking a view component from a view by using the @Component.InvokeAsync method,
it is also possible to invoke a view component from a view by using a tag helper. To invoke a view
component by using a tag helper, you should use a vc element followed by a colon and the name of the
view component. To use a view component as a tag helper, you must use the @addTagHelper directive,
and pass to it the name of the assembly in which the tag helper is declared.
For example, in case the My view component was declared in the ViewComponentExample assembly,
you can invoke it from the Index view in the following way:
<vc:My></vc:My>
Using the vc:My element is identical to calling the @await Component.InvokeAsync("My") method.
namespace ViewComponents
{
public class MyViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(int param)
{
int id = await SomeOperationAsync(param);
return View("Default", id);
}
The following code shows how you can pass a parameter from the Index view to the view component:
If a user runs the application and requests the relative URL, /Home/Index, the following text will display in
the browser: Id: 1.
The following code shows how to pass a parameter from the Index view to the My view component by
using a tag helper:
<vc:My param=”5”></vc:My>
Demonstration Steps
You will find the steps in the section “Demonstration: How to Create and Use View Components“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD05_DEMO.md#demonst
ration-how-to-create-and-use-view-components.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 05-29
Objectives
After completing this lab, you will be able to:
• Add an MVC view to a web application.
• Use Razor to differentiate server-side code from HTML code.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD05_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD05_LAK.md.
Review Question
Question: You need to have the same block of HTML content on different webpages. Right now,
you copy and paste code from one Razor view to others. Do you have a better solution?
Best Practices
• Use Razor comments, declared with the @* *@ delimiters, throughout your Razor views to help
explain the view code to other developers in your team.
• Use the @: and <text> tags only when you think that Razor might misinterpret content as code.
Razor has sophisticated logic for distinguishing content from code, so this is rarely necessary.
When a controller tries to access a partial Place partial views in the /Views/Shared folder if
view, an exception is thrown. they need to be used by various controllers.
MCT USE ONLY. STUDENT USE PROHIBITED
05-32 Developing Views
MCT USE ONLY. STUDENT USE PROHIBITED
06-1
Module 6
Developing Models
Contents:
Module Overview 06-1
Lesson 1: Creating MVC Models 06-2
Lesson 2: Working with Forms 06-13
Lesson 3: Validating MVC Application 06-24
Lab: Developing Models 06-31
Module Review and Takeaways 06-33
Module Overview
Most web applications interact with various types of data or objects. An e-commerce application, for
example, manages products, shopping carts, customers, and orders. A social networking application might
help manage users, status updates, comments, photos, and videos. A blog is used to manage blog entries,
comments, categories, and tags. When you write a Model-View-Controller (MVC) web application, you
create an MVC model to model the data for your web application. Within this model, you create a model
class for each type of object. The model class describes the properties of each type of object and can
include business logic that matches business processes. Therefore, the model is a fundamental building-
block in an MVC application. In this module, you will learn how to create the code for models.
Objectives
After completing this module, you will be able to:
• Add a model to an MVC application and write code in it to implement the business logic.
• Use display and edit data annotations.
Lesson 1
Creating MVC Models
An MVC model is a collection of classes. When you create a model class, you define the properties and
methods that are required for the kind of object the model class describes. You need to know how to
create and describe models, and how to modify the manner in which an MVC creates model class
instances when it runs your web application. It is also important to know how controllers pass models to
views, and how views can render the data stored in a model to the browser.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe how to create MVC models and develop business logic.
• Pass models to views.
• Describe model binders.
• Add create, read, update, and delete (CRUD) operations to controllers.
Developing Models
Every website presents information about various
types of objects. To implement a functional
requirement in your web application, you need
to define model classes for these objects. If you
follow the Agile Development model or Extreme
Programming, you begin with a simple definition
of the class—perhaps its name and a few
properties. Then, you discuss with users and add
details to the planned model class including the
complete set of properties and its relationships to
other classes. When you start developing the
model, you can refer to use cases or user stories
to ensure that these model details are correct.
Note: Agile Development model and Extreme Programming are covered in Module 2,
“Designing ASP.NET Core MVC Web Applications”.
After you have fully understood the requirements for a model, you can write model classes to implement
these requirements. Programmers should create their models in a folder named Models. Because MVC
relies on convention more than configuration, we recommend adhering to the conventions.
The following lines of code illustrate how to create a model class named Photo:
Notice that the model class does not inherit from any other class. Also, notice that you have created
public properties for each property in the model and included the data type, such as int or string in the
declaration. You can create read-only properties by omitting the set; keyword.
The Photo class includes a Comments property. This is declared as a collection of Comment objects and
implements one side of the relationship between photos and comments.
The following lines of code illustrate how you can implement the Comment model class:
Notice that the Comment class includes a PhotoID property. This property stores the ID of the photo
that the user commented on, and it ties the comment to a single photo. Also, notice that the Comment
class includes a Photo property, which returns the Photo object that the comment relates to. These
properties implement the other side of the relationship between photos and comments.
The following code shows a controller that creates a model and passes it to a view:
You can use the Razor @model operator to specify the strongly typed domain model type of the view. To
access the value of a property in the domain object, use @Model.PropertyName.
The following code shows a view that receives a model of the SomeModel type and uses it to generate a
response:
@Model.Text
If a user requests the relative URL, /Home/Index, the following string displays on the screen: some text.
The following code shows a view that receives a collection of items and uses it to generate a response:
If a user requests the relative URL, /Home/Index, the following text will be displayed in the browser:
The following code shows a view that receives a model of the SomeModel type and uses it to generate
the response:
@Model.Text
If a user requests the relative URL, /Home/Index, the following string displays on the screen: some text.
MCT USE ONLY. STUDENT USE PROHIBITED
06-6 Developing Models
In such cases, you can create a dynamic view. A dynamic view does not include the @model declaration
at the top of the page. You can later choose to add the @model declaration to change the view into a
strongly typed view.
When you create dynamic views, Visual Studio does not provide much IntelliSense feedback and error
checking because it cannot check the model class properties to verify the code. In such scenarios, it is your
responsibility to ensure that you access only the properties that exist. To access a property that may or
may not exist, such as the ThirdPartyProduct.Supplier property in the preceding example, check the
property for null before you use it.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 06-7
Note: Other HTML helpers and tag helpers will be covered in Lesson 2, “Working with
Forms”.
A Model class
namespace ModelNamespace
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
A Controller class
public class PersonController : Controller
{
[Route("Person/GetName")]
public IActionResult GetName()
{
return View();
}
}
The following code shows how to use the @html.EditorForModel HTML helper in a view to display all
properties of a model:
If a user requests the relative URL, /Person/GetName, a form displays in the browser. The form contains
two text boxes, one of them represents the FirstName property of the model and the other one
represents the LastName property of the model, and a submit button. If a user enters values in the text
boxes and clicks the submit button, a postback occurs to the GetName action in the PersonController
controller.
The following code shows how to use the @Html.BeginForm HTML helper in a view to render a form
implicitly:
@using (Html.BeginForm())
{
@Html.EditorForModel()
<input type="submit" value="Submit my name" />
}
Note: The @html.BeginForm HTML helper will be covered in Lesson 2, “Working with
Forms”.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Bind Views to Model Classes“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD06_DEMO.md#demonst
ration-how-to-bind-views-and-model-classes
To understand the default model binding process, consider the following sequence of steps:
1. A web browser receives the following request: http://www.contoso.com/product/display/45.
2. This request identifies three aspects:
o The model class that interests the user. The user has requested a product.
o The operation to perform on the model class. The user has requested that the product be
displayed.
o The specific instance of the model class. The user has requested that the product with ID 45 be
displayed.
3. The request is received by the ASP.NET Core MVC runtime.
4. The ASP.NET Core MVC runtime calls an action and passes the correct parameters to it. In the
example, the ASP.NET Core MVC runtime calls the Display action in the Product controller and
passes the ID 45 as a parameter to the action, so that the right product can be displayed. To do this,
the ASP.NET Core MVC runtime uses model binders to determine how parameters are passed to
actions.
o Query Strings. If the user request includes named parameters after a question mark, you can find
these parameters in the Request.QueryString collection.
o Files. If the user request includes uploaded files, these can be used as parameters.
Notice that if there are form values and route values in the request, form values take precedence. Query
string values are only used if there are no form values and no route values available as parameters.
A Model class
namespace ModelNamespace
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
The following code shows a controller class that contains two action methods, both of them with the
name GetName. One of them is annotated with the HttpGetAttribute attribute, and the other one is
annotated with the HttpPostAttribute attribute:
[Route("Person/GetName")]
[HttpPost]
public IActionResult GetName(Person person)
{
return View("ShowName", person);
}
}
The following code shows the view that is called by the GetName() action:
@using (Html.BeginForm())
{
@Html.EditorForModel()
<input type="submit" value="Submit my name" />
}
The following code shows the view that is called by the GetName(Person person) action:
If a user requests the relative URL, /Person/GetName, the GetName() action is called. The GetName()
action calls the GetName view. The GetName view sends to the browser a form that contains two text
boxes and a submit button. When a user fills the text boxes and clicks the submit button, the ASP.NET
Core MVC runtime uses the model binder to create an instance of type Person and fills its properties
based on what the user entered in the form. Then, the ASP.NET Core MVC runtime calls the
GetName(Person person) action, passing it the instance of type Person. The GetName(Person person)
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 06-11
action calls the ShowName view. The ShowName view sends the values of the properties of the Person
model to the browser.
[HttpGet]
public IActionResult Details(int id)
{
// TODO: Add logic here
return View(person);
}
[HttpGet]
public IActionResult Create()
{
// TODO: Add logic here
return View();
}
[HttpPost]
public IActionResult Create(Person person)
{
// TODO: Add logic here
return RedirectToAction("Index");
}
[HttpGet]
public IActionResult Edit(int id)
{
// TODO: Add logic here
return View(person);
}
[HttpPost]
public IActionResult Edit(int id, Person person)
{
// TODO: Add logic here
return RedirectToAction("Index");
}
MCT USE ONLY. STUDENT USE PROHIBITED
06-12 Developing Models
[HttpGet]
public IActionResult Delete(int id)
{
// TODO: Add logic here
return View(person);
}
[HttpPost, ActionName("Delete")]
public IActionResult DeleteConfirmed(int id)
{
// TODO: Add logic here
return RedirectToAction("Index");
}
}
In the preceding example, there are two read operations: the Index method returns a collection of
people, while the Details method returns a specific person. There are two create operations: the Create()
method allows the user to enter the properties of the person to create and the Create(Person person)
method creates a person based on the properties entered by the user. There are two update operations:
the Edit(int id) method allows the user to change the properties of an existing person, while the Edit(int
id, Person person) method saves the changes made by the user to the person. There are two delete
operations accepting the same parameter id. Since the two delete operations have the same name and
parameter, and it is illegal to have two methods in c# with the same signature, the name of the second
action is DeleteConfirmed.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 06-13
Lesson 2
Working with Forms
When you create a model class, you define the properties and methods that are appropriate for the kind
of object the model class describes. Based on the class properties and methods, MVC can determine how
to render the model on a webpage.
The ASP.NET Core MVC Framework includes many HTML helper functions and tag helpers that you can
use in views. You can use helpers to render values, labels, and input controls such as text boxes. Helpers
include common logic that makes it easier for you to render HTML for MVC model class properties and
other tasks. You can also create custom helpers. You need to know the appropriate helper to use in any
scenario.
Lesson Objectives
After completing this lesson, you will be able to:
• Use the display and edit data annotations.
• Use HTML helpers and tag helpers to display a value of a model class property.
• Use HTML helpers and tag helpers to render an editor control for a model class property.
• Use various HTML helpers and tag helpers to build a user interface for a web application.
For example, property names in C# cannot contain spaces. On a rendered webpage, you may often want
to include spaces in a property label. For example, you may want to render a property called
EmailAddress with the label Email Address. To provide MVC with this information, you can use the
DisplayAttribute attribute.
Sometimes, you might want to provide additional type information for a property. For example, a
property with a name Password has a data type string. However, it is probably important to mark this
property as a password to inform the browser that it needs to show an asterisk or circle when a user types
a character. To provide MVC with this information, you can use the DataTypeAttribute attribute.
MCT USE ONLY. STUDENT USE PROHIBITED
06-14 Developing Models
The following code shows how you can annotate the properties of a model with the DisplayAttribute
and the DataTypeAttribute attributes:
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Date)]
public DateTime Birthdate { get; set; }
[Display(Name="Email Address")]
public string EmailAddress { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
A Controller example
public class PersonController : Controller
{
[Route("Person/GetDetails")]
public IActionResult GetDetails()
{
return View();
}
}
The following code illustrates how to display the properties of the model by using the
Html.EditorForModel HTML helper:
@Html.EditorForModel()
If a user requests the relative URL, /Person/GetDetails, a form displays in the browser. The form contains
the following elements:
• A label with the text “My Name” and a text box below it. Notice that although the name of the
property is Name, in the browser the text “My Name” displays because the Name property has a
DisplayAttribute attribute.
• A label with the text “Password” and a text box below it. If a user enters characters in the text box,
they appear as asterisks or circles in the browser because the Password property has a
DataTypeAttribute attribute with a Password data type.
• A label with the text “Birthdate” and a text box below it. The text box is an input HTML element with
a type=”date” attribute because the Birthdate property has a DataTypeAttribute attribute with a
Date data type.
• A label with the text “Email Address” and a text box below it. Notice that although the name of the
property is EmailAddress, in the browser the text “Email Address” displays because the
EmailAddress property has a DisplayAttribute attribute.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 06-15
• A label with the text “Description” and a text area below it because the Description property has a
DataTypeAttribute attribute with a MultilineText data type.
Note: Notice that the data annotations that are mentioned above are included in the
System.ComponentModel.DataAnnotations namespace.
MVC includes several helpers that display properties from model classes. You can use these helpers to
build views that display product details, comment details, user details, and so on. Html.DisplayNameFor
renders the name of a model class property. Html.DisplayFor renders the value of a model class
property. Both these helpers examine the definition of the property in the model class, including the data
display annotations, to ensure that they render the most appropriate HTML.
A Model class
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool ContactMe { get; set; }
}
MCT USE ONLY. STUDENT USE PROHIBITED
06-16 Developing Models
The following code shows a controller named PersonController that creates an instance of the Person
model and passes it to a view named ShowDetails:
A Controller class
public class PersonController : Controller
{
[Route("Person/ShowDetails")]
public IActionResult ShowDetails()
{
Person model = new Person() { FirstName = "James", LastName = "Smith", ContactMe
= true };
return View(model);
}
}
The following code example shows a view named ShowDetails that renders the display name of the
properties of the Person model by using the Html.DisplayNameFor HTML helper:
<h1>Person properties</h1>
@Html.DisplayNameFor(model => model.FirstName)
<br />
@Html.DisplayNameFor(model => model.LastName)
<br />
@Html.DisplayNameFor(model => model.ContactMe)
If a user requests the relative URL “/Person/ShowDetails” the following will be displayed in the browser:
Browser display
Person properties
FirstName
LastName
ContactMe
The text rendered by the Html.DisplayNameFor helper depends on the model class. If you used a
DisplayAttribute attribute to give a more descriptive name to a property, Html.DisplayNameFor will
use the value of the Name parameter. Otherwise, it will render the name of the property.
The following code shows a model class named Person that has data annotations:
If a user requests the relative URL “/Person/ShowDetails”, the following will be displayed in the browser:
Browser display
Person properties
First Name:
Last Name:
Contact me?
<h1>Person details</h1>
@Html.DisplayNameFor(model => model.FirstName)
@Html.DisplayFor(model => model.FirstName)
<br />
@Html.DisplayNameFor(model => model.LastName)
@Html.DisplayFor(model => model.LastName)
<br />
@Html.DisplayNameFor(model => model.ContactMe)
@Html.DisplayFor(model => model.ContactMe)
If a user requests the relative URL “/Person/ShowDetails”, the following will be displayed in the browser:
Browser display
Person details
First Name: James
Last Name: Smith
Contact me?
If the action passes an existing model object to the view, the Html.EditorFor helper also populates each
control with the current values of each property.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 06-19
The following code shows a model class named Person that has data annotations:
A Controller class
public class PersonController : Controller
{
[Route("Person/GetDetails")]
public IActionResult GetDetails()
{
return View();
}
}
The following code shows a view named GetDetails that uses the Html.LabelFor HTML helper and the
HTML.EditorFor HTML helper:
If a user requests the relative URL “/Person/GetDetails”, the following will be displayed in the browser:
Browser display
Get person details
First Name:
Last Name:
Contact me?☐
Note: In Module 5, “Developing Views”, the AnchorTagHelper tag helper was introduced.
MCT USE ONLY. STUDENT USE PROHIBITED
06-20 Developing Models
The LabelTagHelper tag helper is an alternative to the Html.LabelFor HTML helper. It generates a
<label> element for a property in a model and you can use it by adding an asp-for attribute to a
<label> element. The value of the generated for HTML attribute matches the name of the property in
the model class. The content of the <label> element matches the Name property of the
DisplayAttribute attribute that is specified in the model class.
The InputTagHelper tag helper is an alternative to the Html.EditorFor HTML helper. It generates an
<input> element for a property of a model and you can use it by adding an asp-for attribute to a
<input> element. The InputTagHelper tag helper adds an id and a name based on the property name
specified in the asp-for attribute. Similar to the Html.Editor HTML helper, the type of the input element
is determined based on the .NET type of the property of the model class. For example, if the .NET type of
the property is string, the type of the input element is text, while if the .NET type of the property is bool,
the type of the input element is checkbox.
The following code shows how you can rewrite the GetDetails view to use tag helpers instead of using
HTML helpers:
@model ModelNamespace.Person
If a user requests the relative URL “/Person/GetDetails” the following will be displayed in the browser:
Browser display
Get person details
First Name:
Last Name:
Contact me?☐
A Controller class
public class PersonController : Controller
{
[Route("Person/GetDetails")]
public IActionResult GetDetails()
{
return View();
}
[Route("Person/ShowDetails")]
public IActionResult ShowDetails(Person person)
{
return View(person);
}
}
The following code example shows how to render a form that sends data to the ShowDetails action in
the PersonController controller. The form uses the POST HTTP verb:
@model ModelNamespace.Person
<label asp-for="LastName"></label>
<input asp-for="LastName" />
<br />
<label asp-for="ContactMe"></label>
<input asp-for="ContactMe" />
<br />
<input type="submit" value="Submit my details" />
}
<h1>Person details</h1>
@Html.DisplayNameFor(model => model.FirstName)
@Html.DisplayFor(model => model.FirstName)
<br />
@Html.DisplayNameFor(model => model.LastName)
@Html.DisplayFor(model => model.LastName)
<br />
@Html.DisplayNameFor(model => model.ContactMe)
@Html.DisplayFor(model => model.ContactMe)
If a user requests the relative URL “/Person/GetDetails” a form displays in the browser. If a user enters his
first name and last name and clicks the submit button, the ShowDetails action of the PersonController
controller is called.
Note: The Html.BeginForm HTML helper is overloaded. For example, if you would like to
specify the HTTP method that the form uses to submit data, you can enter
Html.BeginForm("ShowDetails", "Person", FormMethod.Post)).
Using FormTagHelper
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model ModelNamespace.Person
</form>
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use Display and Edit Data Annotations“ on
the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD06_DEMO.md#demonst
ration-how-to-use-display-and-edit-data-annotations
MCT USE ONLY. STUDENT USE PROHIBITED
06-24 Developing Models
Lesson 3
Validating MVC Application
It is very important to validate user input in web applications. The validation of user input might be a
challenging task. The developer needs to write code that checks for errors made by a user when he fills a
form. ASP.NET Core MVC enables the developer to ease the process of input validation. When you create
a model class in ASP.NET Core MVC, you can annotate the class properties with attributes that determine
how to validate user input. There are several HTML helpers and tag helpers that you can use in views to
validate user input data.
Lesson Objectives
After completing this lesson, you will be able to:
• Validate user input with data annotations.
• Use HTML helpers and tag helpers to validate data entered by users.
• Use custom validation in MVC applications.
[Range(0, 150)]
public int Age { get; set; }
[Required]
[RegularExpression(".+\\@.+\\..+")]
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 06-25
[DataType(DataType.MultilineText)]
[StringLength(20)]
public string Description { get; set; }
}
The Required, Range, StringLength, and RegularExpression annotations implement input validation in
MVC. If users do not enter data that satisfies the criteria specified for each property, the view displays a
standard error message that prompts the user to enter the correct data.
In the preceding example, you can see that the user must enter a name. To specify the error message that
the user sees when data is not valid, use the ErrorMessage property on the validation data annotations.
The user must also insert an age that will be an integer number between 0 and 150. For the
EmailAddress property, the user must enter a value that matches the regular expression. The regular
expression in the example is a simple expression that requires an @ symbol and a dot. For the
Description property, the user is not allowed to insert a string that contains more than 20 characters.
[Route("Person/GetDetails")]
[HttpPost]
public IActionResult GetDetails(Person person)
{
if (ModelState.IsValid)
{
return View("ShowDetails", person);
}
else
{
return View();
}
}
}
If the data is valid, a view named ShowDetails is called. Otherwise, the GetDetails view is called again, so
that the user can correct the invalid data.
MCT USE ONLY. STUDENT USE PROHIBITED
06-26 Developing Models
When a user requests the relative URL “/Person/GetDetails” a form appears in the browser without any
error messages. However, if a user enters data that is not valid and clicks the button, the form that is
rendered from the GetDetails view will contain error messages next to the fields that were not filled
correctly by the user. For example, if a user did not specify his name, the message “Please enter a name.”
appears next to the Name text box. The reason is that the @Html.EditorForModel HTML helper renders
the error messages to the browser.
Note: The example above demonstrates server side validation. It is also possible to validate
MVC applications by using client side validation. Client side validation will be covered in Module
8, “Using Layouts, CSS and JavaScript in ASP.NET Core MVC”.
The following code example shows how you can change the GetDetails view to use the
Html.ValidationSummary helper to render a summary of validation messages:
@Html.EditorForModel()
<input type="submit" value="Submit my details" />
}
• All. Using this value will cause both property and model level validation messages to be displayed.
• ModelOnly. Using this value will cause only model level validation messages to be displayed
(excludes all property errors).
<label asp-for="Name"></label>
<input asp-for="Name" />
<span asp-validation-for="Name"></span>
<br />
<label asp-for="Age"></label>
<input asp-for="Age" />
<span asp-validation-for="Age"></span>
<br />
<label asp-for="EmailAddress"></label>
<input asp-for="EmailAddress" />
<span asp-validation-for="EmailAddress"></span>
<br />
<label asp-for="Description"></label>
<input asp-for="Description" />
<span asp-validation-for="Description"></span>
<br />
<input type="submit" value="Submit my details" />
</form>
Demonstration Steps
You will find the steps in the section “Demonstration: How to Validate User Input with Data Annotations“
on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD06_DEMO.md#demonst
ration-how-to-validate-user-input-with-data-annotations
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 06-29
After you have created a custom validation data annotation, you can use it to annotate a property in your
model, as the following lines of code illustrate:
[Range(0, 150)]
public int Age { get; set; }
[Required]
[RegularExpression(".+\\@.+\\..+")]
public string EmailAddress { get; set; }
[DataType(DataType.MultilineText)]
[StringLength(20)]
public string Description { get; set; }
}
MCT USE ONLY. STUDENT USE PROHIBITED
06-30 Developing Models
Notice that the IsValid method of the ValidationAttribute class is overloaded. In case you need an
access to the whole model class, you can override another version of the IsValid method, which gets as a
second parameter an object of type ValidationContext.
The following lines of code illustrate how you can access the model class in the IsValid method:
You can use the ValidationContext parameter to use a service that was registered in the
ConfigureServices method of the Startup class.
Note: Services were covered in Module 3, “Configure Middleware and Services in ASP.NET
Core”.
The following lines of code illustrate how you can use a service in the IsValid method:
Demonstration Steps
You will find the steps in the section “Demonstration: How to Add Custom Validations“ on the following
page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD06_DEMO.md#demonst
ration-how-to-add-custom-validations.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 06-31
Objectives
After completing this lab, you will be able to:
• Add new models to the application, and add properties to the model.
• Add GET and POST actions that accept the new model information.
• Use display and edit data annotations in the MVC model to assign property attributes to views and
controllers.
• Use Display, Editor and Form Helpers inside the views.
• Use validation data annotations in the MVC model to assign property attributes to views and
controllers.
• Add custom validation to the application.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD06_LAB_MANUAL.md
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD06_LAK.md
Review Question
Question: You want to display the name of the Comment.Subject property in an MVC view that
renders an edit form for comments. You want the label Edit Subject to appear to the left of a
text box so that a user can edit the Subject value. Which HTML helper should you use to render
the field name?
Best Practice
You should put the models of an ASP.NET Core MVC application in a folder named Models.
Module 7
Using Entity Framework Core in ASP.NET Core
Contents:
Module Overview 07-1
Lesson 1: Introduction to Entity Framework Core 07-2
Lesson 2: Working with Entity Framework Core 07-10
Lesson 3: Using Entity Framework Core to Connect to Microsoft SQL Server 07-22
Lab: Using Entity Framework Core in ASP.NET Core 07-35
Module Review and Takeaways 07-37
Module Overview
Web applications often use information and they usually require a data store for that information. By
rendering webpages that use data from a data store, you can create a web application that changes
continually in response to user input, administrative actions, and publishing events. The data store is
usually a database, but other types of data stores are occasionally used. In Model-View-Controller (MVC)
applications, you can create a model that implements data access logic and business logic. Alternatively,
you can separate business logic from data access logic by using a repository. A repository is a class that a
controller can call to read data from a data store and to write data to a data store. When you write an
ASP.NET application you can use the Entity Framework Core (EF Core) and Language Integrated Query
(LINQ) technologies, which make data access code very quick to write and simple to understand. In this
module, you will see how to build a database-driven website in MVC.
Objectives
After completing this module, you will be able to:
Lesson 1
Introduction to Entity Framework Core
EF Core is an object-relational mapping (ORM) framework that can be used when developing ASP.NET
Core applications. It allows developers to work with databases and use the data in ASP.NET Core
applications. Moreover, it enables developers to be data-oriented without the need to concentrate on
modeling the entities. EF Core is a light-weight version of Entity Framework. In addition, it is also
extensible and cross-platform.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe what is ADO.NET and its uses.
• Explain what is ORM.
• Explain the advantages of ORM.
• Describe what is Entity Framework.
• Explain the differences between Entity Framework 6 and EF Core.
• Explain what is a database provider.
• Add a database provider to your application.
You can also find third-party data providers online and you can implement your own data provider.
return View();
}
}
The following code example demonstrates how to connect to a Microsoft Azure SQL database:
return View();
}
}
Note: Microsoft Azure will be covered in Module 14, “Hosting and Deployment”.
MCT USE ONLY. STUDENT USE PROHIBITED
07-4 Using Entity Framework Core in ASP.NET Core
The following code example demonstrates how to query a database with a data reader in a controller:
return View(cities);
}
}
When you use a data reader, you can access only one database record at a time, as shown in the
preceding example. If you need multiple records at once, you must store them as you move to the next
record. Although this seems like a major inconvenience, data readers are very efficient in terms of memory
utilization because they do not require the entire result set to be fetched into memory.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-5
return View(cities);
}
}
You can use the DataSet objects to hold information from more than one table at one time and to
maintain relationships between the tables.
MCT USE ONLY. STUDENT USE PROHIBITED
07-6 Using Entity Framework Core in ASP.NET Core
Code First
In this approach, you do not use a .edmx file to design your model, and you do not rely on the Visual
Studio designer. The domain model is simply a set of classes with properties that you provide. In the Code
First approach, Entity Framework scans your domain classes and their properties and tries to map them to
the database.
You can use the Code First approach both with new databases and with existing ones. If you do not have
a database, the default behavior of the Code First approach is to create the database the first time you
run your application. If your database already exists, Entity Framework will connect to it and use the
defined mappings between your model classes and the existing database tables.
Database Providers
EF Core is a layer between your code and a
database. It is used to connect your code to the
database. A database provider is a library which
is used by EF Core to connect to a specific
database. Database providers are distributed as
NuGet packages. If you want to use a database
provider in your application, you can install the
corresponding NuGet package.
Note: Connecting to a SQL Server database by using the Microsoft SQL Provider will be
covered in Lesson 3, “Using Entity Framework Core to Connect to Microsoft SQL Server”.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-9
Note: Connecting to an SQLite database by using the SQLite provider will be covered in
Lesson 2, “Working with Entity Framework Core”.
Other Providers
There are plenty of other database providers. Using these providers, it is possible to connect to various
database engines such as MySQL, Maria DB and Db2. However, notice that while some database providers
are maintained by the EF Core Project (Microsoft) vendor, others database providers might be maintained
by other vendors. Before using a database provider, ensure that it meets your requirement.
Note: The dotnet tool will be covered in Module 14, “Hosting and Deployment”.
This is the dotnet command you can use to add a database provider:
This is the NuGet Package Manager Console command you can use to add a database provider:
Lesson 2
Working with Entity Framework Core
You can use EF Core in your ASP.NET Core applications to connect to a database. To connect to a
database by using EF Core you can use an Entity Framework context. An Entity Framework context is a
class that inherits from the DbContext class. In this lesson, you will learn how to configure an Entity
Framework context to connect to a SQLite database.
The Entity Framework context can be used to retrieve data from a database. To retrieve data from a
database by using an Entity Framework context, you can use LINQ to Entities. Furthermore, you can also
load related data by using EF Core. In this lesson, you will see several techniques to load related data by
using EF Core, including the following ORM patterns: explicit loading, eager loading, and lazy loading.
You can also use the Entity Framework context to manipulate data in the database. In this lesson, you will
see how to use the Entity Framework context to add new records to a database, update an existing record
in a database, and delete a record from a database.
Lesson Objectives
After completing this lesson, you will be able to:
• Create an entity.
• Explain what is an Entity Framework context.
• Connect EF Core to a SQLite database.
• Retrieve data from a database by using EF Core.
• Explain the different techniques of loading related data.
• Manipulate data in a database by using EF Core.
An Entity Class
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
One of the most popular databases that you can access when using EF Core is SQLite. SQLite is a
serverless database, which means that you don’t need to have a separate server to use it. One of the big
advantages of SQLite, when compared to other databases, is that it is very easy to configure.
You can use the SQLite database to store data in a file. In this lesson, Entity Framework will be configured
to connect to a SQLite database, and the data in the database will be stored in a file on disk.
Note: Configuring EF Core to connect to a SQL Server will be covered in Lesson 3, “Using
Entity Framework Core to Connect to Microsoft SQL Server”.
To use an Entity Framework context in an ASP.NET Core application, you need to register a service by
using the AddDbContext<> method, passing it the Entity Framework context class type as a generic type
parameter. You should register the service in the ConfigureServices method of the Startup class.
The following code shows how to configure HrContext to connect to a SQLite database which will be
stored in a file named example.db:
The following example demonstrates how the Entity Framework context can receive the options specified
in the ConfigureServices method:
The following code shows a controller that gets a HrContext instance by using the dependency injection
mechanism:
app.UseMvc();
}
Data Seeding
You can use a data seeding to populate the database with sample data when a database is created. When
you call the EnsureCreated method, a new database will be created and initialized with the sample data.
Notice that in case a database already exists when EnsureCreated is called, the sample data won’t be
added to the database.
The following code demonstrates how to seed data by using the OnModelCreating method of the
HrContext class:
modelBuilder.Entity<Person>().HasData(
new { PersonId = 1, FirstName = "James", LastName = "Smith"},
new { PersonId = 2, FirstName = "Arthur", LastName = "Adams"}
);
}
}
Note: In addition to seeding data by using the EnsureCreated method, you can also seed
data when creating a new database by using migrations. Migrations will be covered in Lesson 3,
“Using Entity Framework Core to Connect to Microsoft SQL Server”.
Navigation Properties
In EF Core you can link an entity to other entities using by navigation properties. When an entity is related
to another entity, you should add a navigation property to represent the association. When the
multiplicity of the association is one or zero-or-one, the navigation property is represented by a reference
object. When the multiplicity of the association is many, the navigation property is represented by a
collection.
The following code example shows an entity named Country. Since the Country entity participates in a
one-to-many relationship with the City entity, it contains a navigation property named Cities of type
List<City>. Similarly, since the Country entity participates in a one-to-many relationship with the Person
entity, it contains a navigation property named People of type List<Person>:
The following code example shows an entity named City. Since the City entity participates in a one-to-
many relationship with the Person entity, it contains a navigation property named People of type
List<Person>. Since each city belongs to exactly one country, the City entity contains a navigation
property named Country. The CountryId property represents a foreign key:
The following code example shows an entity named Person with navigation properties to the Country
and City entities:
The following example shows an Entity Framework context that contains collections of the entities:
Explicit Loading
To load related entities by using the explicit loading ORM pattern, you should use the Entry method of
the Entity Framework context class.
The following code example demonstrates how to load a city with its related entities by using explicit
Loading:
_context.Entry(city)
.Collection(c => c.People)
.Load();
_context.Entry(city)
.Reference(c => c.Country)
.Load();
return View(city);
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-17
In the preceding code, when the city variable is initialized, its People property is null. Only after the first
call to the Load method is completed does the People property contain a list of people who live in the
city. Similarly, when the city variable is initialized, its Country property is null. Only after the second call
to the Load method is completed does the Country property contain the country to which the city
belongs.
You can also use the explicit loading ORM pattern in conjunction with LINQ. To do so, you should first call
the Query method, and then you can call the LINQ methods such as Count and Where.
The following code example demonstrates how to retrieve the number of people that live in a city:
Eager Loading
In addition to loading related data by using the explicit loading ORM pattern, you can also choose to load
related data by using the eager loading ORM pattern. However, while loading related data by using the
explicit loading ORM pattern is executed explicitly after the original query is completed, when the eager
loading ORM pattern is used the related data is loaded as part of the original query.
To load related entities by using the explicit loading ORM pattern you need to use the Include method.
The Include method specifies related entities to be included in the query results.
The following code example demonstrates how you can use eager loading to load cities with people that
live in them from the database. Since the Include method is used, the People property of each city in the
cities variable contains the people which live in the city:
In case you need to include related data from multiple relationships, you can use the Include method
several times in the same query.
The following code example demonstrates how to load cities with related entities from the database. The
People property for each city in the cities variable contains the people that live in the city, and the
Country property of each city in the cities variable contains the country to which the city belongs:
If you need to include more levels of related data, you can use the ThenInclude method. The
ThenInclude method can be used to drill down through the relationships.
MCT USE ONLY. STUDENT USE PROHIBITED
07-18 Using Entity Framework Core in ASP.NET Core
The following code example demonstrates how to load all countries, their related cities, and the people
that live in each city:
Lazy Loading
The lazy loading ORM pattern can be used when you want to load related data from the database as you
access the navigation property.
In case you want that EF Core will use lazy loading to load a navigation property, you should change the
navigation property to be overridden. To change a navigation property to be overridden you can use the
virtual keyword.
The following code example demonstrates how the Country, City and People entities can be changed so
their navigation properties will be overridden:
If you want to use the lazy loading ORM pattern, in addition to changing the navigation properties to be
overridden, you should also turn on the creation of lazy-loading proxies. To turn on the creation of lazy-
loading proxies, you can call the UseLazyLoadingProxies method.
The following code example demonstrates how to turn on the creation of lazy-loading proxies for the
DemographyContext class:
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-19
The following code example demonstrates how you can retrieve the number of people that live in a city,
and to which country the city belongs:
An Entity
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
The following code example shows how to add a new entity to a database by using the Entity Framework
context object:
Adding an Entity
public IActionResult Create()
{
_context.Add(new Person() { FirstName = "Nathan ", LastName = "Owen" });
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
Deleting Entities
To delete an entity from the database, you can use the Entity Framework context object. When you delete
an entity from a database, the context marks the change tracking status of the entity as Deleted. When
you call the SaveChanges method, the Entity Framework context object deletes the entity from the
database.
The following code example shows how to delete an entity from a database by using the Entity
Framework context object:
Deleting an Entity
public IActionResult Delete(int id)
{
var person = _context.People.SingleOrDefault(m => m.PersonId == id);
_context.People.Remove(person);
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
Updating Entities
To update an entity in the database, you can use the Entity Framework context object. When you update
an entity, the context marks the change tracking status of the entity as Modified. When you call the
SaveChanges method, the Entity Framework context object updates the entity in the database.
The following code example shows how to retrieve and update an entity by using the Entity Framework
context object:
Updating an Entity
public IActionResult Edit(int id)
{
var person = _context.People.SingleOrDefault(m => m.PersonId == id);
person.FirstName = "Brandon";
_context.Update(person);
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-21
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use Entity Framework Core“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD07_DEMO.md#lesson-2-
working-with-entity-framework-core.
MCT USE ONLY. STUDENT USE PROHIBITED
07-22 Using Entity Framework Core in ASP.NET Core
Lesson 3
Using Entity Framework Core to Connect to Microsoft SQL
Server
You can use an Entity Framework context to connect to different databases. In the previous lesson, you
saw how to use an Entity Framework context to connect to the SQLite database. In this lesson, you will see
how to use an Entity Framework context to connect to a SQL Server database. The database to which the
Entity Framework context is mapped is determined by a connection string. While you can store the
connection string as a hard-coded string in your code, a better approach is to store the connection string
in a configuration file.
When working with Entity Framework context and the entities it contains, you might need to update them
from time to time. Changing the schema of the context or the entities might cause them not to be in sync
with the tables and columns in the database. To ensure that the context and entities are in sync with the
tables and columns in the database, you can use Migrations.
Lesson Objectives
After completing this lesson, you will be able to:
• Connect an ASP.NET Core application to SQL Server by using EF Core.
• Use configuration in ASP.NET Core applications.
• Use a configuration file to store a connection string.
• Explain and use the Repository Pattern.
• Use migrations.
An Entity Class
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-23
The following code example demonstrates how to configure HrContext to connect to a SQL Server
database:
"Server=(localdb)\\MSSQLLocalDB;Database=HumanResources;Trusted_Connection=True;";
services.AddDbContext<HrContext>(
options => options.UseSqlServer(connectionString));
services.AddMvc();
}
The UseSqlServer method configures the Entity Framework context to connect to a SQL Server database.
When using the connection string specified in the example above, the Entity Framework context connects
to a database named HumanResources in the instance LocalDB\MSSQLLocalDB.
Note: LocalDB is an extension of SQL Express that offers an easy way to create multiple
database instances. Since it runs in user mode and starts on demand, it is easy to configure it.
When the HumanResources database is created, it contains a table named Candidates. The reason for
this is that the HrContext class contains a property named Candidates of type DbSet<Person>. The
DbSet<> properties of an Entity Framework context are mapped to tables in the database.
MCT USE ONLY. STUDENT USE PROHIBITED
07-24 Using Entity Framework Core in ASP.NET Core
Note: The PersonId column is a primary key column in the database because the PersonId
property is the entity key of the Person entity. By convention, the entity key of an entity in EF
Core is either a property named Id or a property named <type name>Id.
Configuration in EF Core
EF Core uses conventions to build a database based on the content of the Entity Framework context and
the entities which belong to the Entity Framework context. In case you find that some of those
conventions are not suitable for your needs, you can use configuration to override the conventions.
One way to specify configuration in EF Core is by using Fluent API. When you use Fluent API, you write
the configuration in the OnModelCreating method of the Entity Framework context class.
For example, in EF Core you can use Fluent API to determine the name of a table that is created in a
database. To determine the name of the table in the database you can use the ToTable method,
The following code example demonstrates how to create a table named People in the database which will
be mapped to the Candidates property of the HrContext class:
In addition to Fluent API, you can also specify the configuration in EF Core by using another method
called data annotations. However, the data annotations method is quite different from the Fluent API
method. While in the Fluent API method the configuration is specified in the OnModelCreating method
of the Entity Framework context class, in the data annotations method the configuration in specified by
using attributes in the entities.
The following code example demonstrates how to use data annotation to configure the Person entity:
[Required]
[StringLength(20)]
public string FirstName { get; set; }
[NotMapped]
public string LastName { get; set; }
}
When EF Core uses the Person entity above to create the table in the database, the table is created with
two columns:
• MyId (PK, int, not null).
• FirstName (nvarchar(20), not null).
The MyId column in the database is a primary key column since the MyId property is the key of the
Person entity. The MyId property is the key of the Person entity because it is annotated with the Key
attribute.
The FirstName column in the database is not null since the FirstName property of the Person entity is
annotated with the Required attribute. The data type of the FirstName column is nvarchar(20) since the
maximum length of the FirstName property is configured to 20 by using the StringLength attribute.
The LastName property is annotated with the NotMapped attribute. Therefore, the LastName property
is not mapped to a column in the database.
JSON Configuration
JSON is a text format which can be used for several purposes, such as transmitting data between
applications and storing data in a file. One of the biggest advantages of JSON is that it is very easy for
people to read and write JSON. JSON can also be parsed and generated easily by applications.
MCT USE ONLY. STUDENT USE PROHIBITED
07-26 Using Entity Framework Core in ASP.NET Core
The following code example shows the content of a JSON file named config.json:
To use a JSON configuration file in ASP.NET Core, you should add the JSON configuration provider. To
add the JSON configuration provider, you can call the AddJsonFile method on an object that implements
the IConfigurationBuilder interface. The AddJsonFile method gets the path to the configuration file as
a parameter. Then you can call the Build method on the object, which implements the
IConfigurationBuilder interface, to build an object that implements the IConfiguration instance with
settings from the set of sources registered in the IConfigurationBuilder.Sources property. Finally, you
can call the UseConfiguration method, passing it the object which implements the IConfiguration
instance as a parameter to use the given configuration settings on the web host.
The following code example demonstrates how to use the JSON configuration provider to load the values
from the config.json file:
return WebHost.CreateDefaultBuilder(args)
.UseConfiguration(configuration)
.UseStartup<Startup>();
}
}
To load a value from the configuration file, you can use an indexer of an object which implements the
IConfiguration interface and pass to it the name of the name-value pair as a parameter.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-27
The following code example demonstrates how to retrieve a value from the config.json file. The value of
the value variable will be Mark:
If the name of the name-value pair is hierarchical, you should use a colon to separate the sections of the
name to retrieve the value.
The following code example demonstrates how to retrieve a value from a hierarchical name. The value of
the value variable will be Miami:
XML Configuration
In addition to using JSON configuration files in ASP.NET Core, it is also possible to use XML configuration
files in ASP.NET Core. To use an XML configuration file in ASP.NET Core, you should add the XML
configuration provider. To add the XML configuration provider, you can call the AddXmlFile method on
an object which implements the IConfigurationBuilder interface.
You can configure an ASP.NET Core application to read parameters from multiple configuration files. To
do that you should add several configuration providers. For example, in case you want to read parameters
from both JSON and XML files, you should call both AddJsonFile and AddXmlFile on an object which
implements the IConfigurationBuilder interface. Notice that in case you read data from multiple sources,
the order in which the data providers are added matter. In case the same name exists in multiple sources,
only the value from the last source will be returned. Values from other sources will be ignored.
MCT USE ONLY. STUDENT USE PROHIBITED
07-28 Using Entity Framework Core in ASP.NET Core
The following code example shows the content of an XML file named config.xml:
The following code example demonstrates how to use the XML configuration provider to load the values
from the config.xml file. Notice that the XML configuration provider is added after the JSON configuration
provider:
return WebHost.CreateDefaultBuilder(args)
.UseConfiguration(configuration)
.UseStartup<Startup>();
}
}
The following code example demonstrates how to retrieve values from the config.json and config.xml
files:
In the code example above, the value of the age variable will be 23. This value is read from the
config.xml file. The value of the lastName variable will be Smith. This value is read from the config.json
file. The value of the firstName variable will be James. This value is read from the config.xml file.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-29
Note: The firstName name appears in both the config.json and config.xml files. The
reason that the value returned is the value from the config.xml file is because the XML
configuration provider is added after the JSON configuration provider.
The following code example demonstrates how to read the connection string from the configuration file
by using an indexer of IConfiguration. The connection string is then used to connect the Entity
Framework context to a database named HumanResources in the LocalDB\MSSQLLocalDB instance:
To read a connection string from a configuration file, you can replace the call to the indexer of
IConfiguration with a call to the GetConnectionString method. the following code example
demonstrates how to use the GetConnectionString method to read the connection string from the
configuration file:
A Repository Interface
public interface IRepository
{
IEnumerable<Person> GetPeople();
void InsertPerson();
void DeletePerson();
void UpdatePerson();
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-31
A Repository Class
public class MyRepository : IRepository
{
private HrContext _context;
The following code example demonstrates how to register the repository in the ConfigureServices
method in the Startup.cs file:
The following code example demonstrates how a controller can use the repository:
[Route("Hr/Index")]
public IActionResult Index()
{
var list = _repository.GetPeople();
return View(list);
}
[Route("Hr/Insert")]
public IActionResult Insert()
{
_repository.InsertPerson();
return RedirectToAction("Index");
}
[Route("Hr/Delete")]
public IActionResult Delete()
{
_repository.DeletePerson();
return RedirectToAction("Index");
}
[Route("Hr/Update")]
public IActionResult Update()
{
_repository.UpdatePerson();
return RedirectToAction("Index");
}
}
Demonstration Steps
You will find the steps in the section “Demonstration: How to Apply the Repository Pattern“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD07_DEMO.md#lesson-3-
using-entity-framework-core-to-connect-to-microsoft-sql-server.
Using Migrations
Working with an application that interacts with a
database requires you to create models and
sometimes, you might find yourself changing or
upgrading those models according to the
application needs. When working with EF Core,
your database is relying on the models you have
created. To reflect the changes in the models to
the database, you can use migrations. Using
migrations, you will be able to create a database,
upgrade it and manipulate it according to your
application models.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-33
If you created your database by using an Entity Framework context, and you later want to change
something in your domain model classes e.g. your schemas, EF Core will not update the database
automatically. You might encounter exceptions while running queries or saving your changes to the
database. You can use migrations to update the database schema automatically to match the changes
you made in your classes without having to recreate the database.
With migrations, you define the initial state of your classes and your database. After you change your
classes and execute the migrations in design time, the set of changes you performed over your classes is
translated to the required migration steps for the database, and then those steps are generated as
database instructions in the code. You can apply the changes to the database in design-time, before
deploying the version of the application. Alternatively, you can have the application execute the migration
code after it starts.
The following code example shows which command you should run to apply the migration to create the
schema of the database:
When you change an entity, the database and the code are not in sync anymore. To sync the code and
the database, you should add another migration.
The following code example demonstrates a change in the Person entity. A new property named Age is
added to the Person entity. Therefore, the database and code are not synced anymore:
To apply the AddAge migration to the database, use the following command:
After applying the AddAge migration, a new column named Age is added to the database.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 07-35
Objectives
After completing this lab, you will be able to:
• Add new models to the application and add properties to the models.
• Add a class that derives from the DbContext class.
• Use a repository to the application to connect to the database.
• Use Entity Framework Core to retrieve and store data.
• Use Migrations to create a database and to update the schema of the database.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD07_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD07_LAK.md.
During development properties of entities might be changed. As a result, the database might not be in
sync with the code anymore. To solve this, you can use migrations. In this module, you learned what are
migrations and how you can use migrations to save the code and the database in sync.
Review Questions
Question: Why should you use EF Core instead of directly manipulating the database by using
SQL statements in ADO.NET?
Question: You want to connect your application to a SQL Server database. How can you do it?
Best Practice
It is recommended to store connection strings in a configuration file instead of storing them as hard-
coded strings.
Module 8
Using Layouts, CSS and JavaScript in ASP.NET Core MVC
Contents:
Module Overview 08-1
Lesson 1: Using Layouts 08-2
Lesson 2: Using CSS and JavaScript 08-8
Lesson 3: Using jQuery 08-19
Lab: Using Layouts, CSS and JavaScript in ASP.NET Core MVC 08-29
Module Review and Takeaways 08-31
Module Overview
While building web applications, you should apply a consistent look and feel to the application. You
should include consistent header and footer sections in all the views. Microsoft ASP.NET Core MVC
includes features such as cascading style sheets (CSS) styles and layouts that enhance the appearance and
usability of your web application.
In ASP.NET Core MVC, you can create interactive HTML elements by using JavaScript. You need to know
how to use JavaScript in your web application. To simplify adding JavaScript to your web application, you
need to know how to use libraries such as jQuery.
Objectives
After completing this module, you will be able to:
• Apply a consistent layout to ASP.NET Core MVC applications.
• Add JavaScript code to your web application.
• Use the jQuery library in your web application.
MCT USE ONLY. STUDENT USE PROHIBITED
08-2 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
Lesson 1
Using Layouts
You need to build multiple views to support the features of your application, such as creating an order
and querying order history. Some of these views can have similar parts. You can put these parts into the
content of the views. However, this can result in maintenance issues. This is because, whenever you have
to update the parts, you have to update each view.
To resolve these maintenance issues, you can build a common module or a shared view. A shared view
that helps to store the application logic is called a layout. ASP.NET Core MVC includes features that help
simplify the process of creating and using layouts. You can further simplify the application management
process by using the _ViewStart file that helps you to apply a layout to each view, instead of individually
editing each view.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe layouts.
• Describe how to use layouts.
• Describe the _ViewStart file.
• Describe how to use sections in a layout.
Creating a Layout
When you create layouts, you should store the
layout files in the \Views\Shared folder of the
project. The \Views\Shared folder is the default
location where you can store common view files
or templates.
The following example illustrates a layout named
_Layout.html:
A Layout view
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>
In the preceding example, the @RenderBody method indicates to the rendering engine where the
content of the view should be placed.
The layout and view share the same ViewBag. You can use the ViewBag property to pass information
between a view and a layout. To pass information, you can add a property to the ViewBag property in
the View file and use the same property in the layout file. Properties help you control the content in the
layout to dynamically render webpages from the code in the view file. For example, consider that a
template uses the ViewBag.Title property to render the <title> content in the view. This property helps
you to not only define the Title property of the ViewBag property in the view but also to retrieve the
property in the layout. This retrieval is possible because the code in the view file runs before the layout.
A Controller class
public class HomeController : Controller
{
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
}
MCT USE ONLY. STUDENT USE PROHIBITED
08-4 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
The following code shows a view named Index that has a link to the _Layout layout:
<h2>Index</h2>
If a user requests the relative URL /Home/Index, the following response will be sent to the browser:
You can use the ViewBag.Title property to pass the page title information from the view to the layout.
You can define other properties along with the ViewBag property, such as <meta> elements in the
<head> section and enable them to pass information to the layout.
The _ViewStart.cshtml File
Usually, you have the same layout across an entire web application. You can define the layout for an
application by using the _ViewStart.cshml file. During run time, the code in the _ViewStart.cshtml file
runs before all the other views in the web application. Therefore, you can place all common application
logic in the _ViewStart.cshtml file.
To use the _ViewStart.cshtml file, you should add it to the \Views folder of your project. The following
code illustrates the content of the _ViewStart.cshtml file:
If the web application contains the _ViewStart,cshtml file shown above, you don’t have to include the
link to the layout in the Index view.
The following code shows the Index view, without the link to the layout:
<h2>Index</h2>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-5
If a user requests the relative URL /Home/Index, the following response will be sent to the browser:
The section1 parameter in the RenderSection method specifies the name of the section.
The following code shows a simple controller, which calls a view that has a link to a layout:
A Controller class
public class HomeController : Controller
{
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
}
MCT USE ONLY. STUDENT USE PROHIBITED
08-6 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
The following code shows a view named Index that has a link to the _Layout layout. It demonstrates how
to use the @section directive:
@section section1
{
<div>This is section1 content</div>
}
@section section2
{
<div>This is section2 content</div>
}
If a user requests the relative URL /Home/Index, the following text will be displayed in the browser:
@section section1
{
<div>This is section1 content</div>
}
@*@section section2
{
<div>This is section2 content</div>
}*@
If a user requests the relative URL /Home/Index, an exception will occur. The reason is that although the
section2 section is declared as required in the layout, there is no corresponding section in the view.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-7
The following code shows how to set the section2 section as optional in a layout:
If a user requests the relative URL /Home/Index, the following text will display in the browser:
Demonstration Steps
You will find the steps in the section “Demonstration: How to Create a Layout and Link it to a View“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD08_DEMO.md#demonst
ration-how-to-create-a-layout-and-link-it-to-a-view.
MCT USE ONLY. STUDENT USE PROHIBITED
08-8 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
Lesson 2
Using CSS and JavaScript
While HTML defines the structure and context of a web page, CSS defines how it should appear in a
browser. You need to know how to import styles into a web application to ensure consistency in the
appearance of the application. It is also important to know how to add a link to an external CSS file from a
layout.
You can create interactive HTML elements in your web application by using JavaScript. In ASP.NET Core
you can add these interactive elements to views and layouts. It is important to know how to add
JavaScript code to your web application.
Developing the CSS and JavaScript of your web application from scratch is not always the best option.
There are many external libraries that you can use in a web application to improve its quality and to
shorten the development time. In this lesson, you will see several options to add external libraries to an
ASP.NET Core application.
Lesson Objectives
After completing this lesson, you will be able to:
• Import styles into an ASP.NET Core MVC web application.
• Apply a consistent look and feel to an MVC web application.
• Describe how to add JavaScript files to an MVC web application.
• Describe how to use external libraries in ASP.NET Core MVC web applications.
Importing Styles
Cascading Style Sheets (CSS) is an industry
standard for applying styles to HTML pages. After
creating CSS styles, you should import these
styles into the web application. Different
methods of applying CSS to a webpage are
available. These methods include external CSS
file, inline CSS, and CSS code block in HTML.
Developers usually use an external CSS file
because this file is shared across multiple pages
and it helps to apply a consistent style across the
application. It is common to add a link to the
external CSS file from a layout. Therefore, after
importing the CSS file into the web application, you need to modify the layout of the web application, so
that you can use the CSS styles that you imported. You can modify the layout of a web application by
using the <link> element.
The following example shows how to use the <link> element:
</head>
<body>
<div>
@RenderSection("MenuBar")
</div>
<div>
@RenderBody()
</div>
</body>
</html>
Note: The style.css file should be located in the wwwroot folder. You should also add the
UseStaticFiles middleware to the Configure method of the Startup class. Working with static
files is covered in Module 3, “Configuring Middleware and Services in ASP.NET Core”.
CSS selectors help browsers to determine how the CSS styles should be applied. You can use various
selectors, such as class and id selectors to apply styles to HTML elements.
<h2>Details</h2>
@section MenuBar {
<p class=”menu”> this is menu</p>
}
The following CSS snippet shows how to create the class selector:
CSS id Selector
You can use the CSS id selector to specify a style for any unique element in your HTML code. To apply the
id selector to an HTML element, you need to add the id attribute and a unique name to the HTML
element. You can use the #<id> syntax to add the style in the CSS file.
MCT USE ONLY. STUDENT USE PROHIBITED
08-10 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
<h2>Details</h2>
@section MenuBar {
<p id="leftmenu"> this is menu</p>
}
The following CSS snippet shows how to create the CSS id selector:
Creating an id selector
#leftmenu {
font-size:16px;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script>
function helloWorld() {
alert('Hello World');
}
</script>
</head>
<body>
...
</body>
</html>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-11
If you have multiple views in a web application, you need to add the JavaScript code for each view. You
cannot simultaneously add JavaScript code for multiple views. Therefore, you can define the JavaScript
code in a JavaScript file (.js file). Then, you can reference the JavaScript file in multiple views. This enables
you to maintain a single JavaScript file to edit the JavaScript code for multiple views. You can also have a
reference to multiple JavaScript code files from a single view.
The following example shows the content of a JavaScript file that contains a function:
Note: The example.js file should be located in the wwwroot folder. You should also add
the UseStaticFiles middleware to the Configure method of the Startup class. Working with
static files is covered in Module 3, “Configuring Middleware and Services in ASP.NET Core”.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script src="~/example.js"></script>
</head>
<body>
...
</body>
</html>
Note: You can create a Scripts folder in the wwwroot folder of your MVC project, and
then save all JavaScript files in the Scripts folder.
<body>
@RenderBody()
</body>
</html>
If you want to avoid calling the JavaScript function directly, you can use the onclick JavaScript event to
trigger JavaScript functions. The onclick event initiates the JavaScript function when you click the
corresponding HTML element. JavaScript functions that are attached to the document object model
(DOM) events are called event handlers.
The following code shows how to add the helloWorld event handler to the button’s onclick event:
<h2>Index</h2>
<input type="button" value="Hello" onclick="helloWorld();" />
The following code example demonstrates how to use some of the built-in JavaScript functions to
perform mathematic calculations:
Note: jQuery and its syntax will be covered in greater detail in Lesson 3, "Using jQuery".
To utilize jQuery and other libraries in an ASP.NET Core application, you first need to import them into
the project. There are many ways to import a library into the application, but some are more
recommended than others.
After downloading the files, in order to load them in the application, you should add them to the static
files folder which by default is the wwwroot folder.
Best Practice: You should create a scripts folder in the wwwroot folder of your ASP.NET
Core project and then store all the JavaScript libraries there. Similarly, you should create a styles
folder in the wwwroot folder of your ASP.NET Core project and then store all the CSS libraries
there.
MCT USE ONLY. STUDENT USE PROHIBITED
08-14 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
The following code example demonstrates how to link a layout to the jQuery library:
One of the disadvantages of serving files from a local folder is that the loading time of those files is long.
To make the loading time shorter you can serve the files by using a Content Delivery Networks (CDN)
instead of serving them from a local folder.
Microsoft has a dedicated CDN called Microsoft Ajax CDN that hosts some popular libraries, such as
jQuery and Bootstrap.
Note: Microsoft does not own the license of the libraries but only hosts the libraries for
developers.
You can often reduce the loading time of your web application, by using the libraries hosted on Microsoft
Ajax CDN. Web browsers can cache these libraries on a local system.
The following code example demonstrates how to link a layout to the jQuery library using a CDN:
</html>
Note: In the preceding code example, the file has the .min.js extension, which means this
file is minified and is more lightweight than the file with the .js extension. Bundling and
minification are covered below and in Module 9, "Client-Side Development".
Best Practice: Applications in production should not entirely depend on CDN assets
because they might be unavailable. Applications should test the CDN asset referenced and when
it is not available, to use a fallback method such as serving a local file.
• npm.
NuGet
NuGet is a free and open source package manager which Microsoft has created for the Microsoft
development platform and it is part of Visual Studio. In past versions of ASP.NET, it was recommended to
use the NuGet package manager to install client-side libraries such as jQuery and Bootstrap. In ASP.NET
Core this has changed and the NuGet package manager should be used mainly to install server-side
packages.
Yarn
Yarn is a very fast and secure package and dependency manager. It allows to use and share code with
other developers. Yarn checks the integrity of the packages being installed before executing the code.
Webpack
Webpack is a module bundler for JavaScript applications and serves as a task runner. It processes your
application and creates a dependency graph. This graph maps exactly what are the modules that your
application needs. Webpack creates bundles including the project’s dependencies.
Bower
Bower is a lightweight package manager that helps to manage frameworks, libraries, and assets on the
web. It does not bundle the packages or perform tasks as Webpack but only installs the packages your
application needs.
Note: In this course npm will be used to install and manage client-side libraries.
MCT USE ONLY. STUDENT USE PROHIBITED
08-16 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
Additional Reading: You can learn more about npm from the npm’s official website:
https://aka.ms/moc-20486d-m8-pg3
To start using npm in your ASP.NET Core application, you should add a package.json file to your solution
in your project’s root folder. This file gives npm information which allows it to identify the project and to
handle the project's dependencies. It can also contain more information about the project such as a
project description and the version number in a specific distribution.
The package.json file includes several kinds of properties. It includes information describing your
application which includes application version, name, and privacy settings. The most important properties
for developers are dependencies and devDependencies.
• dependencies. This is a list of packages that your client-side logic depends on such as jQuery and
Bootstrap. When a package is located in the dependencies section, npm will always install it as part
of the application.
• devDependencies. This is a list of packages that will be installed only in a development environment.
Those packages are not related to the client-side logic but to bundling, minification, and compilation
among others.
Each package listed in the dependencies and devDependencies section has a version number that
indicates which version of the package will be installed. Visual Studio will always recommend installing the
latest stable version of each package.
The following code example demonstrates how to add jQuery using the package.json file:
}
}
After adding jQuery inside the dependencies section and saving the package.json file, the jQuery
package will be downloaded and placed inside the node_modules folder. This folder is located inside the
root folder of the application and will contain all the code the application needs.
By default, static files in an ASP.NET Core application can be served only if they are located in the
wwwroot folder. If, however you want to allow static files to be served from a different folder, for
example, the node_modules folder, you can use the UseStaticFiles method and pass an instance of type
StaticFileOptions to it as a parameter.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-17
The following code example demonstrates how to allow static files to be served from the node_modules
folder:
Add the node_modules folder to the static files middleware inside the Startup.cs file
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
The following code example demonstrates how to link the scripts installed by using npm to a layout:
Best Practice: Directly referencing the node_modules folder from inside of views is not
considered a good practice. A better practice is to use bundling and minification.
To use bundling and minification in an ASP.NET Core application, you can use the
bundleconfig.json configuration file. The bundleconfig.json configuration file defines how the
application’s static files will be bundled and minified.
MCT USE ONLY. STUDENT USE PROHIBITED
08-18 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
The following code shows a bundleconfig.json file that adds jQuery to the site bundle:
To enable the execution of bundling and minification during the build time, you can download the
BuildBundlerMinifier NuGet package from the NuGet package manager. This package injects MSBuild
Targets which are grouped tasks that run at build and clean time.
Each time that you run a Build or Clean in your application, the bundleconfig.json file will be analyzed by
the build process and the output files will be produced based on the defined configuration.
Note: You can learn how to perform bundling and minification by using task runners in
ASP.NET Core in Module 9, “Client-Side Development”.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use npm to Add a Library“ on the following
page: `https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD08_DEMO.md#demonst
ration-how-to-use-npm-to-add-a-javascript-library.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-19
Lesson 3
Using jQuery
jQuery is a JavaScript library that simplifies the adding of JavaScript to HTML pages in general and to
views in particular. jQuery is an open-source software that you can use for free. It helps reduce the
amount of code that you need to write and perform tasks such as accessing and modifying HTML
elements on a webpage.
jQuery contains many selectors. Selectors are used to select and manipulate HTML elements. jQuery also
contains several functions. An example of a function in jQuery is the ajax function. You can use the ajax
function in jQuery to call a server.
Validating the user input is very important in web applications. It is possible to validate user input in both
the server-side and the client-side. jQuery can be used to help validate user input in the client-side. It is
important to know how to do client-side validation by using jQuery.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe jQuery.
• Describe how jQuery helps access the HTML elements of a webpage.
• Describe how to modify elements by using jQuery.
• Describe how to call a server by using jQuery.
• Describe client-side validation by using jQuery.
Introduction to jQuery
jQuery is a JavaScript library that you can use
within different browsers. jQuery was first
released in 2006. jQuery helps query the HTML
Document Object Model (DOM) and obtain a set
of HTML DOM elements. This feature of jQuery
helps to:
• Reduce the amount of code that you need to
write to perform a task.
• Reduce the client-side development time of
web applications.
jQuery includes the following features:
• DOM element selections.
• DOM traversal and modification.
• DOM manipulation, based on CSS selectors.
• Events.
• Effects and animations.
• AJAX.
• Extensibility through plug-ins.
MCT USE ONLY. STUDENT USE PROHIBITED
08-20 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
• Utilities.
• Compatibility methods.
• Multi-browser support.
jQuery Files
When working with jQuery, you can choose between working with an original version or a minified
version. The jQuery original version and jQuery minified version provide similar functionalities; however,
they optimize web applications for different purposes:
<h2>Index</h2>
<script>
$(function() {
$.each([4, 9], function(index, value) {
alert(index + ":" + value);
});
});
</script>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-21
The each function in jQuery is an iterator function. It can be used to iterate an array. The elements in the
array are iterated by a numeric index, from 0 to the length of the array minus 1. When the jQuery code of
the example above is run in the browser, two popups are shown to the user: the first one has the text 0:4,
and the second has the text 1:9.
The following code example demonstrates how you can use the jQuery variable instead of the $ variable:
The following jQuery selector accesses the HTML element with the hello id:
You can use jQuery to access all instances of a specific HTML element.
You can use more than one selector in a single query to access multiple types of elements at once.
MCT USE ONLY. STUDENT USE PROHIBITED
08-22 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
The following code example demonstrates how to identify all instances of the a element, all instances of
the div element which have the big class, and an element with hello id, and put them together in a single
jQuery array:
After accessing the HTML elements, you can perform actions on the elements, such as:
• Defining event handlers to respond to events associated with the selected HTML elements.
• Reading the attributes of the HTML elements.
The following example adds an event handler to the click event of an HTML element with hello id:
Experienced developers usually shorten this code by using the $() syntax instead of $(document).ready.
The following example demonstrates how to use the $() syntax instead of $(document).ready():
The following example shows how to access HTML elements by using jQuery inside a view:
<div>
Hello
<input type="button" value="Hello" id="hello" />
</div>
<script>
$(function() {
$("#hello").click(function(event) {
alert("Hello World");
});
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-23
});
</script>
Best Practice: It is important to load jQuery library to the page before calling the $() or
$(document).ready() functions. Using the $ object before the jQuery library is loaded might
cause an exception. When using a layout file to load scripts, place the jQuery library source script
inside the <head> tag before all the other files.
<script>
$(function() {
let res = $('h1').text();
console.log(res); // Result: "This is the main headline of a HTML document".
});
</script>
The following example shows how to use the addClass function to add the input_css_class to an HTML
element:
Additional Reading: For more information about jQuery functions that modify HTML
elements, go to https://aka.ms/moc-20486d-m8-pg1
Demonstration Steps
You will find the steps in the section “Demonstration: How to Modify HTML Elements by Using jQuery“ on
the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD08_DEMO.md#lesson-3-
using-jquery.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-25
Note: In this topic, only the client-side code is demonstrated. The server-side code doesn’t
appear in this topic. An example of a server is Web API. Web API is covered in detail in Module
13, “Implementing Web APIs”.
Additional Reading: For more information about the ajax function, go to
https://aka.ms/moc-20486d-m8-pg2
MCT USE ONLY. STUDENT USE PROHIBITED
08-26 Using Layouts, CSS and JavaScript in ASP.NET Core MVC
• jQuery Validate. A jQuery plugin that makes client-side form validations easy. The plugin has a built-
in bundle of useful validation methods, including URL and email validation. You can perform these
validations before a form submit or after any user interaction.
• jQuery Unobtrusive Validation. A custom Microsoft library that is built based on the jQuery Validate
plugin. This plugin allows you to use the same validation logic you wrote on the server-side in the
form of data annotations and metadata and apply it to the client-side immediately.
The following code example demonstrates a package.json file which includes jQuery Validate and jQuery
Unobtrusive Validation:
The following code example demonstrates how to link the scripts installed by using npm to a layout:
When using jQuery Unobtrusive Validation, instead of writing validation logic in two places, you will write
it once. MVC's Tag Helpers and HTML Helpers consume the validation attributes and type metadata from
model properties and render HTML 5 attributes starting with data- on form elements needing validation.
MVC will generate the data- attributes for both built-in and custom attributes.
jQuery Unobtrusive Validation will then parse the data- attributes and passes the logic to jQuery Validate.
This way you can duplicate the server-side validation logic to the client without writing the same logic
twice.
The following code example demonstrates a model with validation attributes:
[DataType(DataType.EmailAddress)]
[Required(ErrorMessage = "Please enter your email address")]
public string Email { get; set; }
}
The following code example demonstrates how to use jQuery Unobtrusive Validation to implement client-
side validation:
</div>
</form>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 08-29
Objectives
After completing this lab, you will be able to:
• Apply a consistent look and feel to the web application.
• Use layouts to ensure that common interface features, such as the headers, are consistent across the
entire web application.
• Render and execute JavaScript code in the browser.
• Use the jQuery script library to update and animate page elements.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD08_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD08_LAK.md.
Review Question
Question: Why do you have sections in a layout?
Best Practices
• Use npm to add client-side libraries to your application. Avoid installing client-side libraries by using
the NuGet packages manager.
• When using a CDN to add scripts to your application, create a fallback in case the script is not
available on the CDN.
Module 9
Client-Side Development
Contents:
Module Overview 9-1
Lesson 1: Applying Styles 9-2
Lesson 2: Using Task Runners 9-19
Lesson 3: Responsive Design 9-33
Lab: Client-Side Development 9-42
Module Review and Takeaways 9-44
Module Overview
When creating an application, it is important to know how to develop both client-side and server-side of
the application. In this module, you are going to learn client-side tools that will allow you to create
extensive web applications on any scale. These tools are based on the topics covered in Module 8, "Using
Layouts, CSS and JavaScript in ASP.NET Core MVC".
In this module, you are going to learn how to use the Bootstrap framework to style your web application.
Then you are going to learn how to use Sass and Less, two common Cascading Style Sheets (CSS)
preprocessors that add features to CSS, such as variables, nested rules, and functions. These greatly
improve the maintainability of complex web applications.
Next, you will learn how to set up task runners such as Grunt and gulp and how to use them to compile
Sass files during the Microsoft Visual Studio build. You will learn how to use the gulp task runner to
perform bundling and minification of CSS and JavaScript files and how to set up a watcher task to compile
Sass files as you write your code, without the need to rebuild the solution.
Finally, you will learn responsive design tools that allow you to customize your web application’s display
based on the capabilities and specifications of a web browser or a device. You will learn to write CSS
media queries, how to use the Bootstrap responsive grid system, and how to apply the CSS flexbox layout
to your views.
Objectives
After completing this module, you will be able to:
Lesson 1
Applying Styles
To style your web application, you need to write lots of CSS code. There are many tools that allow you to
speed up the process of writing and applying styles to web applications. In this lesson, you are going to
learn some of these tools.
You are going to learn how to use Bootstrap, a framework that includes many pre-built components and
styles that can be applied to your applications. Also, you are going to learn how to use Sass and Less, CSS
preprocessors, that are compiled to CSS and can help you to style your web application.
Lesson Objectives
After completing this lesson, you will be able to:
• Add Bootstrap to your web application.
• Use various Bootstrap components.
• Apply styles to your web applications by using Sass.
• Apply styles to your web applications by using Less.
Introduction to Bootstrap
Bootstrap is an open-source framework for
building responsive web applications using
HTML, CSS, and JavaScript. It allows you to
quickly develop the client-side of your
application by using pre-built components and
styles.
Note: jQuery is covered in Module 8, "Using Layouts, CSS and JavaScript in ASP.NET Core
MVC".
After adding the dependencies, you should also include bootstrap.css, which includes the styles and
bootstrap.js, which includes the scripts. If you are using the production environment, include the minified
versions.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-3
There are many ways to add Bootstrap to your application. In this topic, you will see how you can use
npm to add Bootstrap to your web application.
Note: Various ways to add external client-side libraries to ASP.NET Core applications are
covered in Module 8, "Using Layouts, CSS and JavaScript in ASP.NET Core MVC".
The following code example demonstrates how to add Bootstrap and its dependencies to the
package.json file:
After adding package.json to your web application, the Bootstrap framework and its dependencies will
be downloaded to the node_modules folder.
The following example demonstrates how to link a layout to Bootstrap and its dependencies:
<script src="~/node_modules/jquery/dist/jquery.js"></script>
<script src="~/node_modules/popper.js/dist/umd/popper.js"></script>
<script src="~/node_modules/bootstrap/dist/js/bootstrap.js"></script>
</body>
</html>
Bootstrap Components
Bootstrap includes many components that can
be added to your web application. Some of these
components are composed of pure HTML and
CSS markup and some components need
JavaScript code to work. In this topic, you will see
several common components that you can use in
your application.
Alerts
Alerts are used to display messages to users as a result of their actions. Bootstrap alerts are responsive and
can include links inside them.
To create an alert, you can add a div to your view and apply two CSS classes to this div. The first CSS class
is alert, which defines this div as an alert. The second class that needs to be added will define the alert
type and color. For instance, the alert-danger class defines the alert with a red color scheme. Other alert
classes include alert-primary, alert-secondary, alert-success, alert-warning, alert-info, alert-light and
alert-dark. You can add a role=”alert” attribute to the div to appropriately convey its purpose to
assistive technologies such as screen readers. To create a link inside an alert, add an <a> tag and give it a
CSS class of alert-link.
The following code example demonstrates how to add designed alert messages to a view:
Buttons
Buttons are used to create styled action elements inside forms, alerts, tables, grids and more. Bootstrap
includes several predefined styles for buttons with multiple size options and states.
To create a button, you can add a button HTML element to your view and apply CSS classes to it. A
mandatory CSS class is btn, which applies Bootstrap design for buttons to this element. You can then
apply several CSS classes that define the button characteristics. These CSS classes include:
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-5
Dropdowns
Dropdowns are used to create lists of links that overlay the content of the page when they are shown.
Dropdowns are togglable and interactive.
To create a dropdown list, you can add a div with the dropdown class, which will include all the elements
needed to be displayed. Then, you can add a button or link that when clicked will toggle the display of
the dropdown list. This button can have all the classes that were covered above in the description of
Bootstrap buttons. Also, this button needs to have the dropdown-toggle class and the data-
toggle=”dropdown” attribute that will allow it to toggle the display of the dropdown list.
After the button, you need to add a div with the dropdown-menu class. This div will include all the links
and items displayed inside the dropdown list. An item inside the dropdown list should have the
dropdown-item class. To separate items in a dropdown list, you can add a div with the dropdown-
divider class.
Note: Make sure that you include the popper.js script before using dropdowns because
Bootstrap dropdowns are built on top of this plugin. Adding popper.js to ASP.NET Core
application is covered in the previous topic, “Introduction to Bootstrap”.
<div class="dropdown">
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown">
Info Button with Dropdown
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Link A</a>
<a class="dropdown-item" href="#">Link B</a>
<a class="dropdown-item" href="#">Link C</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Separated Link</a>
</div>
</div>
Forms
Bootstrap includes styles for many types of form controls that can be used practically in any web
application. Bootstrap form controls include: textual form controls such as <input type="text">and
<textarea>, password field, checkbox, radio button, select, file input, range input and more.
To get the desired look, it is important to follow the required hierarchy of the HTML elements and the CSS
classes. For example, to create an input element for some text field that has a label, the label and input
HTML elements should be under a div with the form-group class. Each input control should have the
form-control class.
The following code example demonstrates how to add bootstrap form controls to a view:
<form>
<div class="form-group">
<label for="firstname">First Name</label>
<input type="text" class="form-control" id="firstname" placeholder="First Name">
</div>
<div class="form-group">
<label for="lastname">Last Name</label>
<input type="text" class="form-control" id="lastname" placeholder="Last Name">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control" id="email" placeholder="Email">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" placeholder="Password">
</div>
<div class="form-group">
<label for="city">City</label>
<input type="text" class="form-control" id="city">
</div>
<div class="form-group">
<label for="state">State</label>
<select id="state" class="form-control">
<option selected>Choose...</option>
<option>NY</option>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-7
<option>..</option>
</select>
</div>
<div class="form-group">
<label for="zip">Zip</label>
<input type="text" class="form-control" id="zip">
</div>
<button type="submit" class="btn btn-primary">Sign Me Up</button>
</form>
Navs
Bootstrap has a large variety of responsive and mobile friendly navigational controls. They can allow you
to add navigation menus easily. All navigational controls share the nav class. Each item in the nav menu
should have the nav-item class. Each link inside the menu item should have the nav-link class. The
disabled class should be used on disabled links and the active class should be used on the currently
active link.
The following code example demonstrates how to add a Bootstrap nav to a view:
<ul class="nav">
<li class="nav-item">
<a class="nav-link active" href="/">Home Page</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/home/about">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/home/contact">Contact</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled Link</a>
</li>
</ul>
Navbars
A navbar is a Bootstrap responsive navigation header that can include the website name, menu and more.
To get started with navbars, it is important to be familiar with the following:
• Each navbar should be wrapped with at least two classes: navbar and a class compound of navbar-
expand and one of the following endings: -sm, -md, -lg, or -xl. For example, navbar-expand-sm or
navbar-expand-md and so on.
• Each navbar wrapper can have a class that defines its color scheme, for example, navbar-light or
navbar-dark. You can also customize the color schema with bg-* classes, for example, bg-light.
MCT USE ONLY. STUDENT USE PROHIBITED
09-8 Client-Side Development
o navbar-toggler. Can be used for toggling. You should also add the data-toggle=”collapse”
attribute and the data-target=”#id” attribute, where id stands for the element needed to be
toggled. In case you want the icon to look like a hamburger, you can use the navbar-toggler-
icon class.
o collapse and navbar-collapse. Can be used to group and hide navbar contents.
The following code example demonstrates how to add a Bootstrap navbar to a view:
Modals
Bootstrap modal windows are used to display dialog messages and user notifications inside your
application. They are placed over the site content and can be closed by clicking on the backdrop of the
modal. Only one modal window can be shown at a time.
To trigger a modal window, you can apply two attributes to a button. The first attribute is data-
toggle=”modal”, which opens the modal window. The second attribute is data-target=”#id” where id
stands for the id of the modal.
The modal can be a div that has a modal class. To improve accessibility for people that use screen readers
you can apply the role=”dialog” attribute to the div. You need to use the modal-dialog class to properly
set the width and margin of the modal properly.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-9
To set the styles of the content of the modal, you need to use the model-content class. The modal
content can have several parts which include:
• modal-header. Can be used to style the header of the modal.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Work with Bootstrap“ on the following
page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD09_DEMO.md#demonst
ration-how-to-work-with-bootstrap.
MCT USE ONLY. STUDENT USE PROHIBITED
09-10 Client-Side Development
Note: Both Sass and Less are CSS preprocessors providing similar features with some
unique extensions. Less will be covered in the next topic "Styling Applications with Less".
Best Practice: Place Sass files inside of a dedicated Styles folder which is not located inside
the wwwroot folder. Place the compiled CSS files inside the wwwroot folder.
To compile a Sass file, you can use several techniques, which include:
• Install Sass globally by using npm and then using the sass command. This technique will be covered
in this topic.
• Configure a task runner. This technique will be covered in Lesson 2, “Using Task Runners”.
To install Sass globally, you can run the following command:
Installing Sass
npm install -g sass
The following code example demonstrates how to compile the main.scss file to the main.css file:
Variables
Variables are one of the basic features Sass adds to CSS. Variables are a fundamental part of any
programming language and now you can use them with CSS. Sass variables are often used to define
colors, fonts, dimensions and any values that are repeated multiple times throughout the application. This
allows you to define theme variables once and then inject them where needed.
Variables help you to create maintainable CSS. When you need to change the general theme of your
website, you will mainly need to change the values of your variables without changing the rest of the
code.
The following code example demonstrates how to define and use Sass variables:
.message {
color: $highlights;
font-size: 12px;
}
h1 {
color: $highlights;
}
The following code example shows the CSS output you will get after Sass compilation:
h1 {
color: #00FFFF;
}
Nesting Styles
CSS doesn’t support nested hierarchy of selectors. When working with Sass, you can define styles by using
nested selectors and use a syntax that is more natural and easier to read.
The following code example demonstrates how to define the style of elements by using nested selectors:
The following code example shows the CSS output after compilation of Sass code:
Mixins
Mixins are one of the most powerful and time-saving features of Sass. Mixins allow you to group CSS
declarations and reuse them throughout your code. This feature can help you to reduce the amount of
code and avoid duplication.
The following code example demonstrates how to use a mixin to avoid duplication of code:
Using a Mixin
/* Define a mixin */
@mixin normalized-text() {
font-weight: 300;
line-height: 1.2;
}
The following code example shows the CSS output after compilation of Sass code:
.comment {
font-weight: 300;
line-height: 1.2;
color: grey;
}
Mixins in Sass act as functions - they allow you to pass in parameters and return sets of CSS properties.
This allows mixins to be more flexible.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-13
The following code example shows the CSS output after compilation of Sass code:
.comment {
font-weight: 300;
line-height: 1.2;
color: grey;
}
Control Directives
Control directives allow you to include certain styles only under specific conditions. They can be used
inside mixins to include the same style with variations. Some of the control directives available in Sass are:
@if, @else, @else if, @each and @for.
The following example demonstrates how to use control directives inside a mixin:
.description {
@include season(summer);
}
MCT USE ONLY. STUDENT USE PROHIBITED
09-14 Client-Side Development
The following code example shows the CSS output after compilation of Sass code:
Functions
Sass exposes a wide range of functions that allow you to perform manipulations on styles such as increase
or decrease the lightness of a given color, perform mathematic calculations such as round and ceil and
more.
Following are examples of some functions available in Sass:
• lighten. Increases the lightness of a color by a certain percentage. For example,
lighten(#fe6700,20%) will increase the lightness of a certain orange hue by 20% percent.
• darken. Increases the darkness of a color by a certain percentage. For example,
darken(#fe6700,20%) will increase the darkness of a certain orange hue by 20% percent.
Additional Reading: Full documentation of the functions available in Sass can be found at:
https://aka.ms/moc-20486d-m9-pg11
The following code example demonstrates how to use the functions lighten and darken:
Using Functions
$main-color: #80E619;
.main-headline {
color: darken($main-color, 20%);
}
.sub-headline {
color: lighten($main-color, 20%);
}
The following code example shows the CSS output after compilation of the Sass code:
.sub-headline {
color: #b3f075;
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-15
Best Practice: Place Less files inside of a dedicated Styles folder which is not located inside
the wwwroot folder. Place the compiled CSS files inside the wwwroot folder.
To compile a Less file, you can use several techniques, which include:
• Install Less globally by using npm and then using the lessc command. This technique will be covered
in this topic.
• Configure a task runner. This technique will be covered in Lesson 2, “Using Task Runners”.
To install Less globally, you can run the following command:
Installing Less
npm install -g less
The following code example demonstrates how to compile the main.less file to the main.css file:
Variables
Variables allow you to store information that you want to repeat throughout your code. Less variables
behave like Sass variables with a slight change of syntax. To define a new variable in Less you will use the
@ sign.
The following code example demonstrates how to define and use Less variables:
.message {
color: @highlights;
font-size: 12px;
}
h1 {
color: @highlights;
}
MCT USE ONLY. STUDENT USE PROHIBITED
09-16 Client-Side Development
The following code example shows the CSS output after compilation of the Less code:
Nesting Styles
Nesting is a feature of Less that allows defining styles by using nested code. The syntax of Less nested
selectors is the same as in Sass.
This following code example demonstrates how to define the style of elements by using nested selectors:
The following code example shows the CSS output after compilation of the Less code:
Mixins
Mixins in Less behave and are declared differently than in Sass. Less mixins allow you to mix in values from
one class into another class. This is a very important feature that can reduce the amount of code you have
to write and to avoid duplication.
The following code example demonstrates the repetition of code inside CSS files that Less solves with
mixins:
.comment {
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-17
font-weight: 300;
line-height: 1.2;
padding: 20px;
color: grey;
}
The following code example demonstrates how to use a mixin to avoid duplication of code:
Using a Mixin
/* Define a class */
.normalized-text {
font-weight: 300;
line-height: 1.2;
}
Functions
Less exposes a wide range of functions that allow you to perform manipulations on styles such as increase
or decrease the lightness of a given color, get the dimension of an image in a specific URL, convert one
unit to another, perform mathematic calculations such as round and ceil and more. Some methods might
be like the ones that exist in Sass.
Following are examples of some functions that available in Less:
• lighten. Increases the lightness of a color by a certain percentage. For example,
lighten(#fe6700,20%) will increase the lightness of a certain orange hue by 20% percent.
• darken. Increases the darkness of a color by a certain percentage. For example,
darken(#fe6700,20%) will increase the darkness of a certain orange hue by 20% percent.
• image-size. Retrieves the width and height of a certain image. For example, if the values of the width
and the height of the image.png file are 150px, calling image-size("image.png") will return 150px
150px.
• convert. Converts from one unit to another. For example, convert(9s, "ms") will convert 9s to
9000ms.
Additional Reading: Full documentation of the functions available in Less can be found at:
https://aka.ms/moc-20486d-m9-pg1
MCT USE ONLY. STUDENT USE PROHIBITED
09-18 Client-Side Development
The following code example demonstrates how to use the lighten and darken functions:
.main-headline{
color: darken(@main-color, 20%);
}
.sub-headline{
color: lighten(@main-color, 20%);
}
The following code example shows the CSS output after compilation of the Less code:
Lesson 2
Using Task Runners
In a modern web environment client-side code isn’t always designed as simple JavaScript and CSS files.
These days various technologies are being used which create additional steps in the development of
client-side logic. Whether it can be the use of unconventional file types such as Sass or Less which require
compilation, but in turn reduce the work for the developer, or it can be via processes such as bundling
and minification that improve performance.
As web technologies grow, so does the need to support the myriad possibilities. Task runners were
created for these purposes. Task runners are libraries and applications which can be used to define tasks
and carry them out by using various plugins. This enables new technologies to add a plugin and become
instantly viable. In addition, task runners can handle other development side tasks such as unit testing.
In this lesson, you will learn how to set up a task runner by using either gulp or Grunt, how to use them in
compiling Sass, and how to connect the compilation to running during a Visual Studio build. You will later
learn how to use gulp to perform bundling and minification and how to set up a watcher task to watch
files as you work on them, enabling client-side development without a need to rebuild the solution.
It is important to understand that by properly utilizing task runners and Visual Studio integration, it is
possible to focus more effort on developing client-side code and reducing the requirement to handle
various menial tasks such as running scripts and performing operations by hand.
Lesson Objectives
After completing this lesson, you will be able to:
• Explain the purpose and state various use cases for task runners.
• Create and import tasks and run them in Grunt.
• Create tasks by using plugins and run them in gulp.
• Use gulp to fully import your client-side code and CSS folders into the wwwroot folder while utilizing
advanced client-side technologies like Sass and Bootstrap.
• Use gulp to optimize your client-side code.
Using Grunt
While developing a web application, there are
operations that you may do many times.
Examples of such operations are minification of
code and compilation of files. Instead of doing
these operations yourself, you can use a task
runner which will perform these operations for
you.
In this lesson, two task runners will be
introduced. In this topic, Grunt will be
introduced. Another task runner, gulp, will be
introduced in the next topic.
MCT USE ONLY. STUDENT USE PROHIBITED
09-20 Client-Side Development
Grunt operates by having each task run separately of every other task. Grunt itself only supports the basic
infrastructure for running tasks and you will need to add additional plugins or configure the logic for the
tasks in order to support them. As a result, Grunt supports a large number of additional task libraries each
of which can be run as required, while avoiding having to deal with a bloated environment full of
unnecessary code, and you can run a very bare environment by using only the plugins that you need.
Being a client-side task runner, Grunt will mainly be used to perform operations on client-side code. This
can include things like performing minification (compressing JavaScript or CSS code to reduce space
requirements, and therefore improve loading times), bundling (merging several different JavaScript or CSS
files into one file, in order to reduce the number of requests), compiling nonstandard file types (such as
Sass or TypeScript) and more.
In this topic, you will be introduced to Grunt and learn some basic functionality for it. You will learn how
to compile Sass files by using Grunt, and how to integrate them to automatically run as you work on the
project, enabling you to spend more time working on code, and less time managing tasks.
Grunt in package.json
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"grunt": "1.0.2"
}
}
Note: It is important to note that the version of Grunt in this example is the latest stable
version at the time of writing this course. There might be a newer version at this time.
After adding Grunt to the package.json file, you need to add a new js file in the base folder of the
project. This file should be named Gruntfile.js. Inside the Gruntfile.js file you need to create a wrapper
function in a specific format, every single Gruntfile.js requires it, and cannot be used without it.
The following code is an example of a Gruntfile wrapper function:
Gruntfile.js Wrapper
Using gulp
Sections of Gruntfile.js
The wrapper inside the Gruntfile.js file is the external shell in which all of the Grunt code will run. There
can be several different sections inside the wrapper function, which include:
• Project and task configuration information. The project and task configuration are handled inside
the grunt.initConfig method. In here it is possible to configure various tasks and their behavior.
• Loading the Grunt plugins and tasks. Loading the Grunt plugins is done by using the
grunt.loadNpmTasks method. This method accepts the name of a plugin which is then loaded. Once
the plugin is loaded, it is possible to run the task.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-21
• Custom tasks which are created by the user. This can range from a task designed to run several
other tasks all the way to creating custom tasks with logic that is relevant for the specific application.
An example of a Grunt plugin is grunt-sass. This plugin allows compiling Sass files into CSS files. You can
then use those CSS files in an ASP.NET Core MVC Application.
To use the grunt-sass plugin, you first need to add a dependency to it inside the devDependencies
section of the package.json file.
The following code is an example of adding a Grunt plugin dependency:
Once the plugin is added to and downloaded by npm, you need to configure it in the grunt.initConfig
method. The grunt.initConfig method receives an options parameter which is used to configure tasks.
For example, for grunt-sass, a task for compiling Sass, the sass property in the options parameter should
be assigned to. To compile files from a folder, you will need to first update the dist property (dist is a
shorthand for distribute) and underneath to set the files property which is used to determine which files
are copied.
The files property can accept an array of objects which can be used to declare the logic for Sass
compilation, so that you can potentially copy from multiple sources into multiple destinations, or into the
same destination.
Each object in the array is defined in the following format which is a standard Grunt format which can be
used across many Grunt plugins:
• expand. When set to true, signifies working with directories rather than files.
• cwd. Short for current working directory, denotes the folder from which to run the script.
• src. Short for source, an array of regular expressions to match the files. Usually for Sass files [‘**/*.scss’]
or [‘**/*.sass’] will be used.
• dest. Short for destination, determines the folder to which Sass files will be compiled.
• ext. Short for extension, determines under what file extension complied files will be saved. Usually .css
for Sass.
The following code demonstrates how to configure grunt-sass inside the grunt.initConfig method:
}
}
});
};
In this example you can see that compilation for Sass is being configured from a directory (expand: true).
The directory you will compile from is Styles (cwd: ‘Styles’).
You will compile all files with the extension .scss inside of the folder (src: [‘**/*.scss’]).
The compiled files will be deployed to the wwwroot/styles folder (dest: ‘wwwroot/styles’).
The files will be deployed with the file extension css (ext: ’.css’).
Note: The format of files is standard across Grunt itself. However, the specific order of
configurations may change between the different plugins. Make sure to check the plugin you use
carefully in case you need to make any changes.
Finally, once you have configured the parameters for the task, you will need to load it by calling the
grunt.loadNpmTasks method, and supply grunt-sass as a parameter to the loading function.
The following code demonstrates a complete Gruntfile.js for compiling Sass files:
grunt.loadNpmTasks("grunt-sass");
};
Once you have completed this, you should be able to see the new task inside the Visual Studio Task
Runner Explorer window and you can run the task by right-clicking it and clicking on Run.
To demonstrate running the task configured, assume the following code of a Sass file named
myStyle.scss, which is located inside a folder named Styles in the base folder of the application:
Sass File
$main-color: #FF8934;
.myClass {
background-color: $main-color;
}
#myElement {
color: $main-color;
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-23
After running the task, a new CSS file named myStyle.css will be created under the wwwroot/styles
folder.
The following code is an example of registering a custom task in the Gruntfile.js file:
In this example, you are registering a task named myTask that, whenever it is run, the string Hello
World… will be displayed in the task output screen.
Occasionally, you may wish for multiple tasks to occur at a time. In order to implement this, you will once
again use the grunt.registerTask method, however this time, the syntax you will use is
grunt.registerTask(*alias task name*, [*task description*], *task list*), with the description being
optional, and is often omitted entirely. Tasks like this are known as alias tasks, and in Visual Studio Task
Runner Explorer they appear as such. By running this task all tasks in the list are executed in order.
The following code is an example of an alias task:
Alias Task
module.exports = function(grunt) {
grunt.initConfig({
sass: {
dist: {
files: [{
expand: true,
cwd: 'Styles',
src: ['**/*.scss'],
dest: 'wwwroot/styles',
ext: '.css'
}]
}
}
});
grunt.loadNpmTasks("grunt-sass");
In this example, you will see myTask being executed, followed by the Sass compilation task.
Using gulp
Another task runner that you can use is gulp.
Unlike Grunt, a gulp task can perform several
sequential operations on a single pipeline,
leading to a very different development
approach from Grunt.
Similarly to Grunt, gulp also supports only a very
basic infrastructure for running tasks and
additional plugins will need to be loaded in order
to perform most operations. Configuration for
gulp files is handled inside the tasks themselves,
and since tasks run on a single pipeline,
configuration often ends up being relatively
simple. gulp supports a large number of plugins, which can grant gulp various different functionalities
which can then be used inside of the tasks. This enables running a lean task environment where only
required plugins are added.
gulp is intended to be used for client-side tasks, and is often used for bundling, minification, and
compilation for various client-side technologies.
In this topic, you will learn the basics behind gulp, how to use it to compile Sass files, as well as how to
integrate Sass compilation into the Visual Studio environment, preventing you from needing to compile
on a regular basis.
gulp Dependency
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"gulp": "3.9.1"
}
}
Note: It is important to note that the version of gulp in this example is the latest stable
version at the time of writing this course. There might be a newer version at this time.
After the dependency has been loaded, a gulpfile.js file should be created. Task configuration will be
done inside the gulpfile.js file.
Inside gulpfile.js you should load up gulp by calling the require(‘gulp’) function. The require function
enables you to request that the gulp dependency will be loaded in the context of the current file. Doing
this will enable you to start writing tasks.
The following code is an example of calling the require function in the gulpfile.js file:
gulpfile.js
var gulp = require('gulp');
Writing a Task
After setting up the environment it is possible to start creating gulp tasks. Unlike Grunt which tends to use
tasks that are created in advance, and then configure them, in gulp, the configuration will usually be done
as part of a created task. Therefore, every task that is run in gulp will always act as a custom task.
To add additional functionalities, you will need to load additional plugins. The first step is to add plugins
to the devDependencies section of the package.json file. This will set npm to install them into the
application. Afterwards, you will need to load them from inside the gulpfile.js file. This can be done by
using the require method and providing it with the name of the plugin which you need to load.
An example of a gulp plugin is gulp-sass. This plugin allows compiling Sass files into CSS files. You can
then use those CSS files in an ASP.NET Core MVC application.
The following code demonstrates adding the gulp-sass plugin to the package.json file:
The following code demonstrates adding the gulp-sass plugin to the gulpfile.js file:
After the gulp-sass plugin has been loaded, it will be available to use inside tasks.
By convention, all file configurations should be defined at the top of the gulpfile.js file, directly under the
section for loading all plugins by using the require method. This is done so that you will be able to easily
change them when the requirement changes. It is particularly helpful when using shared configurations
across several tasks. However, this segment is entirely optional. Examples of relevant configurations could
include file paths, task names, important strings and numbers and more. There isn’t any one correct
structure for configurations so you should create a JavaScript object that works for your requirements.
The following code demonstrates a gulpfile.js file with configurations:
var paths = {
webroot: "./wwwroot/"
};
paths.sass = "./Styles/*.scss";
paths.compiledCss = paths.webroot + "styles/";
In the example above, you can see that a shared path configuration is created, which sets the webroot
property to hold the path to the wwwroot folder. Additionally, it is then used to create a path to where
the Sass files are stored and the destination for compiled Sass files.
After the setup is completed, a task can be created by using the gulp.task function. This function uses the
following syntax: gulp.task(*task name*, [*dependency tasks*], *task function*). The task name being
the name of the task which will be created, dependency tasks is a list of other tasks which will be run as
part of this task, and the task function is the code which will be run in the task. Both the dependency tasks
and the task function are optional parameters. You can choose to use one or the other or both as needed.
Inside the gulp.task function you can add your code and use it to perform any tasks you wish. Commonly
gulp functions will perform various operations by creating a memory stream, writing into it, and finally
closing it turning it into a read-only stream. By using the pipe method on the stream, it will write the
current stream into the stream created by the following function, allowing it to continue working from the
point at which the last function finished. The pipe function receives a method as a parameter which will
receive the stream from the previous method in the pipeline.
You will often begin gulp tasks by invoking the gulp.src method. The first parameter of this method is a
regex file path or potentially a collection of regex file paths. This allows loading one or even more files as
needed to perform your desired operation. You can then use the pipe function to continue processing the
files.
At the end of the process, the gulp.dest method will frequently be called to save the stream that was
previously created into files. It receives a folder into which it will write all files currently stored inside the
memory stream. It is usually the last method in the pipeline, however, you can still use the pipe function
to continue using the stream.
When loading the gulp-sass plugin, it is also possible to use the sass method. By using a stream that it
receives from the pipe, the sass method compiles Sass files into CSS files and returns a stream which can
be used with the pipe method to continue the process. It is also possible to attach to the on event.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-27
var paths = {
webroot: "./wwwroot/"
};
paths.sass = "./Styles/*.scss";
paths.compiledCss = paths.webroot + "styles/";
gulp.task("sass", function() {
return gulp.src(paths.sass)
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(paths.compiledCss));
});
In this example, a task is created that reads all of the Sass files under the styles folder of the application. It
then compiles the Sass files into CSS files which are then placed in the styles folder inside of the
wwwroot folder. At this point, you can use the Visual Studio Task Runner Explorer to run a newly created
task.
gulp.task("one", function() {
console.log("one");
});
gulp.task("two", function() {
console.log("two");
});
gulp.task("three", function() {
console.log("three");
});
In this example, calling the all task will run tasks one, two and three, before finally printing the string
four in the Task Runner Console.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use gulp to Compile Sass to CSS“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD09_DEMO.md#demonst
ration-how-to-use-gulp-to-compile-sass-to-css.
By using these plugins, you will be able to perform bundling and minification. Throughout this topic, you
will see several separate minification and bundling tasks and you will learn additional possibilities for
working with gulp.
The following code example demonstrates how to minify a CSS file which is located in the node_modules
folder and add the result to the wwwroot folder:
var paths = {
webroot: "./wwwroot/",
nodeModules: "./node_modules/"
};
gulp.task("minify-vendor-css", function() {
return gulp.src(paths.bootstrapCss)
.pipe(concat(paths.vendorCssFileName))
.pipe(cssmin())
.pipe(gulp.dest(paths.destinationCssFolder));
});
In this example, the bootstrap.css file is read from the node_modules folder and saved as
vendor.min.css by calling the concat function. Then the CSS content inside it is minified by calling the
cssmin function. Finally, the file is written into the styles folder of the wwwroot folder. At this point, the
file can easily be linked to from a layout or a view. A major advantage of this approach is that if new CSS
libraries are added, they can easily be added to the gulpfile.js without affecting the client.
The following code is an example of compiling, bundling and minifying Sass files:
Sass Preprocessing
var gulp = require('gulp');
var concat = require("gulp-concat");
var cssmin = require("gulp-cssmin");
var sass = require('gulp-sass');
var paths = {
webroot: "./wwwroot/",
nodeModules: "./node_modules/"
};
paths.sassFiles = "./Styles/*.scss";
paths.compiledCssFileName = "style.min.css";
paths.destinationCssFolder = paths.webroot + "styles/";
gulp.task("minify-sass", function() {
return gulp.src(paths.sassFiles)
.pipe(sass().on('error', sass.logError))
.pipe(concat(paths.compiledCssFileName))
.pipe(cssmin())
.pipe(gulp.dest(paths.destinationCssFolder));
});
MCT USE ONLY. STUDENT USE PROHIBITED
09-30 Client-Side Development
In this code example, all .scss files inside of the application styles folder are read. Next, all of the .scss
files are compiled into regular CSS files. Then, the CSS files are combined to a single file named
style.min.css. Afterwards, the combined CSS file is minified. Finally, the file is written into the styles folder
of the wwwroot folder. At this point, you can link this file to the layout or a view and it will be fully
usable.
The following code is an example of bundling and minifying multiple client-side JavaScript files:
var paths = {
webroot: "./wwwroot/",
nodeModules: "./node_modules/"
};
gulp.task("minify-vendor-js", function() {
return gulp.src(paths.vendorJsFiles)
.pipe(concat(paths.vendorJsFileName))
.pipe(uglify())
.pipe(gulp.dest(paths.destinationJsFolder));
});
This code example demonstrates how to serve both Bootstrap and jQuery in a single minified JavaScript
file. This task begins by reading both bootstrap.js and jquery.js by using an array of file paths. The task
then merges both files under the file name vendor.min.js, and then it minifies the resulting file by calling
the uglify function. Finally, the file is written into scripts/vendor.min.js. In the future, should the need
arise to add more JavaScript files, they can be added into the gulpfile.js without needing to update the
layout or a view.
Note: It is crucial to note that the logic for minifying CSS and JavaScript files is different,
necessitating separate minification plugins. However, since gulp-concat only puts files together
and renames them it is possibly to use it to bundle both.
The following code is another example of bundling and minifying JavaScript files:
var paths = {
webroot: "./wwwroot/",
nodeModules: "./node_modules/"
};
paths.jsFiles = "./Scripts/*.js";
paths.minifiedJsFileName = "scripts.min.js";
paths.destinationJsFolder = paths.webroot + "scripts/";
gulp.task("minify-js", function() {
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-31
return gulp.src(paths.jsFiles)
.pipe(concat(paths.minifiedJsFileName))
.pipe(uglify())
.pipe(gulp.dest(paths.destinationJsFolder));
});
This code example demonstrates a task that reads all files in the Scripts directory of the application,
combines them into a file called scripts.min.js, minifies it and finally saves it in wwwroot inside the
scripts folder, which is easily linked to from the layout or a view.
Best Practice: It is considered a best practice to keep code from node_modules separate
from the application code in the process of bundling and minification. This helps to troubleshoot
problematic areas within the application code. However, it is entirely possible to combine
node_modules code with the application code.
It is a good idea for JavaScript and CSS files to be bundled and minified whenever a new build is
running. This will enable using the latest versions of JavaScript and CSS files, providing a hassle-
free environment and allowing complete focus on the coding aspect, while not having to deal
with manually running tasks. To achieve this, you can create a singular task assigned to running
all the separate subtasks.
Multiple Tasks
gulp.task("all-css", ["minify-vendor-css", "minify-sass"]);
In this example, you can see that three new tasks have been added. The first performs both CSS bundling
and minification tasks and the second does the same for JavaScript. Meanwhile, the third task calls both
and when all subtasks are done, it prints: All Tasks Completed to the task runner output.
By binding the final task inside the task runner to build start, you can ensure that whenever the
application is run, the latest version of files will be served and available.
Watcher Tasks
An additional tool at your disposal is watcher tasks. Watcher tasks are tasks that are usually run when the
project is loaded and continue running until it is closed. The purpose of watcher tasks is to watch for any
changes occurring in files and if a change occurs, to run the appropriate task. For example, this can allow
making real-time changes in the .scss and .js files and have the appropriate compilation, bundling, and
minification occurs immediately. This can help reduce load during the build process and to allow testing
client-side file changes without recompiling server-side code.
A watcher is configured by using the gulp.watch() method. The first parameter that will be supplied to
the watch is a file or directory regex or a list of such. The second parameter is an array of tasks which will
be run by the watcher.
Watchers are commonly bound to the project.open event of the Task Runner Explorer, allowing them to
run in the background and perform their operations as the developer engages in their day to day work.
MCT USE ONLY. STUDENT USE PROHIBITED
09-32 Client-Side Development
gulp.task("js-watcher", function() {
gulp.watch(paths.jsFiles, ["minify-js"]);
});
In this example, you can see that a watcher has been applied to the styles folder of the application, and
another watcher to the scripts folder. When Sass code inside the styles folder is added, removed or
changed, the minify-sass task will be run. Meanwhile, whenever a JavaScript file inside the application’s
scripts folder is added, removed or changed, the minify-js task will be run.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-33
Lesson 3
Responsive Design
Responsive web design is used to make sure your application looks good on any screen and device. It is
an approach that says that the design should respond to users’ behavior based on screen size, platform,
and orientation. In this lesson, you are going to learn different techniques to adapt your website to
various screen sizes and devices.
Lesson Objectives
After completing this lesson, you will be able to:
• Ensure that a web application displays correctly on different devices.
• Use media queries to apply different CSS for different screen sizes.
• Use the Bootstrap grid system to layout the content of the pages.
• Use CSS flexbox to create flexible web applications.
The width and height properties help you to specify the width and height of the virtual viewport window
in pixels. You can use the device-width keyword to enable the content to fit the default screen size of the
browser.
MCT USE ONLY. STUDENT USE PROHIBITED
09-34 Client-Side Development
The initial-scale property controls the initial scale or zoom level of a webpage. You can use other
properties such as maximum-scale, minimum-scale, and user-scalable to control how the user can
zoom in or out of the page.
Additional Reading: For more information about the viewport attribute, refer to:
https://aka.ms/moc-20486d-m9-pg2
You can also apply a media query in the <link> element. The following example illustrates how to include
a media query in a <link> element:
You can use CSS media queries to apply CSS styles when the screen size is less than 500 pixels. However,
you can use CSS media queries only for the screen layout, but not the print layout.
The following table describes properties that you can include in a media query.
Property Description
width The width of the targeted display area, which includes the browser window
in desktop and mobile devices. In desktop computers, when you resize the
browser window, the width of the browser changes. However, on most
mobile browsers, you cannot resize the browser window. This implies that
the width of the browser remains constant.
height The height of the targeted display area, which includes the browser window
in desktop and mobile devices.
device-width The width of the entire screen of a device. For a desktop with a screen
resolution of 1,024x768, the device-width is usually 1,024 pixels.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-35
device-height The height of the entire screen of a device. For a desktop with a screen
resolution of 1,024x768, the device-height is usually 768 pixels.
orientation The orientation of the device. If the device-width is larger than the device-
height, the orientation value is set to landscape; otherwise, it is set to
portrait.
device-aspect-ratio The ratio of the device-width and device-height properties. The following
example illustrates the device-aspect-ratio for a device with a screen
resolution of 1,280x720.
@media screen and (device-aspect-ratio: 16/9) { }
@media screen and (device-aspect-ratio: 1280/720) { }
@media screen and (device-aspect-ratio: 2560/1440) { }
color The number of bits per color component of the device. If the device is not a
color device, the value is zero.
color-index The number of entries in the color lookup table, of the output device.
monochrome The number of bits per pixel in a monochrome frame buffer. For non-
monochrome devices, this value is zero.
resolution The resolution of the output device or the density of the pixels. The
common units for this property include dpi and dpcm.
grid The property that detects whether the output is in the grid or bitmap
format. Grid-based devices return a value of one; all other devices return a
value of zero.
• container. Use this class when you want to have a responsive container in which its min-width
property is changed at predefined media query ranges called breakpoints. The following are the
breakpoints which exist in Bootstrap:
o xs. Used for extra small devices (portrait phones). min-width is 0px.
o sm. Used for small devices (landscape phones). min-width is 576px.
o md. Used for medium devices (tablets). min-width is 768px.
o lg. Used for large devices (desktops). min-width is 992px.
o xl. Used for extra-large devices (large desktops). min-width is 1200px.
• container-fluid. Use this class when you want the grid to be spread across the full width of the
viewport.
In the container, you can create rows. A row is an element with the row class. Each row can include
columns. A column is an element with the col class.
The following code example demonstrates how to create a container with rows and columns:
<div class="container">
<div class="row">
<div class="col">
<h3>First Column</h3>
</div>
<div class="col">
<h3>Second Column</h3>
</div>
<div class="col">
<h3>Third Column</h3>
</div>
</div>
<div class="row">
<div class="col">
Second Row - First column content.
</div>
<div class="col">
Second Row - Second column content.
</div>
<div class="col">
Second Row - Third column content.
</div>
</div>
</div>
In the example above, a grid with two rows is created. Each row contains three equal-width columns in all
kind of devices (extra small, small, medium, large and extra-large).
It is possible to place different column sizes inside the same grid row. For example, the class col-4 defines
a column that takes four places and a col-2 column defines a column that takes only two places.
The basic CSS classes that available for columns are: col-1, col-2, col-3, col-4, col-5, col-6, col-7, col-8,
col-9, col-10, col-11, col-12. The number beside the col- defines how many grid columns the column will
spread across.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-37
The following code example demonstrates how to create a Bootstrap grid system having columns with
different width:
<div class="container">
<div class="row">
<div class="col-4">
<h3>4 Columns width</h3>
</div>
<div class="col-6">
<h3>6 Columns width</h3>
</div>
<div class="col-2">
<h3>2 Columns width</h3>
</div>
</div>
<div class="row">
<div class="col-3">
<h3>3 Columns width</h3>
</div>
<div class="col-6">
<h3>6 Columns width</h3>
</div>
<div class="col-3">
<h3>3 Columns width</h3>
</div>
</div>
</div>
In the example above, in the first row, the width of the first column is 33.33%, the width of the second
column is 50%, and the width of the third column is 16.67%. In the second row, the width of the first
column is 25%, the width of the second column is 50%, and the width of the third column is 25%.
It is possible to set the Bootstrap grid system to behave differently based on the device on which the
application is running. For small devices, use the col-sm class, for medium devices, use the col-md class,
for large devices, use the col-lg class, and for extra-large devices, use the col-xl class.
The following code example demonstrates how to create a responsive Bootstrap grid system:
@{
ViewBag.Title = "Bootstrap Grid System Example";
}
<h2>@ViewBag.Title</h2>
<h3>This page shows a Bootstrap grid system</h3>
<div class="container">
<div class="row">
<div class="col-12 col-md-4">
<h3>first row, first column</h3>
</div>
<div class="col-6 col-md-8">
<h3>first row, second column</h3>
</div>
</div>
<div class="row">
<div class="col-6 col-sm-4">
MCT USE ONLY. STUDENT USE PROHIBITED
09-38 Client-Side Development
In the example above, in the first row for medium, large and extra-large devices the width of the first
column is 33.33% and the width of the second column is 66.67%. In extra-small and small devices, the
width of the first column is 100% and the width of the second column is 50%. In the second row, for
extra-small devices, the width of each column is 50%, and for all other devices, the width of each column
is 33.33%.
Alignment
It is possible to vertically align the columns within the row in three main ways. To do so you need to add a
class to the row element:
• align-items-start. Aligns the columns to the top.
• align-items-center. Aligns items to the center.
• align-items-end. Aligns items to the bottom.
Also, it is possible to align the items horizontally inside the row, using the following classes:
• justify-content-start. Align columns to the start of the row.
• justify-content-center. Align columns to the center of the row.
• justify-content-end. Align columns to the end of the row.
• justify-content-around. Spread the empty space around the columns.
• justify content-between. Spread the empty space between the columns.
This example shows how to align columns vertically and horizontally inside rows:
Grid Alignment
@{
ViewBag.Title = "Bootstrap Grid System Example";
}
<h2>@ViewBag.Title</h2>
<h3>This page shows a Bootstrap grid system</h3>
<div class="container">
<div class="row align-items-center justify-content-center">
<div class="col">
Column aligned to center
</div>
<div class="col">
Column aligned to center
</div>
<div class="col">
Column aligned to center
</div>
</div>
</div>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-39
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use the Bootstrap Grid System“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD09_DEMO.md#demonst
ration-how-to-use-the-bootstrap-grid-system.
Parent Container
Parent container in context of flexbox module is
an HTML element that has other HTML elements
within it and has the property display: flex
applied. One of the main ideas of flexbox is that
the parent container can alter its children items
width, height, and order to fill the available space within it in diverse ways.
Parent container properties are:
• flex-direction: row | row-reverse | column | column-reverse;
Defines the direction in which the container’s children flow.
• flex-wrap: nowrap | wrap | wrap-reverse;
Flexbox items will always try to fit in one row or column. You can change this behavior and allow the
items to wrap to second line or column when the available space ends by using flexbox-wrap.
The following code example demonstrates how to use parent container properties to lay out the content
of a view:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<style>
.container {
display: flex;
justify-content: center;
align-content: center;
flex-wrap: wrap;
}
.item {
width: 200px;
background-color: grey;
}
</style>
</head>
<body>
<div class="container">
<div class="item">01</div>
<div class="item">02</div>
<div class="item">03</div>
<div class="item">04</div>
<div class="item">05</div>
<div class="item">06</div>
<div class="item">07</div>
<div class="item">08</div>
</div>
</body>
</html>
Child Items
Each direct child inside the flex container can override the behavior specified by the container by using
the properties described below:
• order: -n…0…n;
Allows to target individual items and change where they appear in the visual order.
• flex-grow: 0…n; default 0;
This defines the ability for a flex item to grow if necessary. It dictates what amount of the available
space inside the flex container the item should take up.
• flex-shrink: 0…n; default 0;
This defines the ability for a flex item to shrink if necessary. It determines how much the flex item will
shrink relative to the rest of the flex items in the flex container.
• flex-basis: length | auto;
This defines the default size of an element before the remaining space is distributed. The flex grow
and flex shrink are relative to flex basis.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 09-41
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<style>
.container {
display: flex;
justify-content: center;
align-items: center;
height: 800px;
}
.box {
width: 100px;
}
.box-4 {
order: -1;
align-self: flex-start;
}
.box-2 {
flex-grow: 2;
}
</style>
</head>
<body>
<div class="container">
<div class="box box-1">01</div>
<div class="box box-2">02</div>
<div class="box box-3">03</div>
<div class="box box-4">04</div>
<div class="box box-5">05</div>
<div class="box box-6">06</div>
<div class="box box-7">07</div>
<div class="box box-8">08</div>
</div>
</body>
</html>
MCT USE ONLY. STUDENT USE PROHIBITED
09-42 Client-Side Development
Objectives
After completing this lab, you will be able to:
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD09_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD09_LAK.md.
3. Run a task.
Review Questions
Question: What are the differences and similarities between Sass and Less?
Question: What tools and techniques would you use to adapt your web application for mobile
and tablet devices?
Best Practices
• Use Sass or Less to add features to CSS, such as variables, nested rules, functions, inheritance,
importing styles and operators which improve the maintainability of large and complex applications.
• Use task runners not only to compress or compile files, but to add a variety of additional behaviors
such as code quality tools and client-side unit testing.
• Use the viewport attribute to customize your web application’s display based on specifications of the
user’s web browser.
Module 10
Testing and Troubleshooting
Contents:
Module Overview 10-1
Lesson 1: Testing MVC Applications 10-2
Lesson 2: Implementing an Exception Handling Strategy 10-14
Lesson 3: Logging MVC Applications 10-24
Lab: Testing and Troubleshooting 10-31
Module Review and Takeaways 10-34
Module Overview
Software systems such as web applications are complex and require multiple components, which are often
written by different developers, to work together. Incorrect assumptions, inaccurate understanding,
coding errors, and many other sources can create bugs that result in exceptions or unexpected behavior.
To improve the quality of your web application and create a satisfying user experience, you must identify
bugs from any source and eliminate them.
Traditionally, testers perform most of the testing at the end of a development project. However, it has
recently become widely accepted that testing throughout the project life cycle improves quality and
ensures that there are no bugs in production software. You need to understand how to run tests on small
components of your web application to ensure that they function as expected before you assemble them
into a complete web application.
It is also important that you know how to handle exceptions while they occur. While an application is
running, you may encounter unexpected occurrences. It is important that you manage your exceptions
correctly and provide a good user feedback while avoiding leaking information about the application
structure.
Finally, by using logs throughout the application, you can monitor user activities that might lead to
unexpected issues and then you can find solutions to bugs, which you normally would be unsure how to
reproduce, by following flows which occurred on the production environment and finding additional
errors.
Objectives
After completing this module, you will be able to:
• Run unit tests against the Model–View–Controller (MVC) components, such as model classes and
controllers, and locate potential bugs.
• Build a Microsoft ASP.NET Core MVC application that handles exceptions smoothly and robustly.
• Run logging providers that benefit your applications and run them by using a common logging API.
MCT USE ONLY. STUDENT USE PROHIBITED
10-2 Testing and Troubleshooting
Lesson 1
Testing MVC Applications
A unit test is a procedure that instantiates a component that you have written, calls one aspect of the
functionalities of the component, and checks that the component responds correctly according to the
design. In object-oriented programming, unit tests usually instantiate a class and call one of its methods.
In an ASP.NET Core MVC web application, unit tests can instantiate model classes or controllers, and call
their methods and actions. As a web developer, you need to know how to create a testing project, add
tests to the testing project, and run tests in the testing project. The tests check individual aspects of your
code without relying on database servers, network connections, and other infrastructure. This is because
unit tests focus on the code you write.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the use of unit tests to improve the quality of code you write.
• List the principles of test-driven development (TDD).
• Describe how loosely-coupled components increase the testability and maintainability of your
application.
• Write unit tests against model classes and controllers.
• Describe how dependency injection allows components to be more easily testable.
• Run unit tests in Microsoft Visual Studio 2017.
• Use a mocking tool to automate mock creation in unit tests.
Note: Unit tests are important because they can be defined early in development.
Integration and acceptance tests are usually run later, when several components are approaching
completion.
1. Phase 1. The ProductController class is defined and the Test_Index_Action unit test is written. The
test checks that the Index action works with an integer parameter. When you call the Index action
with an integer, the action returns a View result action, that includes inside its model a collection that
includes the same number of Product objects. The test passes.
2. Phase 2. A developer modifies the Index action so that it returns a partial view. The developer
renames the action _Index to conform to the team’s partial view naming convention. The test fails
because the name has changed. The developer modifies the test so that it calls the renamed action
and the test passes.
3. Phase 3. A developer modifies the Index action by writing a different Language Integrated Query
(LINQ) to implement a new functional requirement. However, the developer makes a mistake in the
LINQ syntax. The Index action now returns a View result action with zero products on the model
regardless of the integer that is passed as a parameter. The test fails.
In phase 2, the test failure arose because the action name had changed, not because of a mistake in the
action code. The solution was to change the name of the action called in the test method, but this can
remind developers to alter calls to renamed actions throughout the solution.
In phase 3, the test failure arose because the developer misunderstood LINQ or made a typing error in the
LINQ syntax. The unit test highlighted the error as soon as it arose. This helps the developer focus on a
solution to resolve the test failure and ensure that new functionality does not cause failures in code
written earlier in the project.
3. Refactor. The developer cleans up the application code, removes duplicate code, removes hardcoded
values, improves readability, and makes other technical improvements. The developer reruns the test
to ensure that refactoring has not caused a failure.
The cycle is then repeated. In each iteration, the developer adds a small new element of the final
functionality with a corresponding test.
It is important that the code in your tests is treated as production code. It should be well thought out and
easy to read so that other developers can understand the test and quickly diagnose any test failures.
Test-Driven Development Principles
TDD is different from traditional approaches to application development. To use TDD effectively, you
must understand its fundamental principles:
• Write tests before code. In the TDD development cycle, you write the test before writing any code in
the application. This means the test must fail the first time it is run. You can understand the test as a
specification for the functionality you are building. By writing the test first, you ensure that you begin
with a thorough understanding of the problem you are trying to solve.
• Move in small steps. By breaking a large application down into small elements of functionality, you
can improve developer productivity. You can do this by giving a developer a small problem to solve
in each iteration. The developer can solve the simple problem quickly and understand all the possible
circumstances in which their code runs.
• Only write enough code to pass the test. In each iteration, do not be tempted to add extra code that
is not related to the test. For example, if you know that users will call an action with other parameters
than the ones in the test, do not write code to handle these parameters. Instead, during the next
iteration, write a second test for those parameters. Then write and refactor that code.
Developers can refer to tests as examples of how to use a class or method. This can increase developer
productivity because developers can view the code that demonstrates the method or class.
Notice that, in the test, the TestProduct double uses in-memory data set up during the Arrange phase. It
does not query the database. Therefore, you can test your code without relying on the network
connection to the database or the database server itself. This approach also ensures that the test runs
quickly, which is important because slow tests discourage developers from running tests regularly.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 10-7
Note: There are many other test frameworks available for MVC web applications, and you
can choose the one you prefer. Many of these frameworks can be added by using the NuGet
package manager, which is available in Visual Studio.
The following code is an example of a test for the Product model created previously:
// Act
float result = product.GetPriceWithTax(10);
// Assert
Assert.AreEqual(11, result);
}
}
This test is designed to check that the GetPriceWithTax method is working correctly.
A Repository Interface
public interface IProductRepository
{
IQueryable<Product> Products { get; }
Product Add(Product product);
Product FindProductById(int id);
Product Delete(Product product);
}
Note that the StoreContext is a class which inherits from the Entity Framework DbContext class, which is
injected through dependency injection and declared inside of the ConfigureService method. In fact, the
interface methods such as Add, Delete, and FindProductById just wrap methods from the DbContext
class such as Remove.
MCT USE ONLY. STUDENT USE PROHIBITED
10-10 Testing and Troubleshooting
Note: Working with the DbContext class was covered in Module 7, “Using Entity
Framework Core in ASP.NET Core”.
public FakeProductRepository()
{
List<Product> products = new List<Product>();
_products = products.AsQueryable();
}
The following code example shows how to test a controller action by using a test double:
// Act
var result = productController.Index() as ViewResult;
// Assert
Assert.AreEqual(typeof(List<Product>), result.Model.GetType());
}
}
Note that the test creates a new instance of the FakeProductRepository class and adds three Product
objects to it. In this case, you can run the test without setting any properties on the Product objects. This
context is passed to the ProductController constructor and the test proceeds to the Act and Assert
phases.
Note: When developing an ASP.NET Core application, it is possible to run Live Unit Testing.
This allows you to run unit tests as you code, ensuring the integrity of your code without having
to constantly run tests manually. You can find out more about Live Unit Testing here:
https://aka.ms/moc-20486d-m10-pg1
Demonstration Steps
You will find the steps in the section “Demonstration: How to Run Unit Tests“ on the following page:
https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD10_DEMO.md#demonst
ration-how-to-run-unit-tests.
MCT USE ONLY. STUDENT USE PROHIBITED
10-12 Testing and Troubleshooting
Note: Popular mocking frameworks for ASP.NET Core MVC include Moq and NSubstitute.
These frameworks are available in NuGet and there are additional alternatives. Choose the
framework that best suits your testing needs.
• Mocking objects with multiple interfaces. In some tests, where there are several dependencies
between classes of different types, you must create many mock objects of different classes. In such
situations, it is tedious to manually code many mock objects. Mocking frameworks can help by
automatically generating the objects from the interfaces that you specify.
In each unit test, you are interested in a small and specific area of functionality. Some properties and
methods of an interface will be crucial to your test, while others will be irrelevant. A mocking framework
enables you to specify irrelevant properties and methods in a given test. When the framework creates a
mock object for your test, it creates stubs for the irrelevant properties and methods. A stub is a simple
implementation with little code. In this way, the mocking framework frees you from having to implement
all the requirements of the interface laboriously.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 10-13
The following code is an example of a controller unit test by using the Moq framework:
// Act
var result = productController.Index() as ViewResult;
// Assert
Assert.AreEqual(typeof(List<Product>), result.Model.GetType());
}
}
In this example, you can see a unit test that leverages the strengths of a mocking framework. Unlike the
previous controller test, in this test, you do not need to create a testing double for IProductRepository.
Instead, by using the mock object exposed by the Moq framework, you can set up a fake class that returns
a list of products that you defined.
MCT USE ONLY. STUDENT USE PROHIBITED
10-14 Testing and Troubleshooting
Lesson 2
Implementing an Exception Handling Strategy
Unexpected events are likely to occur from time to time in any complex system, including MVC web
applications. Occasionally, such unexpected run-time events cause an error. When this happens, ASP.NET
Core or the .NET Framework generates an exception, which is an object that you can use to diagnose and
resolve the error. The exception contains information that you can use to diagnose the problem.
Exceptions that are not handled in your code will cause the web application to halt and an error message
to be displayed to the user. As a web developer, you need to know how to detect, handle, and raise
exceptions, and identify the cause of the problem. Visual Studio provides a broad range of tools for
debugging exceptions and improving the robustness of your code.
Lesson Objectives
After completing this lesson, you will be able to:
• Explain how to raise and catch exceptions.
• Run your application in multiple environments.
• Utilize middleware to handle exceptions in an ASP.NET Core MVC Application.
• Configure error handling in an ASP.NET Core application.
In this example, you will catch any exception of the ArgumentNullException type. If an exception occurs
of a type other then ArgumentNullException, the exception will not be caught.
Best Practice: It considered best practice to always end exception classes with the suffix
Exception. This lets the developer immediately understand why this class exists and how to use it
correctly.
In order to resolve this, you can use the IHostingEnvironment service. This service is frequently injected
into the Configure method of the Startup class. The IHostingEnvironment interface exposes useful
methods in the form of IsDevelopment, IsStaging, IsProduction, and IsEnvironment(*Environment
Name*). You can use these methods to determine the currently running environment.
The following code is an example of using the IHostingEnvironment service:
app.UseMvcWithDefaultRoute();
In this example, you can see that in a Development environment, the UseDeveloperExceptionPage
method is called, and in Staging, Production, and ExternalProduction environments, the
UseExceptionHandler with a route of /error is called instead. The UseDeveloperExceptionPage and
UseExceptionHandler methods will be covered in the third topic of this lesson, “Configuring Error
Handling”.
Note that, in this example, Configure is overridden in the custom ExternalProduction environment.
In this example, you can see that while in Development mode, the application will use uncompressed
JavaScript and CSS files. In Production and Staging, the application will serve bundled and minified files
instead.
Note: The appsettings.json file was described in Module 7, "Using Entity Framework Core
in ASP.NET Core".
Using Profiles
While most servers run on a single consistent environment, however, in a development environment that
is not the case. As a developer, you will often be tasked with checking a variety of issues that can be
reproduced only on specific environments, and you will often end up switching environments many times.
In order to facilitate this, you can set up various development environments within Visual Studio. To
configure environments for your project, right-click the project file, select properties from the menu, and
then select the Debug menu.
Using Visual Studio, you can create a new runtime profile for the application. Each profile has a unique
name. In addition, each profile has a commandName property, which is used to determine the web
server to launch. Common values for the commandName property include IIS Express, which specifies
that IIS Express will be launched, and Project, which specifies that Kestrel server will be launched. The
differences between IIS Express and Kestrel will be covered further in Module 14, "Hosting and
Deployment". In the profile, you can also add and change environment variables. These variables
determine various behaviors at run time, and of particular importance is the
ASPNETCORE_ENVIRONMENT variable. By setting the value of this key, you can set the environment in
which the application is run. In a profile, you can also specify if a browser will open on your application
whenever you run it from inside Visual Studio.
Note that after you have added a profile, it can also be seen inside of the project's Properties section
inside of the launchSetting.json file. You can also directly modify the launchSettings.json file yourself to
create new developments profiles.
To run the development profile, you can use the debugging menu in the Visual Studio toolbar. By clicking
the arrow next to the run button, a drop-down list will appear. From this list, you can select the
development profile. You can then run the application using the selected profile. This allows you to switch
development environments on the fly, allowing for a fast response time and the ability to test
environment changes.
Another easy way in which you can run your application in various environments is by using the
command line to set the ASPNETCORE_ENVIRONMENT variable, and then execute the dotnet run
command to run the application. The dotnet run command will run an application by using the first
profile inside of the launchSettings.json file that uses the commandName property of the project. By
setting ASPNETCORE_ENVIRONMENT beforehand the project will run by using the environment set in
the ASPNETCORE_ENVIRONMENT. This configuration will persist only within that specific command line
window. Note that this will only work if the ASPNETCORE_ENVIRONMENT variable is not specifically set
inside the profile configuration. The dotnet run command will be covered in module 14, "Hosting and
Deployment".
The following code is an example of running the application by using the dotnet run command:
dotnet run
Note: Note that the setting of the ASPNETCORE_ENVIRONMENT variable in Linux and
macOS may be slightly different, but the behavior will be the same.
MCT USE ONLY. STUDENT USE PROHIBITED
10-20 Testing and Troubleshooting
app.UseMvcWithDefaultRoute();
Note that, in order to implement a controller as an exception handler, you will need to call the UseMvc or
the UseMvcWithDefaultRoute middleware. Alternatively, if you prefer using a static HTML file, you can
use an exception handler that redirects to a static file alongside the UseStaticFiles middleware.
As a general rule, you will want to keep this page as static as possible since if an error handling controller
throws exceptions, a generic server error page will be displayed instead. While creating controllers and
views for displaying errors, ensure that you keep the logic simple.
The following code demonstrates a controller that functions as an exception handler:
Best Practice: Avoid using HTTP method attributes inside error handlers because the
request might be of different types, which could result in exceptions not getting handled.
You are not limited to calling just the UseStatusCodePages middleware for handling HTTP status codes.
The following methods are also available:
• UseStatusCodePagesWithRedirects. Allows sending a redirection URL to the client. This new URL
will be used instead of the original one. Using the {0} parameter in the URL can be used to set status
codes.
• UseStatusCodePagesWithReExecute. Similar to redirection. However, this redirection occurs directly
in the pipeline without affecting the client.
Handling Exceptions in the Server
Sometimes, in most ASP.NET Core MVC applications, an exception might occur on the web server, instead
of inside your ASP.NET Core MVC application code, and not be caught inside the application, but caught
by the server instead. In this case, if the exception is caught before the response headers are sent, a
response will be sent with no body and a 500 status code (internal server error). On the other hand, if the
response headers were sent before the server catches the exception, the connection is immediately closed
by the server. It is therefore important to avoid any exceptions leaving your application because such
errors can be very difficult to trace.
Handling Model State Errors
Another option for handling exceptions is through the model. As part of the action of the controller, the
model is validated as is covered in Module 6, "Developing Models". By using the IsValid property on the
model inside the controller action, you can stop further processing invalid data and take appropriate
actions on the controller.
Using Exception Filters
You can also configure a special filter called exception filter (Filters were previously covered in Module 4,
"Developing Controllers"). You can create an exception filter by creating a class that inherits from the
ExceptionFilterAttribute class. This filter can then be applied as an attribute to specific actions and
controllers, which allows for handling specific exception cases.
The class that implements the exception filter class should override the OnException method inside it.
You can then use the ExceptionContext parameter to retrieve or set the current exception, you can
update the Result property, potentially changing the view or the response context, and you can also set
the ExceptionHandled property to stop the propagation of the exception (If it is left as false, the
exception will keep propagating).
The following code is an example of an exception filter:
{
var result = new ViewResult { ViewName = "InvalidModel" };
context.Result = result;
context.ExceptionHandled = true;
}
}
}
In this example, you can see an error filter that redirects to the InvalidModel view whenever an exception
occurs with an invalid model.
Best Practice: Note that as a general rule, creating error handling middleware is
preferable, and exception filters are best left for edge cases.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Configure Exception Handling“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD10_DEMO.md#demonst
ration-how-to-configure-exception-handling.
MCT USE ONLY. STUDENT USE PROHIBITED
10-24 Testing and Troubleshooting
Lesson 3
Logging MVC Applications
Throughout an application’s life span, unexpected exceptions might occur on a daily basis. While it is
usually reasonably easy to find and debug on development environments, the same cannot be said about
production environments. In most cases, as a developer, you will not be able to directly work in the
production environment. In order to make it easier to find bugs, you need to add regular logs throughout
the application to monitor the flow of the application and point out exceptions. Doing this can help you
find issues in your applications and deal with bugs that were not caught before production.
Lesson Objectives
After completing this lesson, you will be able to:
• Decide the type of logging to be used in your application.
• Add logging providers to allow your application to log exceptions and important information as it
runs in a specified manner.
• Call logging methods throughout your application to log important events.
Logging Exceptions
Exceptions that you face during development can
be investigated and debugged by using the
Visual Studio debugging tools. In an ideal
situation, no exceptions would arise when your
web application is complete and deployed for
users over the Internet. However, in the real
world, unforeseen circumstances arise resulting in
exceptions. For example, database failures,
network issues, and configuration errors in any
part of the system can cause exceptions.
You have already seen how to present a branded
and informative custom error page to users when
exceptions occur. It is also appropriate to log the exceptions in a production web application, so the
administrators and developers can assess the extent of the problem, remove the cause, and improve the
robustness of the code.
By storing logs on a database, you can store and divide the logs in a way that can make them easy to
peruse However, this requires a steady connection to the database and appropriate database
administration. Also, these logs may not be easily accessible to the developers on a daily basis.
By logging to email, you can ensure that developers are informed very quickly and can handle the
problem almost as soon as it occurs. However, it can also easily result in repeated emails for repeated
issues, an excess of emails being sent, losses over network connections, and a difficult-to-manage email
inbox.
Ensure that you discuss these options and any additional options with your team and decide which option
is most appropriate for your application. Remember that you can use more than one method if it fits your
application. For example, you could log to files on a daily basis while also mailing developers whenever
critical exceptions occur.
Where to Write Error Logging Code
When you decide where to write code that logs errors, you should consider that errors might arise in
almost any part of your application. You should choose an approach that enables you to write error
logging code once that will run for any exception anywhere in your application.
For example, it is not appropriate to write error logging code in individual try/catch blocks. If you do this,
you will have to create a try/catch block for every procedure in your application and write duplicate
logging code in the catch block.
A far more effective approach is to log by using middleware. By logging exceptions by using middleware,
you can ensure that problematic code is properly logged and traced. This allows for a singular point of
failure to handle all errors in the application.
Using Third-Party Logging Tools
Because exception logging is a very common functional requirement for web applications, there are many
third-party solutions that you can choose if you do not want to write your own logging code. Many of
these are available within Visual Studio from the NuGet package manager.
To add logging, you will need to add a call to the ConfigureLogging method as part of the
CreateDefaultBuilder pipeline. The ConfigureLogging method exposes two parameters, a parameter of
the WebHostBuilderContext type, which can be used to retrieve the IHostingEnvironment, and an
ILoggingBuilder parameter, which is used to set up the logger providers and configurations.
In the ConfigureLogging method, you will need to add the logging provider, which will enable logging
by using the specific provider. You can log to more than one provider at the same time. ASP.NET Core has
several built-in providers, which include:
• Console. Logs the message to the application's console window.
• Debug. Logs the message to the application's debug window.
• EventSource. Uses event tracing API to store the event, behavior differs between operating systems,
and it may not work on all platforms.
• EventLog. Logs the message to the windows event log. Is exclusive to Windows.
• TraceSource. Creates a trace source that can be listened to with a variety of trace listeners.
• AzureAppServices. Creates logs that integrate with the Azure platform.
The providers can be added by calling the add*Provider Name*() method. After you call this method,
the provider is set up for logging throughout the application.
The following code is an example of using the ConfigureLogging method:
In this example, you can see that the application will write logs to the console. While running the
application from Visual Studio, this can be found in the Output tab under ASP.NET Core Web Server.
{
_logger.LogInformation("Adding an entry to the logger.");
return Content("Result from controller");
}
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvcWithDefaultRoute();
Best Practice: While it is possible to give the ILogger any class, it could result in logs
becoming confusing to use. It is recommended to always create a logger for the current class.
Log Levels
In an ASP.NET Core MVC application, you will often want to keep track of a wide variety of events. At
times, you might want to keep track of every individual DB request or every single call to the server, while
in other times you might want to track major flows being called and exceptions that occur. For this
purpose, you can use log levels to control which logs will be displayed in your application. By setting a log
severity, only logs that match the severity or of a higher severity will be displayed.
The log levels and their severity values are as follows:
• Trace (0). These should be used when trying to debug specific things and are often used to track very
large amounts of data. By default, trace logs are never displayed and should not be used outside of a
development environment.
• Debug (1). Logs of this level are frequently used for development environments and keep track of
large amounts of data. Logs of this level should not be logged outside of development environments.
• Information (2). Logs of this level should be used at important points throughout the application to
ensure the application is running correctly but should avoid getting to a point where they affect
system performance. Information logs are frequently, but not always. enabled in production
environments.
• Warning (3). Logs of this level should be used whenever unexpected flows occur. Exceptions that are
handled or flows with missing data will often log a warning. These logs should always appear in a
development environment, which allows you to find problematic flows.
• Error (4). Logs of this level should occur whenever exceptions cannot be handled. These should be
cases in which a flow has broken down but did not result in application wide issues. These logs should
always be kept.
MCT USE ONLY. STUDENT USE PROHIBITED
10-28 Testing and Troubleshooting
• Critical (5). Logs of this level should be used to signify application wide failures. Failures such as an
invalid database connection and a failure on the web server should be tracked with critical logs.
To call logs of different levels, you can use the Log*level* method. For example, to create an information
level log, you should use LogInformation, and in order to create a critical log, you should use
LogCritical.
The following code is an example of log levels:
Log Event ID
An additional measure that can enable tracking specific errors is using log event ID, which is an optional
parameter that you can add to as part of a call to a log method in order to track specific event cases.
Commonly, these event IDs are declared as constants and used to further distinguish the events.
By supplying an integer as the first parameter to the logging method, you can assign that event to use the
specified event ID. This can help find specific events later when trying to find issues.
The following code is an example of using event ID in logs:
Log Event ID
const int CALL_INDEX = 1000;
Best Practice: A best practice is to store event IDs as enums or constants. Doing so allows
you to quickly switch the ID if required. You should not provide hard coded IDs as it is difficult to
maintain and understand the significance behind it.
Logging Exceptions
Another useful parameter that can be accepted by the various logging method is exceptions. A log can be
used to directly display exceptions. This is useful for handling exceptions that occur in try/catch blocks
because later you will be able to see the exception details in the log. This can be very useful for finding
issues that occur in production. The parameter for the exception should be the first parameter if event ID
is omitted, or It will be the second parameter if event ID is used.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 10-29
Exception Logging
public IActionResult Index()
{
try
{
int x = 3;
x -= 3;
int result = 30 / x;
}
catch (Exception ex)
{
_logger.LogError(INVALID_DIVISION, ex, "An error occurred while dividing!");
}
return Content("Result from controller");
}
Configure Logging
You can also utilize the appsettings.json file to set various details for your logging. It is most commonly
used to set log levels. This can easily allow you to set a different log level in different environments. To
load the configuration for logging, you will need to call the AddConfiguration method on the
ILoggingBuilder parameter and supply it with a configuration by using the GetSection method on the
Configuration property of the WebHostBuilderContext parameter. After this is done, the logging
configuration will be loaded.
The following code is an example of loading logging configuration:
The following code is an example of an appsettings.json file that contains logging configuration:
Best Practice: Note that the configuration section is not required to be named "Logging",
however, by using another name, it can cause confusion to developers working with the
configuration file.
Third-Party Provider
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddFile("myLog.txt");
})
.UseStartup<Startup>();
Note: Note that various third-party libraries may have additional configuration steps. You
should consult the provider's documentation to find out additional details. Most third-party
libraries will also expose extension methods that allow you to easily add the provider. In cases
where they do not, you can use the AddProvider method instead.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Log an MVC Application“ on the following
page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD10_DEMO.md#demonst
ration-how-to-log-an-mvc-application.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 10-31
Objectives
After completing this lab, you will be able to:
• Test an ASP.NET Core MVC application.
• Add exception handling for the different environments.
• Add logging to an ASP.NET Core MVC application.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD10_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD10_LAK.md.
Review Question
Question: You want to ensure that the PhotoController object passes a single Photo object to
the Display view, when a user calls the Search action for an existing photo title. What unit tests
should you create to check this functionality?
Tools
MSTest, NUnit, xUnit. These are unit testing frameworks. They allow setting up unit test projects for
applications.
Moq, NSubstitute. These are mocking frameworks. They automate the creation of test doubles for unit
tests.
JSNLOG, elmah.io, Loggr, NLog, Serilog. They are third-party providers which can grant the application
more options for logging.
Best Practices
• If you are using TDD or extreme programming, define each test before you write the code that
implements a requirement. Use the test as a full specification that your code must satisfy. This
requires a full understanding of the design.
• Investigate and choose a mocking framework to help you create test double objects for use in unit
tests. Though it may take time to select the best framework and to learn how to code mock objects,
the time you invest will be worth it over the life of the project.
• Do not be tempted to skip unit tests when under time pressure. Doing so can introduce bugs and
errors into your system and result in more time being spent debugging.
Module 11
Managing Security
Contents:
Module Overview 11-1
Lesson 1: Authentication in ASP.NET Core 11-2
Lesson 2: Authorization in ASP.NET Core 11-18
Lesson 3: Defending from Attacks 11-28
Lab: Managing Security 11-45
Module Review and Takeaways 11-47
Module Overview
Since web applications are normally targeted towards users utilizing only a browser to use the application,
there are likely to be far more users than in the case of installed applications. However, the open nature of
a web application means security must always be at the forefront of your mind when building them. As
part of security, you must decide which users can perform what actions, all while protecting users and
your application from malicious third parties with possible access to your application.
Authentication is the act of utilizing several parameters to make sure that a user is who they claim to be.
By implementing authentication, you can ascertain who a user is and provide them with appropriate
content while utilizing your applications.
Authorization is the process where an already authenticated user in the application can be granted access
to specific actions or resources. By utilizing authorization, you can prevent users from accessing sensitive
material not intended from them or from performing actions which they should not be able to.
Finally, at some point in its lifespan, your applications may come under attack by malicious users. These
can vary in means and intent, but the cost of being undefended can be great. You may lose potential
users who are affected, valuable data could be erroneously changed, and in the worst cases the entire
application may become unusable. Solutions to some of the most popular attacks will be reviewed in this
module.
Objectives
After completing this module, you will be able to:
Lesson 1
Authentication in ASP.NET Core
Authentication is a process by which a user can be identified within an application, and undesirable users
can be prevented from accessing important data. ASP.NET Core provides you with ASP.NET Core Identity
which can be used to set up authentication in your ASP.NET Core MVC application.
In this lesson, you will learn why authentication is required and how by using ASP.NET Core Identity you
can add authentication to your applications. Furthermore, you will also learn about additional ways in
which you can further extend your authentication, allowing you to use external login providers and
external storage providers.
ASP.NET Core Identity is a powerful tool which helps you to manage both authentication and
authorization, throughout your application. With ASP.NET Core you will be able to save your application
users in a Microsoft SQL Server database by using tools you have learned in module 7, "Using Entity
Framework Core in ASP.NET Core". After understanding the basic structure of an ASP.NET Core Identity
application, you will be shown how to further configure the authentication process to protect your
application from threats.
In addition, you will also learn about various ASP.NET Core providers which can be used to allow
additional login options for your users, such as adding log in through external providers, as well as
allowing you to further customize storage options for your authentication system.
Lesson Objectives
After completing this lesson, you will be able to:
• Explain why authentication is needed.
• Create an ASP.NET Core MVC application utilizing ASP.NET Core Identity.
• Utilize ASP.NET Core Identity in MVC controllers.
• Configure ASP.NET Core Identity behavior.
• Describe the use of external providers with ASP.NET Core Identity.
content. In addition, authentication can also be used to acquire personal data about website users. This
can then be used to provide them additional services from your company.
A common type of authentication is authentication by username and password. The user will need to
know both the unique username they use in the application and the related password. This is, however,
not the only possible option for authentication and additional methods exist such as fingerprints, phone
confirmation, email confirmation, and more.
When a single type of authentication is used, it is called single-factor authentication. For the most part it
mainly uses username and password. When multiple forms of authentication are required, it is called
multi-factor authentication instead and usually involves both username and password, as well as
additional forms of authentication.
Creating a proper authentication environment is crucial, as it can add an important layer of security to
your applications. Modern applications often involve sensitive data, and as such, require that it is only
provided to appropriate users. Without authentication, data is accessible to all users, allowing malicious
users to acquire sensitive information.
Most forms of authentication these days use what is known as token-based authentication. When the
initial authentication is performed, a token is generated by the server and sent to the client. The client
then sends the token again in every future request, allowing the server to confirm that it is the same user.
If the token is incorrect or expired, the user will need to authenticate again.
ASP.NET Core Identity also exposes various services, which you can inject throughout your application, for
managing system users and performing login and logout operations. This makes adding it to your
application simple and ensures that your application utilizes the various technologies you have learned
previously in this course.
The following code is an example of inheriting from the default IdentityUser class:
In this example, you can see the WebsiteUser class, which exposes the UserHandle property. This
property could be used to determine how the user's name is displayed during online operations, without
revealing the username used for login.
Best Practice: Note that only a password hash should ever be stored for ASP.NET Core
Identity users. Real passwords should never be stored, and it is a massive security exploit to do so.
IdentityDbContext
public class AuthenticationContext : IdentityDbContext<WebsiteUser>
{
public AuthenticationContext(DbContextOptions<AuthenticationContext> options) :
base(options)
{
}
}
In this example, you can see that the AuthenticationContext class inherits from IdentityDbContext and
provides the WebsiteUser custom user class instead of the default IdentityUser. It is otherwise identical
in structure to DbContext.
Best Practice: You can use the IdentityDbContext to manage additional datasets, in the
same way as DbContext. It adds additional functionality on top of DbContext and isn't intended
to be used exclusively for authentication.
You will also need to add a call to the AddDefaultIdentity<*User*> () method. The
AddDefaultIdentity method expects the class which will be used to manage users in the application. This
can be either a custom class which inherits from IdentityUser or it can be IdentityUser itself.
Note: If you do not wish to use Entity Framework in your application you should not make
a call to AddEntityFrameworkStores.
Identity Services
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<AuthenticationContext>(options =>
options.UseSqlite("Data Source=user.db"));
services.AddDefaultIdentity<WebsiteUser>()
.AddEntityFrameworkStores<AuthenticationContext>();
}
In this example, you can see that the services for ASP.NET Core Identity are instantiated by the call to the
AddDefaultIdentity method. This application uses the WebsiteUser class to manage users. It then
connects the identity services to the IdentityDbContext provided by AuthenticationContext.
Finally, in the Configure method of the startup class, you will need to load the middleware for handling
the authentication process in order to enable Identity. You can do this by calling the UseAuthentication
method of the IApplicationBuilder object. It is important that you call UseAuthentication before the
call to UseMvc. This is to ensure that the authentication will always be ready for use within our ASP.NET
Core MVC Controllers.
The following code is an example of enabling identity middleware:
Identity Middleware
public void Configure(IApplicationBuilder app, AuthenticationContext authContext)
{
authContext.Database.EnsureDeleted();
authContext.Database.EnsureCreated();
app.UseAuthentication();
app.UseMvcWithDefaultRoute();
Performing Login
One of the most common flows in the
authentication process is logging in. Logging in is
the process of the user entering their credentials
in a dedicated form in an application and the
server confirming that the credentials are valid
before sending an authentication token to the
client, which will be used to check the user
identity on future requests. To perform this initial
connection, you will need to be able to handle a login request on the server.
To facilitate login, you should create a controller named AccountController. While you can use a
different name for it, much of the default logic in ASP.NET Core MVC relies on AccountController and
using a separate name will require additional work.
In order to be able to perform login operations, you will need to inject the SignInManager<T> service,
where T is the class which inherits from IdentityUser as described in the previous topic. The
SignInManager class exposes valuable methods for performing authentication operations. For example,
to login you will need to call the PasswordSignInAsync(*username*, *password*, *isPersistant*,
*lockoutOnFailure*) method.
The PasswordSignInAsync method is designed to authenticate a user and create a session, while also
updating the client with the authentication token required for future requests. It accepts parameters as
follows:
*username*. The username used by the user. String variable.
*password*. The users non-hashed password, the non-hashed password should never be stored
anywhere as that is a security exploit. String variable.
*isPersistant*. Determines if the cookie created by the sign in method persists after the browser is closed.
Boolean variable.
*lockoutOnFailure*. Determines if the user will be locked out if the login attempt is unsuccessful.
Lockout can be used to prevent malicious attempts to login at the cost of inconveniencing the user if the
wrong password is typed. Boolean variable.
Once the authentication method succeeds, you can then redirect the user to an appropriate URL. In
particular, a handy feature of ASP.NET Core Authorization, which will be covered in further detail in the
next topic, "Authorization in ASP.NET Core", is the addition of a RedirectUrl parameter in the query
string. By using this parameter, you are able to navigate to the original destination page which requires
authentication when it is successful.
You will also need to create a view which will call the login method on the controller. Note that the view
should use a different model then the one used by the DatabaseContext. Many properties in the
UserIdentity are not likely to be used, while other, more sensitive properties such as passwords should be
present in the model used by the view, while they should be stored in a hashed format on the class
derived from UserIdentity.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-7
Best Practice: You may sometimes find that you require separate models for server data
and client data. A common example for this is when using password. While the user will need to
input his password on the client, it should not be stored within the database. As such, you will
need a client-side model which does store a password, and a server-side model which does not
retain the password. To differentiate this, you can name models which are intended to be used
on the client side with ViewModel at the end. This will make them easily visible as models
intended for usage within the view.
The following code is an example of a ViewModel which can be used for login:
Login ViewModel
public class LoginViewModel
{
public string Username { get; set; }
public string Password { get; set; }
}
Note that the password in this view model will not be hashed and should not be saved.
The following code is an example of a view for login:
Login View
@model Authentication.Models.LoginViewModel
<h2>Login</h2>
<div>
<form method="post" asp-action="Login">
<div>
<label asp-for="Username">Username</label>
<input asp-for="Username" />
</div>
<div>
<label asp-for="Password">Password</label>
<input asp-for="Password" />
</div>
<input type="submit" value="Login" />
</form>
</div>
Login Method
public class AccountController : Controller
{
private readonly SignInManager<WebsiteUser> _signInManager;
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(model.Username,
model.Password, true, false);
if (result.Succeeded)
{
if (Request.Query.Keys.Contains("ReturnUrl"))
MCT USE ONLY. STUDENT USE PROHIBITED
11-8 Managing Security
{
return Redirect(Request.Query["ReturnUrl"].First());
}
else
{
return RedirectToAction("Index", "Home");
}
}
}
return View();
}
}
As you can see in this example, there is an AccountController class which receives a SignInManager
instance with the WebsiteUser type through dependency injection. Whenever a POST request is made for
the Login method, the model state is validated, and if it is valid, the username and password are used to
perform sign in. If the sign in is successful, it will check if a ReturnUrl is available. If it exists, it will redirect
to the originally intended page. Otherwise, it will navigate to the home page. In all other cases, it will
reload the login view.
Note: Note that since PasswordSignInAsync is an async method, you will need to wrap
the method in a Task, and use await to determine if the login is successful.
Performing Logout
Occasionally, users that are logged in to your application will wish to logout of the application. This can
be for a variety of reasons, such as switching to another user or preventing access to their account from
potentially malicious sources nearby. To facilitate this and to allow users to log out at their discretion, you
will want to add logic for handling logout requests.
Similarly, to login, logout should also be on the same controller, although it is possible to separate them if
it makes more sense for your application. It also utilizes the SignInManager service to perform the
logout.
An additional step you should take before performing a logout is to check if there is a currently
authenticated user logged in to the current session. This can be done by using the User property of the
Controller class. The User property exposes various properties relating to the current user in the session.
To check if a user has been successfully authenticated, you can check the IsAuthenticated property under
the Identity property in the User. This will return the current authentication status and you can use it to
make decisions on how to handle the user throughout your application.
To actually perform a logout, you will need to call the SignOutAsync() method from the
SignInManager. This method clears the stored identity tokens for the user, ensuring that future calls will
not be on the current user, unless a new login is performed.
The following code is an example of a logout view:
Logout View
<a asp-action="Logout" asp-controller="Account">Log out</a>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-9
Logout method
public IActionResult Logout()
{
if (User.Identity.IsAuthenticated)
{
_signInManager.SignOutAsync();
}
return RedirectToAction("Index", "Home");
}
Note that in this example a check is made to see if the current user is authenticated before performing
the sign out. This is done by checking the User.Identity.IsAuthenticated.
Register ViewModel
public class RegisterViewModel : LoginViewModel
{
public string UserHandle { get; set; }
}
Note that since the registration process also requires the Username and Password properties, it is logical
to extend the LoginViewModel class.
The following code is an example of a view for registering new users:
Registration View
@model Authentication.Models.RegisterViewModel
<div>
<form method="post" asp-action="Register">
<div>
<label asp-for="Username">Username</label>
<input asp-for="Username" />
</div>
<div>
<label asp-for="Password">Password</label>
<input asp-for="Password" />
</div>
<div>
<label asp-for="UserHandle">Display Name</label>
<input asp-for="UserHandle" />
MCT USE ONLY. STUDENT USE PROHIBITED
11-10 Managing Security
</div>
<input type="submit" value="Register" />
</form>
</div>
Register Method
public class AccountController : Controller
{
private readonly SignInManager<WebsiteUser> _signInManager;
private readonly UserManager<WebsiteUser> _userManager;
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
WebsiteUser user = new WebsiteUser
{
UserHandle = model.UserHandle,
UserName = model.Username,
};
In this example, you can see that both SignInManager and UserManager are injected in the constructor.
When performing the Register action, you can see that once the ModelState is validated, a new
WebsiteUser is created and a password property is not filled in. Afterward, the CreateAsync method of
the UserManager is called with the new user and password, which will be hashed. Finally, if the user is
created successfully, the Login method will be called.
By using the FindByNameAsync(*name*) method on the UserManager object and providing it with the
value of the Name property on User.Identity, the UserManager will retrieve the user, if it is found.
Afterward, you are able to extract properties off the user class you selected and apply them to the page
model as is needed.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-11
The following code is an example of a view model for displaying the user:
The following code is an example of a view which displays custom IdentityUser properties:
The following code is an example of a controller retrieving the properties for IdentityUser:
In this example, you can see that this action will only display for authenticated users. Furthermore, it then
attempts to retrieve the user for the currently logged in user with the WebsiteUser custom class which
inherits from IdentityUser. It then uses it to create a new view containing properties from the
WebsiteUser class, including UserHandle which is not present on the default UserIdentity class.
MCT USE ONLY. STUDENT USE PROHIBITED
11-12 Managing Security
Configuring IdentityOptions
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<AuthenticationContext>(options =>
options.UseSqlite("Data Source=user.db"));
services.AddDefaultIdentity<WebsiteUser>(options =>
{
/* Configure Identity Options options here */
})
.AddEntityFrameworkStores<AuthenticationContext>();
}
User Settings
User settings are used to determine a variety of configurations regarding the registration of users in the
system. Settings here can be used to require a unique email address for every configured user or to
determine which letters and symbols can be used in the application.
You can use the User property of IdentityOptions to configure settings relating to the IdentityUser.
The following code is an example of configuring settings related to the identity user:
User Settings
services.AddDefaultIdentity<WebsiteUser>(options =>
{
options.User.RequireUniqueEmail = true;
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 -._@+";
})
.AddEntityFrameworkStores<AuthenticationContext>();
In this example, you can see that all users in the application will require a unique value for the Email
property to be set and that only characters from the following:
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 -._@+" can be used
in the Username property.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-13
Lockout Settings
An important feature which is frequently present in various systems involving authentication is the
concept of user lockout. When the application detects multiple attempts to log in with the same
username but invalid passwords, it can be set to lock the user for future logins for a period of time. This
can prevent malicious sources from logging in to the system by utilizing brute force tactics which involve
attempting to guess a password, by vastly increasing the time between attempts rendering them unviable.
To apply the lockout, you will need to make sure the lockoutOnFailure parameter in the call to the
PasswordSignInAsync method is true, or the lockout settings will be ignored.
You can use the Lockout property of the IdentityOptions to configure settings relating to the lockout.
The following code is an example of enabling lockout on login:
Enabling Lockout
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password,
true, true);
Lockout Settings
services.AddDefaultIdentity<WebsiteUser>(options =>
{
options.Lockout.AllowedForNewUsers = true;
options.Lockout.MaxFailedAccessAttempts = 3;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(1);
})
.AddEntityFrameworkStores<AuthenticationContext>();
In this example, you can see that lockout can occur for new users who have not yet logged in successfully.
The users will be locked out for one minute and users can attempt to log in up to three times before
being locked out.
Password settings
Secure passwords are a requirement in modern authentication environments and it is the developer's duty
to assign a password requirement that is appropriate for the specific environment. While in many cases a
default password can be used, sometimes you will find that you need to customize it further.
In order to configure password settings and complexity, you can access the Password property of the
IdentityOptions.
The following code is an example of password configuration in Identity:
Password Settings
services.AddDefaultIdentity<WebsiteUser>(options =>
{
options.Password.RequiredLength = 10;
options.Password.RequiredUniqueChars = 3;
options.Password.RequireDigit = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
})
.AddEntityFrameworkStores<AuthenticationContext>();
MCT USE ONLY. STUDENT USE PROHIBITED
11-14 Managing Security
In this example, you can see that passwords for this application will be at least 10 characters long, require
at least three different characters to be used, require a numeric character, as well as at least one symbol
character. Finally, at least one uppercase character will be required. Lower case characters will not be
required and can be omitted.
Sign In
In modern applications, you may sometimes require that users provide additional details that allow you to
access them for identity verification. This can be helpful in notifying them of potential attempts to
compromise their accounts.
The SignIn property of the IdentityOptions can be used to require the user to confirm their email or
phone number in the application. Login will be impossible until successful confirmation occurs.
Note: Setting up both email and mobile number confirmation requires additional work and
will not be covered in this course.
SignIn Settings
services.AddDefaultIdentity<WebsiteUser>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
options.SignIn.RequireConfirmedPhoneNumber = false;
})
.AddEntityFrameworkStores<AuthenticationContext>();
In this example, you can see that the application requires users to confirm registration via email.
Confirming phone numbers is not required.
Cookies Settings
An additional type of setting that affects Identity is the cookie configuration. By calling the
ConfigureApplicationCookie method in ConfigureServices, you can use a
CookieAuthenticationOptions parameter to set various properties for cookies in your application.
Various settings such as cookie name and expiration time can be set here and will apply to the cookies
used by the application.
Note: Note that this setting affects application cookies as a whole rather then just Identity.
When using it, you should plan accordingly.
Cookies Settings
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<AuthenticationContext>(options =>
options.UseSqlite("Data Source=user.db"));
services.AddDefaultIdentity<WebsiteUser>(options =>
{
})
.AddEntityFrameworkStores<AuthenticationContext>();
services.ConfigureApplicationCookie(options =>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-15
{
options.Cookie.Name = "AuthenticationCookie";
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = false;
});
}
In this example, you can see that cookies are configured separately from Identity and are not part of it.
The cookies are configured to use the name "AuthenticationCookie" and they will last up to 30 minutes
from the time of creation. They will not be refreshed.
Demonstration Steps
You will find the steps in the section “Demonstration: How to use ASP.NET Core Identity“ on the following
page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD11_DEMO.md#demonst
ration-how-to-use-ASP.NET-core-identity
Windows Authentication
When creating an application designed to be used in an intranet environment, you should consider using
Windows Authentication in your application. By setting up Windows Authentication, users will be able to
sign into the application by using their Windows credentials.
By using Windows authentication, the active directory will be used to determine user credentials and
permissions, allowing users to be managed and created by internal administrators. This helps create safer
applications and removes the need to manually manage users.
MCT USE ONLY. STUDENT USE PROHIBITED
11-16 Managing Security
Note: Due to security concerns, most providers will notify the users about which details
become accessible to your application during the first login. This is done by the various third-
party providers to ensure personal information is not passed without the user's consent. You can
configure your application to request additional details and the user will be notified accordingly.
There is no single uniform way of configuring providers, as each provider utilizes different protocols and
logic. As a result, you should always consider the cost/benefit of adding each provider rather then
providing as many as you can.
Note: External providers can be used alongside the default ones and it may even be wise to
offer the option to users without accounts
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-17
Lesson 2
Authorization in ASP.NET Core
Authorization is the process of determining what a user is allowed to do in an application. For example, in
a library application, an administrative user may approve checking out books, adding books, loaning
books, and throwing away destroyed books. While a regular user in the library can make requests to lend
books or browse them online.
In this lesson, you will learn why authorization is a crucial part of many applications and how you can
utilize it to restrict access within your application. Furthermore, you will learn about several of the options
available to you to further refine the authorization process, allowing you to set the requirement criteria
you need.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the need for authorization.
• Explain why there is a need to restrict access to resources.
• Using the simple authorization attribute.
• Describe different authorization methods.
• Implement different authorization methods.
Introduction to Authorization
In many applications, you will want to prevent
certain users from accessing various resources in
your application. Whether you wish to prevent
users from accessing specific pages, specific data,
or specific actions, this can be handled by the
ASP.NET Core Authorization infrastructure.
ASP.NET Core authorization lets you create and implement different kinds of authorization within your
application and utilize it to limit content as is appropriate. For example, you can block your application's
store page by using simple authorization, you can prevent adult products displaying for younger users by
setting claims authorization, or you can allow only administrators to access financial details about your
website by using role authorization.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-19
Setting Up Authorization
Authentication is also relatively simple to set up, requiring only a small change in the call to
ConfigureServices. Instead of the call to the AddDefaultIdentity<*User*>() method on the
IServicesCollection parameter, you will need to instead call the AddIdentity<*User*, *Role*>()
method. The User parameter should remain the same, however the Role parameter is the IdentityRole
class or a class derived from IdentityRole.
The IdentityRole class is designed to manage roles within the application, similarly to how IdentityUser
manages users and it is used to start up the RoleManager class. However, unlike IdentityUser, which is
often extended, IdentityRole isn't commonly extended and the base class is most frequently used.
While the names of AddDefaultIdentity and AddIdentity are similar, they operate very differently and if
you wish to use ASP.NET Core MVC logic alongside authorization, you will need to utilize AddIdentity. If
you try and use AddDefaultIdentity in an ASP.NET Core MVC application, you may find that redirection
paths from failed authorization attempts will cause you to navigate to invalid paths to the
AccountController. This is due to AddDefaultIdentity utilizing an ASP.NET Core Razor Pages
infrastructure.
The following code is an example of calling AddIdentity:
AddIdentity
services.AddIdentity<WebsiteUser, IdentityRole>()
.AddEntityFrameworkStores<AuthenticationContext>();
Note: If you wish to continue utilizing AddDefaultIdentity in your applications, you can
pipe a call to AddRoles<*Role*>() after the call to AddDefaultIdentity. Note that this will
require you to manage authentication by using ASP.NET Core Razor Pages.
Simple Authorization
The most basic form of authorization revolves around blocking users who are not signed in from
accessing certain pages or performing specific actions. This is called simple authorization, and it
automatically redirects to the Account\Login action, alongside a return URL for easy reconnection. This
allows you to easily gate off entire controllers or individual actions and prevents you from requiring to
manually configure redirections.
In order to implement simple authorization, all you require is to add the [Authorize] attribute. This
attribute can be added to a controller class, preventing unauthorized users from accessing the class at all
while not logged in. Users who are not signed in but attempt to navigate to the controller will be
redirected to the Login URL.
The Authorize attribute isn't limited to the controller class and can also be added to individual actions.
This can be valuable for example when setting up a controller you wish to be publicly accessible but with
specific actions blocked from users who have not yet logged in. For example, this could include browsing
a shop being publicly accessible while actually making a purchase requiring logging in.
Alternatively, you may wish to limit most actions of a controller, while allowing one or two exceptions.
While you can theoretically add Authorize to every action manually, it could result in an action
accidentally being added without authorization to a secure controller, resulting in a security breach.
Instead, in such a case, it would be better to add Authorize to the controller itself and add the
[AllowAnonymous] attribute to actions which are exceptions. By setting AllowAnonymous, you enable
access to the action itself while the rest of the controller is not accessible. An example for this could be a
web page that allows you to view data, while requiring sign in to actually make changes.
MCT USE ONLY. STUDENT USE PROHIBITED
11-20 Managing Security
In this example, the authorization requirement is on the controller itself. All actions in the controller are
blocked unless a user is authenticated.
The following code is an example of simple authorization on an action:
[Authorize]
public IActionResult BuyProduct(int productId)
{
_shop.PurchaseProduct(productId);
return View();
}
}
In this example, only the BuyProduct action requires authentication. Users can freely view products in the
store, but they need to be authenticated to buy a product.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-21
AllowAnonymous
[Authorize]
public class RecipeController : Controller
{
private ICookbook _cookbook;
[AllowAnonymous]
public IActionResult Index()
{
return View(_cookbook.GetAllRecipes());
}
In this example, you can see that a cookbook controller is created. All users will be able to get the list of
recipes, but they will not be able to access the pages for individual recipes. Only authenticated users will
be able to see individual recipes or add new ones.
Authorization Options
As you have seen the most basic form of
authorization involves preventing access to users
who are not logged in. However, most of the
time, your requirements may be more nuanced
than that. Perhaps you wish to only allow
administrators access to certain pages, or require
specific user details.
In this topic, you will learn of additional
commonly used options for authorization inside
of your web applications. These include, but are
not limited to, role-based authorization, claims-
based authorization and policy-based
authorization.
Role-Based Authorization
A simple and common form of authorization, role-based authorization determines what users can access
based on their roles within the system. There are no default roles defined within ASP.NET Core
applications, however you can create your own roles to fit the requirements of your applications.
MCT USE ONLY. STUDENT USE PROHIBITED
11-22 Managing Security
The first step towards working with roles is by populating the roles within the system. This can be done by
using the RoleManager<*Role*> service which is instantiated by the call to AddIdentity within the
ConfigureServices method. The RoleManager will be of the same type provided for the role in the call
to AddIdentity, usually IdentityRole, and can be injected throughout the application allowing you to
manage roles.
Creating Roles
In order to create a role, you will need to call the CreateAsync(*role*) method on the role manager. The
role parameter expects a role of the same type which is configured by the AddIdentity method. To
instantiate the basic role, all you require is to provide a role name.
You can, at any point, check if a given role exists by calling the RoleExistsAsync(*role name*) method. It
accepts a string name, and checks if this specific role has already been created.
Note: Generally, you can create roles at any point throughout the application, but it is
recommended that you create them as part of the database instantiation, or in the Configure
method. If you intend to create them in the Configure method, ensure that the database exists
before creating any roles.
The following code is an example of a method in the startup class for setting up roles:
Creating Roles
public async void CreateRoles(RoleManager<IdentityRole> roleManager)
{
string[] roleNames = { "Administrator", "Manager", "User" };
foreach (var roleName in roleNames)
{
bool roleExists = await roleManager.RoleExistsAsync(roleName);
if (!roleExists)
{
IdentityRole role = new IdentityRole();
role.Name = roleName;
await roleManager.CreateAsync(role);
}
}
}
In this example, you can see that three roles are created in the application. In addition, before creating
each role, if a role already exists, for example if the database was created at a previous time, it will not
attempt to recreate the role. This can also allow you to add additional roles in the future. The
"Administrator", "Manager" and "User" roles will all be created.
Assigning Roles
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
WebsiteUser user = new WebsiteUser
{
UserHandle = model.UserHandle,
UserName = model.Username,
Email = model.Email
};
if (result.Succeeded)
{
var addedUser = await _userManager.FindByNameAsync(model.Username);
In this example, you can see that a new user is created and assigned to both the "User" and
"Administrator" roles.
Role Authorization
[Authorize(Roles = "Administrator")]
public async Task<IActionResult> DeleteUser(string username)
{
var user = await _userManager.FindByNameAsync(username);
if (user != null)
{
await _userManager.DeleteAsync(user);
}
return View();
}
In this case, only "Administrator" users will be able to delete users in the application.
MCT USE ONLY. STUDENT USE PROHIBITED
11-24 Managing Security
The following code is an example of the syntax for giving access to multiple roles:
Claims-Based Authorization
Another common method of authorization in ASP.NET Core is the system of claims. A claim is a key-value
pair which defines the user itself, rather than the user's permissions. Examples of claims include email or
address and it is even possible to add custom claims.
Claims can be declared as is in an ASP.NET Core MVC application by just using a key and a value, but you
can also optionally choose to add Issuer to claims. This denotes where the claim comes from and can be
used to identify the source of the data. This can be useful when you have an application which
communicates with other applications or providers and want to verify that the data is certified by a
trusted source.
Claims-based authorization is used to determine if the user has a specific type of claim or not. This can be
useful for whenever you require the user to input specific details before accessing specific pages. For
example, you may require that the user provide an email address in order to allow the user to register for
a newsletter or you may require that the user input a street address to allow access to a shipping button.
Creating a Claim
To create a new claim, you can use the Claim class. The simplest constructor, Claim(*claim type*, *claim
value*), receives a claim type, which is a string denoting the key for the claim and a value which is the
value for the claim. You can also utilize the ClaimTypes enum as the first parameter to provide a large
variety of common claims.
After you have created a claim, you can use the AddClaimAsync(*user*, *claim*) method on the
UserManager to add the claim to a specific user. The user being of the same type as the one used by the
UserManager and the claim being the newly created Claim object.
The following code is an example of adding a claim to a new user:
Adding a Claim
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
WebsiteUser user = new WebsiteUser
{
UserHandle = model.UserHandle,
UserName = model.Username,
Email = model.Email
};
if (result.Succeeded)
{
var addedUser = await _userManager.FindByNameAsync(model.Username);
if (!string.IsNullOrWhiteSpace(model.Email))
{
Claim claim = new Claim(ClaimTypes.Email, model.Email);
await _userManager.AddClaimAsync(addedUser, claim);
}
}
return View();
}
In this example, if the user adds their email address during registration, an Email type claim will be
created with the same value as the user's email.
services.AddDbContext<AuthenticationContext>(options =>
options.UseSqlite("Data Source=user.db"));
services.AddIdentity<WebsiteUser, IdentityRole>()
.AddEntityFrameworkStores<AuthenticationContext>();
services.AddAuthorization(options =>
{
options.AddPolicy("RequireEmail", policy =>
policy.RequireClaim(ClaimTypes.Email));
});
}
In this example, you can see that an authorization policy named "RequireEmail" is added. It will authorize
all users who have an "Email" claim, while blocking users without an "Email" claim. If an array of email
names was added to the RequireClaim function as a second parameter, only the claim values specified
would be authorized.
Claim-Based Authorization
[Authorize(Policy = "RequireEmail")]
public IActionResult Index()
{
return View();
}
In this example, you can see that the RequireEmail policy is enforced on this action. Only users with an
Email claim will be able to access this method.
Multiple Authorizations
[Authorize(Policy = "RequireEmail")]
[Authorize(Roles = "Administrator")]
public IActionResult Index()
{
return View();
}
In this example, the user will only be able to access this action if it has the "Administrator" role, and
fulfills the "RequireEmail" policy.
Best Practice: Whenever an authenticated user attempts to access a resource that they do
not have permissions to access, the ASP.NET Core MVC application will redirect the request to
"Account\AccessDenied". You should add a controller view and method to handle this path,
and update the users that they do not meet the criteria to view the page. If you do not do this,
users will encounter a browser error.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Authorize Access to Controller Actions“ on
the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD11_DEMO.md#demonst
ration-how-to-authorize-access-to-controller-actions.
MCT USE ONLY. STUDENT USE PROHIBITED
11-28 Managing Security
Lesson 3
Defending from Attacks
Web applications are designed from the ground up to be accessible. This is because inherently web
applications are intended to allow users to access them from multiple different browsers without being
reliant on installations. However, this also means that web applications also have to be particularly wary
against malicious attacks from a variety of sources.
In this lesson, you will learn about a variety of different security attacks which frequently target ASP.NET
Core MVC applications, as well as ways to utilize the ASP.NET Core environment to defend your website
and your users.
There are many types of attacks which can target web applications and different kinds of attacks may
target different victims. Some attacks specifically attempt to disrupt the web application server, affecting
data integrity or disrupting performance, while others may attempt to instead attack the individual user,
steal their data, abuse their permissions, or disrupt their experience.
After finishing this lesson, you will be aware of some of the more common types of attacks directed
towards web applications and have some basic knowledge in how you can help to mitigate their effects
on your application and its users.
Lesson Objectives
After completing this lesson, you will be able to:
• Understand cross-site scripting and how to defend against it.
Cross-Site Scripting
Cross-site scripting commonly known as XSS, is a
security exploit by which a malicious user
(commonly referred to as attacker) utilizes the
input on a website to insert malicious scripts,
such as JavaScript code, with the intention of
altering the default behavior for a website. When
these scripts are run, they can be used to steal
private information such as session tokens or
cookies and alter the behavior of pages,
potentially redirecting users to malicious
websites.
To enact this exploit, the attacker will look for an
input with content which is later viewable by other users, for example a chat box in a chat or a username
in any application in which other users can be seen. When a script is successfully inserted by the attacker,
opening a webpage with the script will cause it to run, potentially causing it to steal information from the
user or disrupt their ability to use the website.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-29
Note that any modifications which occur only within data displayed to the attacker and only affect the
specific browser it is made from is not an XSS attack. XSS attacks are only such when it affects a browser
other than the browser in which they were performed. It is completely impossible to prevent a user from
sabotaging the browser which he is using, as most browsers give knowledgeable users full control over
the HTML and JavaScript. If the user can affect a different browsing session, even if it only affects the same
credentials, it would be considered an XSS attack.
As a general rule of thumb, you will need to be careful in the following areas whenever handling any data
that was input from the user in order to avoid XSS attacks:
• HTML inputs. Never display any untrusted data which may come from another source. This can
include data sent from another session, data influenced by headers, or even data loaded from
databases.
• HTML elements. Putting untrusted data into an HTML element can cause scripts to be run, resulting in
malicious activities. This form is particularly easy to abused, as it's very easy to write a script inside of
an element.
• HTML attributes. A clever attacker can utilize a standard attribute structure to end an existing element
and add a script or can abuse one of the on- events of HTML elements to call a function.
• JavaScript. Untrusted data added or inserted in your JavaScript could result in undesirable function
calls or undesired code execution.
• Query string. A further vulnerability point could be the query string. In an unsecure environment, data
parsed from inside the query string could result in script executions.
You can avoid these issues by ensuring you use appropriate encoding on untrusted input. Encoding
ensures that various symbols commonly used in HTML and JavaScript, such as '<', or '\' are replaced with
string keys which are globally recognized by browsers and cannot be used to execute code. For example,
in HTML, the '<' character will, for example, be replaced with "<" while the '\' character will be replaced
with "\". All encoded characters follow a specific format, for example HTML encoding begins with '&'
and ends with ';', while JavaScript encoding utilizes a "\\u*key code*" format. This ensures that browsers
will be able to identify that these elements are encoded and render them appropriately.
Furthermore, HTML itself requires encoded characters to display them. This is due to it being impossible
to use several characters due to being used as part of the HTML markup. For example, if you place the
text "<This text appears between lesser than and greater than>" inside of a view, it will render as
"<This text appears between lesser than and greater than>". If you try and input the completed
sentence directly, it will instead create a nonstandard element "<this>" with the remaining words flagged
as attributes. The first will be perfectly legible and safe code, while the second could present a security
exploit.
You can find a list of various standard character encodings in this link: https://aka.ms/moc-20486d-m11-
pg2
@scriptTag
In this example, the view will print out the text: "<script type='text/javascript'>alert('XSS')</script>".
It will not issue a JavaScript alert and no XSS will occur.
Best Practice: It is possible to allow non-encoded HTML to be used within a Razor page,
but you will need to make a dedicated effort by using the HtmlString class which is not encoded
and as such is prone to XSS attacks. It is advised to not allow non-encoded HTML.
JavaScript XSS
@{
var script = "xss()";
}
<script type="text/javascript">
function xss() {
alert("XSS");
}
document.write(@script);
</script>
In this example, when we attempt to write unknown data, the xss function is called instead. Depending on
the application many undesirable flows can occur. This is highly problematic since any available function
could end up being called leading to a large amount of possible issues, from incorrectly rendering pages,
all the way to creating invalid links.
@{
var script = "xss()";
}
<script type="text/javascript">
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-31
function xss() {
alert("XSS");
}
document.write("@encoder.Encode(@script)");
</script>
In this example, the string "xss()" will be displayed and no functions will be called. This will allow you to
ensure correct script behavior for users and prevent malicious data from affecting others.
Injecting Encoders
Occasionally, you may wish to work with encoding on the controller. You can do this by injecting
HtmlEncoder, which is used for encoding for HTML content such as element content or attributes, and
JavaScriptEncoder, which is used for injecting encoded elements into JavaScript code. This can allow you
to pre-encode information from unknown sources and not have to encode it on the Razor page itself.
Injecting Encoders:
private JavaScriptEncoder _javaScriptEncoder;
private HtmlEncoder _htmlEncoder;
In this example, you can see that an unsafe text is being encoded into both HTML encoding and
JavaScript encoding.
Note: Razor pages utilize HtmlEncoder by default. Therefore, while it will always prevent
invalid HTML, it can cause errors or security exploits while using the @ directive in JavaScript.
In this example, if the username matches a valid relative path in the application, it can potentially cause
serious problems. For example a user name such as "../../Account/Logout" would actually redirect the
current user to a log out method, requiring them to log back in or even worse, the username could
potentially be "../Admin" potentially deleting an administrator's user.
The following code is an example of using URL encoding to prevent URL XSS:
Note: An XSRF attack does not even require the user to click any buttons. The malicious
website can create a hidden form within the HTML and submit it by using JavaScript, without
requiring any input from the user.
3. The form is then submitted through JavaScript. The browser sends the request alongside the
authentication cookie since the request is to "www.contoso.com" and the user has an active
authentication cookie.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-33
4. The request is performed on the "www.contoso.com" server without the user being aware of it. Any
operation the user can perform can occur in this way.
XSRF attacks are not influenced by utilizing a more secure connection using HTTPS, since the form can
just send the request by using HTTPS and it can also be used to target any possible method.
Best Practice: If your website API provides any GET methods which are capable of
changing data, malicious users can target your site on websites which allow user images by
providing a vulnerable URL to your website causing problems with any of your users which load
any of these image links. It is therefore a best practice for a GET method to never change data.
There are many additional ways in which an XSRF attack can be performed, not only requiring cookies,
and you must take care to protect your websites and applications.
ASP.NET Core MVC utilizes a form of anti-forgery validation method in which when a form is created by
the controller as part of a view, it receives an additional hidden input with the name
__RequestVerificationToken. This name is generated every single time the view is created and thus
provides a unique key by which to identify the specific instance which posted the form. When XSRF
protection is set up, all XSRF protected actions will validate that a correct token is provided or an error will
occur instead. This guarantees that requests arrive only from within the application.
ValidateAntiForgeryToken Attribute
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(string userName)
{
return View(userName);
}
In this example, you can see that post calls to the Index action will require that the user possess a valid
anti-forgery token to call this action. Any attempts to access this index from outside a valid form will result
in an error instead.
You may sometimes find yourself in need of marking multiple requests across a controller, while requiring
some requests to not require anti-forgery, such as various GET based actions. There are several tools you
can use to help the process become more convenient and comfortable for the user.
AutoValidateAntiforgeryToken
[AutoValidateAntiforgeryToken]
public class AntiForgeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(string userName)
{
return View("Index", userName);
}
[HttpDelete]
public IActionResult RemoveUser(string userName)
{
string url = string.Format("~/RemovedUser/{0}", userName);
return RedirectToAction("Account", "RemoveUser", "User");
}
}
In this example, the normal Index action (GET) will work regardless of origin, while both the Index action
with a POST method and the RemoveUser action which is a DELETE method will both require the client
to utilize anti-forgery tokens.
Finally, if you wish to specifically make an action accessible in a controller which requires anti-forgery
tokens you can use the IgnoreAntiforgeryToken attribute to remove it for a specific action. This will let
you make certain actions available in scenarios where they are usually blocked.
The following code is an example of the IgnoreAntiforgeryToken attribute:
IgnoreAntiforgeryToken Attribute
[AutoValidateAntiforgeryToken]
public class AntiForgeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
[IgnoreAntiforgeryToken]
public IActionResult Index(string userName)
{
return View("Index", userName);
}
[HttpDelete]
public IActionResult RemoveUser(string userName)
{
string url = string.Format("~/RemovedUser/{0}", userName);
return RedirectToAction("Account", "RemoveUser", "User");
}
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-35
In this example, both Index actions will always be accessible, but the RemoveUser action will require an
anti-forgery token.
Anti-Forgery on a View
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
In this example, you can see that a form is created to perform a post method to the Index action. Since
tag helpers are enabled, it will be generated with an anti-forgery token.
Best Practice: It is always possible to specifically disable Form tag helpers to prevent the
form from utilizing anti-forgery tokens. However, it is not recommended since it means you
choose not to utilize the protection offered by the anti-forgery mechanism.
Should you find yourself needing to use JavaScript to call your methods, you can create a form and either
submit it via JavaScript or you can find the hidden element with the name __RequestVerificationToken
and use it to create your request.
If you need a to add a secure call to a get method, such as to prevent hostile websites from getting data
from your API or if you wish to not utilize the form tag helper, you can manually add a directive call to
@Html.AntiForgeryToken()to create the request token. This can allow an alternative option and help
you create secure get requests.
The following code is an example of the Html AntiForgeryToken directive:
In this example, an anti-forgery token is generated inside the form, tag helpers are not required.
MCT USE ONLY. STUDENT USE PROHIBITED
11-36 Managing Security
SQL Injection
SQL injection attacks are a common yet
extremely dangerous form of malicious attacks
which can be used to affect applications. Unlike
the majority of attacks introduced in this chapter,
SQL injection attacks can also be used to target
applications without any web functionality and as
such familiarity with how they work is required
for any developer.
SQL injection shares a similarity to XSS in that
both attacks use parameters to add logic for
performing operations. However, whereas XSS
targets other clients in their attacks, potentially
affecting other users, SQL injection directly targets the database itself. Similar to XSS, SQL injection occurs
when a malicious user calls for APIs or changes inputs. But unlike code designed to run scripts, SQL
injection instead manipulates input strings and utilizes them to change how SQL queries work. SQL
injection could be caused by users providing malicious control input or even directly modifying query
strings and server requests on their browsers.
For example, in a basic scenario you might have a controller which receives an ID parameter from a user
and utilizes it to get the username for the provided ID.
The following code is an example of a controller action which is vulnerable to SQL injection:
In this example, you can see a basic method which receives a parameter with the string type and adds it
into the query string before executing it. However, this code is extremely vulnerable to SQL injection as
you can add malicious code to drastically alter the behavior of the query for example:
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-37
The following code is an example of malicious input that can result in SQL injection:
When this example is provided to the previous example, whether through putting it into the UI input and
submitting, through modifying a query string, or through other means, the query once executed will
attempt to completely delete the table USERS from the database. If this ever affects any relevant database
it can instantly cause massive losses, leading to potentially catastrophic results.
Parameterized Queries
public IActionResult GetUser(string id)
{
using (SqlConnection connection = new SqlConnection(_connectionString))
{
connection.Open();
In this example, you can see that the user ID is added as a named parameter. As a result, even if malicious
SQL is inserted, it will be treated as a string instead of part of the query.
MCT USE ONLY. STUDENT USE PROHIBITED
11-38 Managing Security
Sanitize Parameters
Finally, as the least recommended option, you can sanitize parameters either by parsing or by using them
as part of the routing. If the parameters are not string type you can parse them yourself or you can rely on
ASP.NET Core MVC Routing to provide parameters of the correct types. Alternatively, you can use regular
expressions and whitelisting to disallow specific strings from appearing.
Cross-Origin Requests
Modern browsers utilize a special restriction,
known as same-origin policy, which is designed
to prevent websites of different origins accessing
resources on another website. This helps prevent
malicious websites from being able to call APIs
on other websites and helps protect both end
users, as well as the application itself. However,
you may occasionally find a need to have
different web applications communicate with
each other, despite having a different origin. To
allow this, you can enable Cross-Origin Resource
Sharing (CORS).
CORS allows servers to determine that they allow some or all of these normally blocked access attempts
by allowing the developer to set conditions under which the application will accept requests from another
origin. The behavior for CORS is a W3 standard, and thus standard across all major browsers and as such
does not suffer from compatibility issues. It is important to note however that when utilizing CORS you do
not accidentally open yourself up to various attacks.
Same-Origin
As covered before, CORS needs to be enabled whenever the request origin is changed. The request origin
is determined by matching up the following segments of the URL:
• Schema. The protocol declaration at the beginning of the URL, such as http:// and https://. For
example, http://www.contoso.com and http://www.adventure-works.com both have the same
schema. Meanwhile https://www.contoso.com bears a different schema.
• Host. The host is comprised of both domains and subdomains and encompasses the section between
the schema and the port. For example, https://www.contoso.com and
http://www.contoso.com:8080 both share the same host. Meanwhile, https://contoso.com,
https://www.adventure-works.com and https://www.contoso.com all possess different schemas.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-39
• Port. The port is an optional part of the URL, appearing immediately after the host when it is present.
If the port is not specified the default values for the protocol will be used, such as 80 for http, and
443 for https. When a port is present it will be denoted by :*port number*. For example,
http://www.contoso.com:80 and http://www.contoso.com will both have the same port number,
80. Meanwhile https://www.contoso.com and http://www.contoso.com:442 have different ports
at 443 and 442, as will http://www.contoso.com with 80 and https://www.contoso.com with 443,
but http://www.contoso.com and https://www.contoso.com:80 will both use port 80.
Any parts appearing past the schema, host, and port will not affect the origin, but all three must be
identical for it to count as being the same origin. If you have two applications which need to
communicate on two separate origins, you will need to implement CORS.
Note: If the browser supports CORS, such as Microsoft Edge, the Origin header will be
added by default. CORS cannot be used in browsers which do not add Origin to the request.
Note in this example that the Origin header is set to "https://www.adventure-works.com", while the
request is made to "http://www.contoso.com". This is set by the browser whenever a request to another
origin is made.
When the server receives a CORS request, it will validate that the request is from a legitimate source. If the
request came from a legitimate source with a valid Origin header, the server will add the header Access-
Control-Allow-Origin with a value equal to the original request Origin. The browser will then ensure
that there is a match between both values, and if no match is found, the browser will disallow the request,
even if the server returns a valid response.
The following code is an example of a CORS response header:
Note: Note that origins should not end in a trailing '/'. If they do, they will not be parsed
correctly.
The following code is an example of the UseCors middleware with a policy builder:
app.UseMvcWithDefaultRoute();
In this example, if any cross-origin requests occur from "https://www.contoso.com" the server will serve
them the same as if they came from within the website. Requests from any other site will not be accepted
and result in an error.
An alternative option is also present by instead defining a CORS policy in the AddCors method. This can
be done by invoking it as AddCors(*cors options action*). This method accepts a CorsOption object
which can be used to create multiple CORS policies by invoking the AddPolicy(*policy name*, *cors
policy builder*) method. This will create a named CORS policy which can later be used in the application,
such as within the middleware. To call it from the CORS middleware, use the method call UseCors(*policy
name*) which will receive the policy name declared earlier.
This policy is named "FromContoso" and will accept both "https://www.contoso.com" and
"http://www.contoso.com". Note that at this point the policy is created, but will not be applied.
The following code is an example of using CORS policy to set up the CORS middleware:
app.UseMvcWithDefaultRoute();
Note: It is important that the call to the UseCors middleware is made before any resources
which may depend on CORS are called. If the call to UseCors appears after UseMvc, any
ASP.NET Core MVC logic will ignore CORS.
In this case, the "FromContoso" policy will be applied on any requests to the action.
In the event you wish to disallow CORS on a specific action, you can use the DisableCors attribute on a
specific action inside a controller implementing EnableCors. This will prevent CORS from being used on
the specific action.
The following code is an example of disabling CORS for an action:
In this example, any attempts to call the Index action from external sources will be blocked.
In this example, the policy will accept CORS requests from "www.contoso.com", and from all the HTTP
method types. The header "my-header" will also have to be present in the request.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 11-43
Note: It is important to note that regardless of how well you protect your application from
running HTTP requests, if the user attempts to access your application by using HTTP, any details
sent by the user can be intercepted.
It is important to note that the call to the UseHttpsRedirection middleware must appear before any
middleware which can provide the user with any html files, resources, or actions. This includes, but is not
limited to, UseMvc and UseStaticFiles. If they are called before UseHttpsRedirection, these resources
will be accessible to normal HTTP traffic.
MCT USE ONLY. STUDENT USE PROHIBITED
11-44 Managing Security
The following code is an example of enabling HTTPS redirection inside your applications:
app.UseStaticFiles();
app.UseMvc();
Objectives
After completing this lab, you will be able to:
• Use Identity.
• Add Authorization.
• Avoid the Cross-Site Request Forgery attack.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD11_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD11_LAK.md.
Review Question
Question: What would be the risk if you add a call to an action using HTTP instead of HTTPS?
Best Practice
Remember that the methods outlined in this module exist to help you protect your application, but will
not necessarily stop all malicious attack. You should always be vigilant and work to remove any holes in
your application security if they are discovered.
Module 12
Performance and Communication
Contents:
Module Overview 12-1
Lesson 1: Implementing a Caching Strategy 12-2
Lesson 2: Managing State 12-11
Lesson 3: Two-Way Communication 12-20
Lab: Performance and Communication 12-32
Module Review and Takeaways 12-34
Module Overview
Modern web applications require complex interactions with users. Users will often request a lot of data in
a small time-frame, while also expecting relevant data as soon as it comes online. This can easily cause a
significant amount of load on an unprepared server, resulting in unnecessarily complex or repeated
operations and a heavy load on your server. Fortunately, there are multiple ways to reduce the load.
Caching allows you to store commonly repeated requests, preventing the need to perform the same logic
repeatedly. By using caching, you can reuse data that has already been loaded and present it to the user.
This provides the user with a fast response time and reduces system resources used in conducting the
logic for the action.
State meanwhile allows achieving a state of consistency between different requests. By utilizing various
forms of state management, you can transform the normally stateless web experience into one that is
custom tailored to individual clients, with different users enjoying a separate and relevant experience in
the same application.
Finally, SignalR is a framework that allows the abstraction of several different communication protocols
into an easy to use API, which allows you to easily create a single set of tools on the server and client to
facilitate two-way communications. This allows you to focus on the logic you wish to implement while
allowing you to not have to cater to specific browsers.
Objectives
After completing this module, you will be able to:
Lesson 1
Implementing a Caching Strategy
Usually, web applications display information on a webpage by retrieving the information from a
database. If the information that should be retrieved from the database is large or involves complicated
logic, the application might take longer to display the information on a webpage. ASP.NET Core MVC
supports some caching techniques to help reduce the time required to process a user request.
Before implementing caching, you should first analyze if caching is relevant to your application because
caching is irrelevant to webpages whose content changes frequently. To successfully implement caching
in your web application, you need to familiarize yourself with the various types of caches, such as the
cache tag helper, and data cache.
Additionally, in larger-scale applications where scaling will be required, you will need to consider using a
distributed cache, allowing multiple separate servers running your application to use the same cache.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the benefits of using caching.
• Describe the cache tag helper.
• Describe the in-memory cache.
• Describe distributed caching.
• Configure caching.
• Helps increase the number of users who can access the server farm simultaneously.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-3
However, you should consider several important details before implementing caching. If you perform
caching for content that frequently changes, you will quickly end up in situations where users are
receiving irrelevant content. When performing the caching, the data is stored in the web server and used
instead of the most recent data existing. This can be highly useful for things like maintaining a shopping
cart for the user or for rendering collections of strings which are calculated the first time but remain
consistent. However, data that changes regularly, such as various real-time data such as product
inventories and areas where the content changes between visits.
Caching can also be handled on environments running across multiple servers. When working with
multiple hosting servers, you will need to either work with a sticky session, which means that the same
server handles all requests from the same client, or use a distributed cache, which can handle caching for
multiple servers in a separate dedicated server. Otherwise, it will result in multiple servers handling
separate requests with each server creating its own cache, which results in a waste of memory.
Servers have a limited amount of free memory they can allocate towards caching at any given moment in
time. This means that sometimes the server will need to clear out older cache entries on its own. To assist
the server in handling these issues, you can set both the caching priority and the caching lifetime. By
setting a specific lifetime, you can ensure that cached data doesn't remain for too long, particularly in
systems where the cached data refreshes infrequently. By setting caching priority, you can determine that
if there are memory issues, lower priority cached data will be deleted first, whereas higher priority cached
data will remain unless there is no lower priority cached data.
Caching lifetime can include customization options such as:
• Setting cache expiry time in absolute units of time.
• Setting a date at which the cache will be cleared.
• Setting a period of time for deletion since the cache is last accessed.
• Setting if the expiration is sliding expiration, and the timer is reset whenever the cached resource is
accessed.
Caching priority can be set to Low, Normal, High, or NeverRemove to determine priority for removal
whenever memory is cleaned up.
<cache>
@for (int i = 0; i < DateTime.Now.Second; i++)
{
<div>Number of seconds</div>
}
</cache>
In this example, a number of divs will be displayed according to the number of seconds in the current
time when the request is made. Any future requests, while the caching remains, will yield the same
number of divs.
Additionally, the cache tag helper supports a variety of additional attributes that can be used to further
customize the caching logic to fit with the requirements.
By using the enabled attribute, you can enable and disable caching for specific segments of code. An
example for this kind of usage could be a website that displays stocks either on a daily or a current trend.
While displaying the daily stock values, you can retrieve the stock values once and render the same values
throughout the day. On the opposite end of the spectrum, if you are looking at the current value of
stocks, you are able to disable caching enabling new values to be retrieved in the same refresh while using
one component for stocks in both pages.
The following code is an example of cache tag helper enabled attribute:
You can also use a variety of attributes to specify expiration behavior for the cached data, which include:
• expires-on: Accepts a DateTime object which determines until when the caching will remain.
• expires-after: Accepts a TimeSpan object which determines the amount of time to keep the cached
data since the original request was made. If a new request is made the caching time will not be
changed.
• expires-sliding: Accepts a TimeSpan object which determines the amount of time to keep the cached
data since the previous request was made.
The following code is an example of the various cache tag helper expiry attributes:
<cache expires-after="TimeSpan.FromSeconds(60)">
<div>
@DateTime.Now.ToString("dd/MM/yyyy hh:mm")
</div>
</cache>
<cache expires-sliding="TimeSpan.FromSeconds(5)">
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-5
<div>
@DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss")
</div>
</cache>
The content of the first cache tag helper will expire by 2026 and continue showing the same date until
that time. The content of second cache tag helper will expire after 60 seconds since it was first accessed,
while the last one will expire five seconds after the last request. If requests keep coming within five
seconds of each other, the last one will not expire.
An additional attribute that can be used to further customize when caching happens is the vary by
attribute. This allows you to maintain separate caches for different properties and even use vary by on its
own to set up your own logic. This allows you, for instance, to vary the cache by various model or
ViewBag properties.
Several common options for vary-by are as follows:
• vary-by-query: Accepts single or multiple values separated by commas, which are key in the query
strings. The caching will be based on the values of the specified query string keys and a separate
cache will be used for every different value.
• vary-by-cookie: Accepts single or multiple values separated by commas, which are cookie names. The
caching will be based on the values of the specified cookies and a separate cache will be used for
every different value.
• vary-by-route: Accepts single or multiple values separated by commas, which are route data
parameter names. The caching will be based on the values of the route data parameter names and a
separate cache will be used for every different value.
• vary-by-user: Accepts the strings true and false. The caching will be based the currently logged in
user and include non-authenticated users. Logging out or logging in will clear the cache.
• vary-by-header: Accepts single or multiple values separated by commas, which are field names in the
request header. The caching will be based on the values of the specified request headers and a
separate cache will be used for every different value.
• vary-by: Allows setting custom string value to be used for caching. Whenever the value changes a
separate cache will be used. By concatenating strings, you can create a condition as specific as
required.
The following code is an example of vary-by-query:
In this example, the display for the product list will be cached according to the MaxPrice and MinPrice
keys in the query string. As long as the values for both keys not change, if a cache exists for those values it
will be used, otherwise, the code will be recalculated and a new cache entry will be added alongside the
previous ones.
MCT USE ONLY. STUDENT USE PROHIBITED
12-6 Performance and Communication
In this example, a new cache will be created whenever a new product is loaded. Whenever a model that is
already in the cache is loaded, the cache will be used.
Another crucial attribute for caching is priority. By setting this attribute you can determine when the
cache should be cleared. The valid values for this attribute are the enum values of the CacheItemPriority
enum, from the Microsoft.Extensions.Caching.Memory namespace, with Low being the first to be
cleared and NeverRemove being the lowest priority to clear due to memory constraints. By default,
Normal will be used.
Note: Despite being named NeverRemove, if the memory issues become severe enough,
items tagged as NeverRemove can be removed.
Note: Multiple attributes can be used for the caching logic, enabling powerful and diverse
cache tag helpers.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Configure Caching“ on the following page:
https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD12_DEMO.md#demonst
ration-how-to-configure-caching.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-7
Additional methods for using caching include the GetOrCreate and GetOrCreateAsync methods. These
methods will check the key for you and if the cache item with the matching key does not exist, perform
the logic for retrieving the data. This can help further simplify the code. GetOrCreateAsync should be
used if the caching logic is reliant on tasks.
The following code is an example of using the GetOrCreate method:
return View(products);
}
Sometimes the default caching settings isn't useful to you and you want to customize it further. To do
this, you can provide the Set method an additional third parameter of the type
MemoryCacheEntryOptions. The MemoryCacheEntryOptions allows you to set useful parameters such
as:
• AbsoluteExpiration. The absolute date in which the cache entry expires.
• PostEvictionCallBacks. A callback function that will be called when the cache entry is removed.
• Priority. The priority of the cache entry.
• SlidingExpiration. An offset after which the cache is cleared. This is reset every time the cache is
accessed.
The following code is an example of using the MemoryCacheEntryOptions parameter:
return View(products);
}
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-9
Distributed Cache
A major issue with regular caching is that it is
reliant on a specific web server. In a sticky server
scenario, this means that you will need to keep
working with the same server, even if the server
becomes busy and non-responsive. In a non-
sticky server scenario, any attempts to use
caching are ineffective, possibly resulting in the
same data getting cached across multiple servers.
A distributed cache is instead held in a
centralized area and is not affected by individual
web servers. Therefore, the cache will persist even
when servers are taken down. Additionally, data
stored on a distributed cache is used for handling requests from multiple users with the same cache. This
can greatly reduce the amount of cached data, leading to less storage needed and offering more
comprehensive solutions.
It is important to note that unlike other caching options, distributed caching saves and reads the data as a
byte[]. Therefore, you will often need to convert data types to use distributed caching correctly.
Note: Redis will not be covered in this course. If you want to find out more details about
Redis you can read about it here: https://aka.ms/moc-20486d-m12-pg1
<distributed-cache name="defaultCache">
@for (int i = 0; i < DateTime.Now.Second; i++)
{
<div>Number of seconds</div>
}
</distributed-cache>
<distributed-cache expires-sliding="TimeSpan.FromSeconds(5)"
name="slidingExpirationCache">
<div>
@DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss")
</div>
</distributed-cache>
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-11
Lesson 2
Managing State
While developing applications, you may want to create functions that require information to be retained
across requests. For example, consider an application in which you need to first select a customer and
then work on the order relevant to the customer. HTTP is a stateless protocol. Therefore, ASP.NET Core
includes different state management techniques to help store information for multiple HTTP requests. You
need to know how to configure and scale state storage mechanisms to support web server farms.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the benefits of storing state information.
• List the options for storing state information.
• Configure and use Session state in ASP.NET Core MVC applications.
• Store a client-side information with the HTML5 Web Storage API.
Most applications will end up supporting state in some form or another, but it can take many different
forms. Common technologies used for state management from the client side include cookies, query
strings and data from various HTML controls, such as hidden fields. Meanwhile, in the server,
HttpContext, alongside the TempData property of the controllers can be used to synchronize different
requests from the same client to result in a coherent and seamless experience for the user, with individual
technologies used for implementing different requirements as needed.
In addition, the client itself can also maintain a degree of session management by using the HTML5 web
storage API. This is a form of session management that is entirely in JavaScript on the client side.
Client-Side Options
Query Strings
One of the simplest ways to obtain persistence
throughout the application is by using query
strings in requests. By doing this, you can pass a
limited amount of extra information on every
request. While a relatively simple way to preserve session state, it can quickly end up with sending very
large amounts of information from multiple pages, as well as manually needing to be added to individual
requests. Using query strings works best when you need to add very small amounts of additional state
related information. To do this, you just need to append the data from previous requests to your current
request.
Best Practice: Query strings are not secure, and you should never use them to send secure
data such as usernames and passwords. They should only be used for adding minimal amounts of
data, as it is easy to use them maliciously.
Hidden Fields
Another possibility for retaining data is by using input elements with the type hidden. When included as
part of a form, these will be sent whenever requests are made from a form and are frequently useful when
trying to create multi-page forms, since a hidden field with an identifying value can be used. A major
weakness of hidden fields is that they can be relatively easily maliciously changed by the user, requiring
validation on the server side.
Cookies
Cookies are a form of client-side data retention that allows the storage of key-value pairs. Unlike the
previous methods, cookies for a site are always sent on every request, which has both advantages and
disadvantages. On the one hand, as developers, you do not need to explicitly add cookies at any point,
but on the other hand, if cookies contain too much data, it can slow down requests to the server.
Cookies are usually limited in size and quantity on browsers, and therefore it is a good idea to use a
relatively small amount of focused data. A good example of the kind of data frequently stored in cookies
is authentication tokens which are returned to the client after a successful login. The client will then store
that token on every request, which the server can use to validate the session for the sender.
It is important to note that cookies can easily be manipulated by knowledgeable users, and thus the server
should always validate that the cookies are returning expected results.
Also note that cookies can be set up to remain after the browser is closed and can be customized to
persist for different amounts of time, allowing behavior similar to caching.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-13
Server-Side Options
TempData
One of the first methods to handle session state on the server side is by using TempData. This is a special
data store which stores items until they are read, at which point they are deleted. The TempData is
shared across all controllers of the application, allowing you to interact with it between completely
unrelated requests.
TempData can have items added to it by setting a key, and the items are removed when getting a value
for a key. Additionally, you are also able to use the Keep method, which will mark a key to not be deleted
when getting from it, or the Peek method, which allows getting the value of a key without deleting it.
TempData Example
public IActionResult Index()
{
object tempDataValue = TempData["myKey"];
if (tempDataValue != null)
{
return Content("TempData exists!" + tempDataValue);
}
In this example, you can see that when the TempData does not contain the myKey key, it will set it in the
TempData with the value of the string, Temporary Value, and return the string content TempData does
not exist!. If the key does exist however, it will instead be read and deleted from the TempData and
instead the string content TempData exists! Temporary Value will be returned.
By default, TempData utilizes browser cookies as the storage mechanism. It is however also possible to
use session-based storage by appending a call to AddSessionStateTempDataProvider method to the
AddMvc method in the ConfigureServices method. By doing this, the storage mechanism used for
sessions will be used instead of cookies.
The following code is an example of configuring TempData to use session storage:
services.AddSession();
}
Note: AddSession and Session Storage will be covered in more details in topic three,
"Configuring Session State"
HttpContext.Items
One way of managing state on the server side is by using the Items property of the HttpContext object
that is used by middleware in your application. The Items property is a Dictionary of object type keys
and object type values, allowing you to store and access any property you need to be later used in other
middleware.
MCT USE ONLY. STUDENT USE PROHIBITED
12-14 Performance and Communication
This method of state management is relevant on a request-specific basis, since this property is part of the
request itself. However, it allows you to communicate between different middleware and share important
information without having to conduct complex methods repeatedly.
Best Practice: When creating middleware intended for specific applications, the use of
strings as keys for the HttpContext.Items dictionary is very useful and will frequently be a good
choice. However, when creating middleware that is intended to be used in more than one
application, you should use specific objects as keys. This will ensure that your HttpContext.Items
do not accidentally clash with those of another middleware.
HttpContext.Items Example
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Use(async (context, next) =>
{
context.Items["Query"] = VerifyQueryString(context.Request);
await next.Invoke();
});
In the preceding example, you can see an item on the HttpContext parameter being set with the key
Query in the first middleware, and the resulting value being displayed from a different middleware. If no
query string is present, the string No Query String! will be displayed in the browser. If a query string is
present, the string The Query String is: followed by the content of the query string will be displayed.
Cache
An additional means of state management you can use is caching. Caching can be an efficient means of
handling repeated requests and handling reoccurring information. Caching is useful for handling the state
for repeating requests, ensuring data consistency.
Best Practice: When using distributed caching, you should not store any user-specific
information in the cache. This could result in at best invalid information being displayed, and at
worst present a major security breach.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-15
Dependency Injection
Since services can be injected into any component throughout your application, they can also be used for
state management. There is no end to possible usages for services in state management, and by using
them any desired behavior can be achieved.
Session State
Another method for managing state is the Session State. This is a built-in mechanism designed to
specifically handle sessions between the server and client. By utilizing specific cookies from the client, the
server is able to identify the specific client and apply user-specific logic throughout the request. The same
session can persist for lengthy periods of time, allowing users to keep using the system without requiring
constant authentication.
A session will only be created when at least one value is retained as part of the sessions. Sessions will be
specific to individual cookies, which are set to deletion when the browser is closed. This can allow a user
to continue interacting with the application in a seamless manner. In the event an expired cookie is
received, a new session will be opened for handling it. By default, sessions will persist for 20 minutes, but
you can configure it as needed. You can also expire the session early by calling the Clear method of the
ISession interface.
Note: There is no default implementation for clearing sessions when the cookie expires, or
when the client closes the browser. If you require this behavior, it will need to be created
manually.
In single server scenarios, it is possible to store the session in a memory cache. In multiple servers’
scenarios, sessions will be stored by using a distributed cache. This allows your session to work across
multiple servers, with the client being agnostic to which server handled the last request, allowing for a
convenient user experience.
You also need to call the UseSession method on the IApplicationBuilder parameter in the Configure
method. This will set up the Session property of the HttpContext, allowing it to be used in other
middleware or in various components throughout the application. It is important to note that UseSession
should be called before any middleware that will use the session or you will encounter an
InvalidOperationException when trying to use the session.
The following code is an example of configuring session state:
services.AddMvc();
}
app.UseMvcWithDefaultRoute();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(20);
});
services.AddMvc();
}
app.UseMvcWithDefaultRoute();
Note: In the example above, you can see that AddDistributedMemoryCache is used. This
method technically implements the IDistributedCache, but it behaves similarly to
IMemoryCache and is not distributed. It is useful for creating caching with the intention of
adding multiple servers in the future and as a method of quickly testing distributed cache
methods on development environments. It does not actually handle distributed caching logic.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Store and Retrieve State Information“ on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD12_DEMO.md#demonst
ration-how-to-store-and-retrieve-state-information.
• localStorage. Items stored with localStorage will persist until deleted. They will be relevant between
different browser windows and tabs and will still exist when the browser is closed. This is useful with
information that does not need to change often and data that needs to last for a long time.
• sessionStorage. Items stored with sessionStorage are specific to the individual browser tab and will
not be accessible from other windows or tabs. It will always be cleared once the tab is closed. This is
useful for information relating to the current browsing session and for sensitive information.
Note: It is important to note that these APIs store data in browser specific storage. Data
stored in one browser will never be accessible to another browser.
These APIs hold a dictionary of key-value pairs. Both the key and value are strings and trying to store any
other type will store the toString value of that variable.
To use the API itself, you can use the following functions, which exist on both localStorage and
sessionStorage:
• Set(*key*, *value*). Receives a string key parameter and a string value parameter. The value will be
stored under the key in the storage.
• Get(*key*). Receives a string key parameter and returns the currently stored value for that key. If
there is no value for the key, null will be returned instead.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-19
• Remove(*key*). Receives a string key parameter and removes that entry from the storage if it exists.
The following code is an example of using html5 localStorage:
In this example, the number of visits is tracked by the localStorage. Whenever another visit to the page
occurs the number will increase and be stored in the localStorage. If the user visits the page a fifth time,
the entry will be removed and the cycle will restart. This will persist across browser resets and between
tabs.
The following code is an example of using HTML5 sessionStorage:
In this example, the number of visits is tracked by the sessionStorage. Whenever another visit to the
page occurs the number will increase and be stored in the sessionStorage. If the user attempts to open
the tab in a different tab or window, the values will be separate from the original tab. Upon closing a tab,
the data is reset.
Note: If you wish to store an object or array rather than a string, you can parse JavaScript
objects into JSON format by using the JSON.stringify() function and convert JSON data back
into JavaScript data by using the JSON.parse function.
MCT USE ONLY. STUDENT USE PROHIBITED
12-20 Performance and Communication
Lesson 3
Two-Way Communication
In the modern era, it becomes increasingly important to receive immediate updates. These days many
websites require frequent interaction between multiple users and frequently changing data. Websites
facilitating online games and chatting services, as well as providing real-time data are becoming more
and more popular. They require the creation of bi-directional websites, where the server updates the
client in addition to the client sending messages to the server. In order to facilitate this, you will need to
use methods of achieving two-way communication. First, you will be introduced to the WebSocket
protocol, which allows websites to handle two-way communication. Then you will be introduced to the
Microsoft SignalR library, which allows you to reduce a lot of the setup process required to set up such
environments while offering backwards compatibility with older browsers.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the WebSocket protocol.
• Use SignalR in an ASP.NET Core MVC application.
• Add additional configurations to alter the basic behavior of SignalR to conform to additional
requirements.
The WebSocket utilizes the existing HTTP framework to create this binding. When the initial connection
for WebSockets is made, the HTTP Upgrade header is used. This lets the server know that the WebSocket
protocol needs to be used, and as long as the server supports it, the status code 101, which represents
switching protocol, will be used. At this point, the client will send its payload and the server will store the
client details for future requests.
Once the connection has been made both the client and server can use the existing connection to send
messages to each other. This facilitates two-way communication and allows proper communication
between both server and client.
The client can also choose to close the connection at specific points, allowing the server to clear up any
related resources, and further improve our ability to handle requests. The server can also choose to close
connections, such as with a timeout, allowing the server to not retain irrelevant data for long.
The WebSocket protocol is frequently used for real-time websites such as stock sites, gaming sites, and
chatting sites, as all of those require frequent updates, often user reliant. For example, a multiplayer game
requires tracking the movement of all games, while a chat will often alert users of other users writing
messages or joining and leaving channels. Note that for websites with infrequent updates, it may be
better to perform a request every so often and the WebSocket protocol is more useful for real-time uses.
Note: While WebSockets is a standard protocol that is defined and required by the W3C
(World Wide Web Consortium) as part of a modern browser, older browsers may not support
WebSockets and other options will be required. As with many other technologies the use of
WebSockets should be considered based on the target audience for your browsers.
Using SignalR
ASP.NET Core SignalR is a library which helps to
create applications that utilize two-way
communications throughout your application.
SignalR will automatically determine and use
optimal techniques for achieving two-way
communication, preferring WebSockets when
available. However, if the browser does not
support WebSockets, other options such as long
polling will be used instead. This allows you to
create applications without having to worry
about the client browser, while still offering a
two-way communication experience.
SignalR itself is mainly handled in two sections. The first section, called the hub, is server based. The hub
declares various methods to be called by the client, with every method being able in turn to reply to one
or more clients. As an example, in a chat application, you will often want one method for chatting with all
users of the room and an additional method for chatting with a specific user.
The other section is in the client. It is responsible for connection to a specific hub, invoking methods on
the hub, and receiving messages from the hub. This can be implemented in various different ways, but in
ASP.NET Core MVC applications it is handled in JavaScript.
Note: By default, SignalR uses JSON format to transfer data, although it also supports the
MessagePack binary protocol, which can be enabled and used to transfer data even faster. Take
note that enabling it will require additional steps to parsing it on the client.
MCT USE ONLY. STUDENT USE PROHIBITED
12-22 Performance and Communication
Configure SignalR
The first step in adding SignalR to an application is to add the necessary configuration in the Startup
class. First, in the ConfigureServices method, on the IServiceCollection parameter, add a call to the
AddSignalR method.
Second, in the Configure method, on the IApplicationBuilder parameter, add a call to the UseSignalR
method. This method expects an Action that has an HubRouteBuilder generic parameter. This
parameter allows you to create routes for the various hubs in use throughout your application.
The following code is an example of configuring SignalR on the server:
app.UseSignalR(routes =>
{
});
app.UseMvcWithDefaultRoute();
In this example, you can see the basic method calls required to set up SignalR. Currently, no routes are
defined since no hub has been created yet.
Best Practice: It is a good idea to set up SignalR middleware before setting up ASP.NET
Core MVC middleware in the pipeline. This ensures SignalR related routes will not accidentally be
sent to the ASP.NET Core MVC framework.
SignalR Hubs
The next step is to set up a server hub. This can be done by adding a new class which inherits from the
Microsoft.AspNetCore.SignalR.Hub class. This class will implement the various hub methods. Generally,
hub methods will be async methods that return a Task. The only restriction on parameters is that they
have to be serializable, but any number of parameters can be used.
Inside the hub methods, you will want to also send messages to the various clients. This can be done by
using the Clients property. This property exposes a variety of possible client options you can send to, all
of which implement the IClientProxy interface. This interface exposes the SendAsync method, which
allows various overloads of sending parameters. The most common utilization for it is
SendAsync(*method*, *arg1*, *arg2*…. *argN*). All parameters are objects which are serialized into
JSON or MessagePack and then sent to the various clients. Not every method in the hub will necessarily
call the SendAsync method, but most will.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-23
The various client properties and methods implementing the IClientProxy include:
• All. Property which returns all clients connected to the hub.
• Caller. Property which returns the client which invoked the hub method.
• Others. Property which returns all clients other than the one which invoked the hub method.
• AllExcept. Method which receives a list of string connection IDs of clients and returns all clients
except for the ones with matching connection ID.
• Client. Method which receives a string with a connection ID of a specific client and returns the
matching client.
• Clients. Method which receives a list of string connection IDs of clients and returns all clients with a
matching connection ID.
• Group. Method which receives a string group name and returns all clients which have been assigned
to the matching group name.
• GroupExcept. Method which receives a string group name and a list of string connection IDs of
clients and returns all clients which have been assigned to the matching group name with the
exception of the matching connection IDs.
• Groups. Method which receives a list of string group names and returns all clients which have been
assigned to the matching group name.
• OthersInGroup. Method which receives a string group name and returns all clients which have been
assigned to the matching group name with the exception of the calling user.
• User. Method which receives a string user ID of a specific user, by default from the
ClaimTypes.NameIdentifier and returns the matching user (This can apply to more than one client if
the user is connected from multiple clients).
• Users. Method which receives a list of string user IDs, by default from the
ClaimTypes.NameIdentifier and returns the matching user (This can apply to more than one client if
the user is connected from multiple clients).
The hub can make multiple SendAsync requests, potentially with different method names, and perform
calls to additional classes that perform additional logic. You can use dependency injection with hubs, as
with other components of ASP.NET Core applications.
The following code is an example of a chat hub:
Chat Hub
using Microsoft.AspNetCore.SignalR;
In this example, you can see a chat hub. Whenever a connected client invokes the MessageAll method, all
clients will receive the message as well as the user that sent it. The clients will need to implement the
NewMessage method in order to process it.
MCT USE ONLY. STUDENT USE PROHIBITED
12-24 Performance and Communication
SignalR Routing
Once the hub has been configured, you are able to map routes to it inside the Configure method. This
can be done by calling the MapHub method on the HubRouteBuilder parameter. The MapHub method
uses the following syntax: MapHub<T>(*path*). T is the type of the hub which will be instantiated and
path is a string which represents the relative URL path on which the SignalR hub will be hosted. Once this
is done, the hub is hosted and accessible on the server.
The following code is an example of configuring SignalR hub routes:
In this example, we can see that the route /mychathub is mapped to the hub MyChatHub which was
created earlier.
HTML Code
<div>
<input type="text" class="username" />
<input type="text" class="message" />
</div>
<div>
<input type="button" class="all" value="Send To All" />
</div>
<div class="chatLog">
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-25
</div>
<script src="~/lib/jquery.js"></script>
<script src="~/lib/signalr.js"></script>
<script src="~/script/chat.js"></script>
In this example, you can see an HTML page with a text box for username, a text box for a message, a
button for sending the message, and an empty div for holding future messages.
Note: An important thing to note is that SignalR in ASP.NET Core MVC applications does
not require jQuery to be used on the client side. It is only being added in order to interact with
the DOM.
Connect to a Hub
The next step is to set up the client to handle the SignalR connection. You will need to build a connection.
In order to do this, you must create a new object of type signalR.HubConnectionBuilder. After this
object is created, you will need to call the withUrl(*hubUrl*) method on the connection builder,
specifying the URL of the hub, and finally you will need to call the build method, which returns the
connection object that you will use.
The following code is an example of building the SignalR connection:
In this example, you can see that a new signalR.HubConnectionBuilder object is created. It is then
provided with the relative URL /mychathub, and finally, the build method is called to create the
connection.
The following code is an example of listening to a method call from the server:
In this example, you can see that the client implements the NewMessage method, which receives two
parameters, sender and message. The message and sender will be appended to the chat log div as the
string Message from *sender*: *message*. This will occur every time the hub calls NewMessage.
MCT USE ONLY. STUDENT USE PROHIBITED
12-26 Performance and Communication
In this example, you can see that the connection is started. If an error will occur, it will be printed in the
browser console.
In this example, you can see that when the user clicks the button with the all class, the values of the input
with the username class and the input with the message class are extracted. They are then used when
the connection object calls the invoke method, calling the MessageAll method on the hub and providing
the sender and message as parameters.
Note: It is important to note, SignalR connections which do not use WebSockets will require
sticky servers to be defined. Since, by default, WebSockets create a socket with a specific server,
you do not need any special logic to handle this case.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Use SignalR“ on the following page:
https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD12_DEMO.md#demonst
ration-how-to-use-signalr.
Note: The Groups property is exclusively used to add to and remove from Groups and
nothing else. If you need a list of people inside a group or a list of groups you will need to
manage it yourself.
The following code is an example of a hub for a chat room which uses groups:
await Clients.Group("ChatRoom").SendAsync("UserListUpdate",
_connectedUsers.Values);
}
In this example, you can see a chat room being managed. Every user that joins is also added to the
dictionary managed by the hub and whenever the active users changed, all logged in users receive an
updated list of all connected users.
The following code is an example of the client-side code for the hub:
$(".join").click(function () {
var sender = $(".username").val();
connection.invoke("JoinChatRoom", sender);
});
$(".leave").click(function () {
connection.invoke("LeaveChatRoom");
});
$(".message").click(function () {
var message = $(".message").val();
connection.invoke("MessageChatRoom", message);
});
In this example, you can see that the client invokes the methods declared in the hub and listens to the
methods called by the hub.
The MessagePack serialization format allows you to improve performance by sending smaller more
compact data. As an additional benefit to security, it is much harder to read by looking at network traffic,
as it requires the data to be converted from a MessagePack format, whereas JSON is immediately
readable.
Using MessagePack requires a few extra steps to set up:
1. Install the Microsoft.AspNetCore.SignalR.Protocols.MessagePack Nuget package. This will load
the required server-side package to use MessagePack.
2. In the ConfigureServices method of the Startup class, in the call to AddSignalR on the
IServiceCollection object, pipe a call to AddMessagePackProtocol. This will cause the hub to
serialize and deserialize in the MessagePack format.
3. Add a dependency for @aspnet/signalr-protocol-msgpack in the package.json file. This will retrieve
the signalr-protocol-msgpack library, as well as msgpack5, which is a dependency for signalr-
protocol-msgpack.
4. Add references to msgpack5.js and then to signalr-protocol-msgpack.js in the HTML files. The
order of these two files is very important.
5. In the JavaScript file, as part of the HubConnectionBuilder pipe, add a call to
withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol)) before the call to
build. This will set up the connection to use message pack on the client.
The following code is an example of setting up MessagePack in ConfigureServices:
MessagePack in ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR().AddMessagePackProtocol();
services.AddMvc();
}
The following code is an example of setting up MessagePack as part of the SignalR connection:
MessagePack on Client
var connection = new signalR.HubConnectionBuilder()
.withUrl("/mychathub")
.withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
.build();
Note: It is also possible to use other format protocols. However, they will need additional
third-party libraries or for you to write your own.
MCT USE ONLY. STUDENT USE PROHIBITED
12-30 Performance and Communication
In this example, you can see that the server is set to close a connection if no message has been sent within
30 seconds of the initial handshake. Additionally, once a connection has been established, the server will
ping the client to prevent a timeout once every 50 seconds.
Additionally, it is also possible to set up configurations on the client side. It is possible to add an
additional options parameter to the withUrl method, allowing you to set up additional configurations.
Common configuration options include:
• transport. Allows setting which transport types are used by the client. The client will not use any ones
that are omitted and they are separated by a bitwise or operator.
• serverTimeoutInMilliseconds. Sets a period of time which the client will wait for a reply from the
server, before closing the connection.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 12-31
Objectives
After completing this lab, you will be able to:
• Implement a caching strategy.
• Manage state.
• Add two-way communication.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD12_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD12_LAK.md.
Question: A member of your team changed the Configure method in the Startup class, so
calling to the UseMvc middleware occurs before calling the UseSignalR middleware. Can you
explain to him what is the impact of his change?
MCT USE ONLY. STUDENT USE PROHIBITED
12-34 Performance and Communication
Note: Remember that all of these technologies, while extremely helpful, should be
considered before usage. If there is no complicated server-side logic or integration with external
resources, caching might not be useful. If the application is designed to work as a stateless
experience, there is no need to use state. And if the application does not require two-way
communication, you should not use SignalR. As with any other feature, consider the benefits
before using these.
Review Question
Question: How would you use of the technologies covered in this topic on a shopping website?
Module 13
Implementing Web APIs
Contents:
Module Overview 13-1
Lesson 1: Introducing Web APIs 13-2
Lesson 2: Developing a Web API 13-9
Lesson 3: Calling a Web API 13-21
Lab: Implementing Web APIs 13-30
Module Review and Takeaways 13-32
Module Overview
Most web applications require integration with external systems such as mobile applications. You need to
know how to use Web APIs to promote application interaction with external systems. You can use the
Web API to implement Representational State Transfer (REST) services in your application. REST services
help reduce application overhead and limit the data that is transmitted between client and server systems.
You need to know how to call a Web API by using server-side code and jQuery code to effectively
implement REST-style Web APIs in your application.
Objectives
After completing this module, you will be able to:
• Create services by using Microsoft ASP.NET Core Web API.
• Call a Web API from server-side code and jQuery.
MCT USE ONLY. STUDENT USE PROHIBITED
13-2 Implementing Web APIs
Lesson 1
Introducing Web APIs
HTTP is a communication protocol that was created by Tim Berners-Lee and his team while working on
the WorldWideWeb (later renamed to World Wide Web) project. Originally designed to transfer
hypertext-based resources across computer networks, HTTP is an application layer protocol that acts as
the primary protocol for many applications including the World Wide Web.
Because of its vast adoption and the common use of web technologies, HTTP is now one of the most
popular protocols for building applications and services. In this lesson, you will be introduced to the basic
structure of HTTP messages and understand the basic principles of the REST architectural approach.
Web API is a full-featured framework for developing HTTP-based services. Using Web API gives
developers reliable methods for creating, testing, and deploying HTTP-based services. In this lesson, Web
API will be introduced.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe what are HTTP services.
• Describe the structure of an HTTP message.
• Describe status codes.
• Describe Web APIs.
HTTP Services
HTTP is a first-class application protocol that was
built to power the World Wide Web. To support
such a challenge, HTTP was built to allow
applications to scale, taking into consideration
concepts such as caching and stateless
architecture. Today, HTTP is supported by many
different devices and platforms reaching most
computer systems available today.
HTTP also offers simplicity, by using text
messages and following the request-response
messaging pattern. HTTP differs from most
application layer protocols because it was not
designed as a Remote Procedure Calls (RPC) mechanism or a Remote Method Invocation (RMI)
mechanism. Instead, HTTP provides semantics for retrieving and changing resources that can be accessed
directly by using an address.
HTTP is used to develop both websites and services. Services developed by using HTTP are generally
known as HTTP-based services.
Using URI
Uniform Resource Identifier (URI) is an addressing standard that is used by many protocols. HTTP uses URI
as part of its resource-based approach to identify resources over the network.
HTTP URIs follow this structure:
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-3
• Port (optional). The port defines a specific port to be addressed. If not present, a default port will be
used. Different schemas can define different default ports. The default port for HTTP is 80.
• Absolute path (optional). The path provides additional data that together with the query describes
a resource. The path can have a hierarchical structure similar to a directory structure, separated by the
slash sign (/).
• Query (optional). The query provides additional nonhierarchical data that together with the path
describes a resource.
Different URIs can be used to describe different resources. For example, the following URIs describe
different destinations in an airline booking system:
• http://localhost/destinations/seattle
• http://localhost/destinations/london
When accessing each URI, a different set of data, also known as a representation, will be retrieved.
Using Verbs
HTTP defines a set of methods or verbs that add an action-like semantics to requests. Verbs are a central
mechanism in HTTP and it is one of the mechanisms that make HTTP the powerful protocol it is. The
following list shows the widely used verbs that are defined in HTTP:
• GET. Used to retrieve a representation of a resource. Requests intended to retrieve data based on the
request URI.
• POST. Used to create, update, and by some protocols, retrieve entities from the server. Requests
intended to send an entity to the server. The actual operation that is performed by the request is
determined by the server. The server should return information about the outcome of the operation
in the result.
• PUT. Used to create and update resources. Requests intended to store the entity sent in the request
URI, completely overriding any existing entity in that URI.
• DELETE. Used to delete resources. Requests intended to delete the entity identified by the request
URI.
Introduction to REST
Representational State Transfer (REST) describes an architectural style that takes advantage of the
resource-based nature of HTTP. It was first used in 2000 by Roy Fielding, one of the authors of the HTTP,
URI, and HTML specifications. Fielding described, in his doctoral dissertation, an architectural style that
uses some elements of HTTP and the World Wide Web for creating scalable and extendable applications.
Today, REST is used to add important capabilities to a service. Services that use the REST architectural style
are also known as RESTful services. RESTful services use the different HTTP verbs to allow the user to
manipulate the resources and create a full API based on resources.
Media Types
HTTP was originally designed to transfer hypertext. Hypertext is a nonlinear format that contains
references to other resources, some of which are other hypertext resources. However, some resources
contain other formats such as image files and videos, which required HTTP to support the transfer of
different types of message formats. To support different formats, HTTP uses Multipurpose Internet Mail
Extensions (MIME) types, also known as media types.
MCT USE ONLY. STUDENT USE PROHIBITED
13-4 Implementing Web APIs
In HTTP, media types are declared by using headers as part of a process that is known as content
negotiation. When a client sends a request, it can send a list of requested media types, and in order of
preference, it can accept them in the response. The server should try to fulfill the request for content as
specified by the client. Content negotiation enables servers and clients to set the expectation of what
content they should expect during their HTTP transaction.
HTTP Messages
HTTP is a simple request-response protocol. All
HTTP messages contain the following elements:
• Start-line
• Headers
• An empty line
• Body (optional)
Although requests and responses share the same
basic structure, there are some differences
between them of which you should be aware.
Request Messages
Request messages are sent by the client to the server. Request messages have a specific structure based
on the general structure of the HTTP messages.
This example shows an HTTP request message:
An HTTP Request
GET http://localhost:4392/travelers/1 HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US,en;q=0.7,he;q=0.3
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Accept-Encoding: gzip, deflate
Host: localhost:4392
DNT: 1
Connection: Keep-Alive
The first and the most distinct difference between the request and response messages is the structure of
the start-line, called request-lines.
Request-line
This HTTP request messages start-line has a typical request-line with the following space-delimited parts:
• HTTP method. This HTTP request message uses the GET method, which indicates that the client is
trying to retrieve a resource.
• Request URI. This part represents the URI to which the message is being sent.
• HTTP version. This part indicates that the message uses HTTP version 1.1.
Headers
This request message also has several headers that provide metadata for the request. Although headers
exist in both response and request messages, some headers are used exclusively by one of them. For
example, the Accept header is used in requests to communicate the kinds of responses the clients would
prefer to receive. This header is a part of a process known as content negotiation.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-5
Body
The request message has no body. This is typical of requests that use the GET method.
Response Messages
Response messages also have a specific structure based on the general structure of HTTP messages.
This example shows an HTTP response message:
The HTTP Response returned by the server for the above request
HTTP/1.1 200 OK
Server: ASP.NET Development Server/11.0.0.0
Date: Tue, 13 Nov 2012 18:05:11 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Content-Length: 188
Connection: Close
{"TravelerId":1,"TravelerUserIdentity":"aaabbbccc","FirstName":"FirstName1","LastName":"L
astName1","MobilePhone":"555-555-5555","HomeAddress":"One microsoft
road","Passport":"AB123456789"}
Status-Line
HTTP response start-lines are called status-lines. This HTTP response message has a typical status-line with
the following space-delimited parts:
• HTTP version. This part indicates that the message uses HTTP version 1.1.
• Status-Code. Status-codes help define the result of the request. This message returns a status-code
of 200, which indicates a successful operation. Status codes will be covered in the next topic, “Status
Codes”.
• Reason-Phrase. A reason-phrase is a short text that describes the status code, providing a human-
readable version of the status code.
Headers
Like the request message, the response message also has headers. Some headers are unique for HTTP
responses. For example, the Server header provides technical information about the server software being
used. The Cache-Control and Pragma headers describe how caching mechanisms should treat the
message.
Other headers, such as the Content-Type and Content-Length, provide metadata for the message body
and are used in both requests and responses that have a body.
Body
A response message returns a representation of a resource in JavaScript Object Notation (JSON). The
JSON, in this case, contains information about a specific traveler in a travel management system. The
format of the representation is communicated by using the Content-Type header describing the media
type.
MCT USE ONLY. STUDENT USE PROHIBITED
13-6 Implementing Web APIs
Status Codes
Status-codes are three-digit integers returned as
a part of response messages status-lines. Status
codes describe the result of the effort of the
server to satisfy the request. The next section of
the status line after the status code is the reason-
phrase, a human-readable textual description of
the status-code.
3xx – Redirection Codes that indicate that additional action • 301 Moved Permanently
should be taken by the client (usually in
• 302 Found
respect to different network addresses) in
order to achieve the result that you want. • 303 See Other
4xx – Client Error Codes that indicate an error that is • 400 Bad Request
caused by the client’s request. This might
• 401 Unauthorized
be caused by a wrong address, bad
message format, or any kind of invalid • 404 Not Found
data passed in the client’s request.
5xx – Server Error Codes that indicate an error that was • 500 Internal Server
caused by the server while it tried to
• 505 HTTP Version Not
process a seemingly valid request.
Supported
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-7
The WCF Web API team released six preview versions until in February 2012, the ware united with the
ASP.NET team, forming the ASP.NET Web API.
In June 2016, Microsoft released the first version of .NET Core and ASP.NET Core. ASP.NET Core Web API
is the framework for developing HTTP services in .NET Core.
MCT USE ONLY. STUDENT USE PROHIBITED
13-8 Implementing Web APIs
REST and Web API enable all kinds of different applications, including mobile device applications, to
interact with services. In particular, REST and Web API provide the following benefits for mobile
applications:
• They reduce the processing power needed to create complex request messages for data retrieval.
• They enhance the performance of the application by reducing the amount of data exchange between
client and server.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-9
Lesson 2
Developing a Web API
You need to know how to develop Web API for applications because Web API facilitates creating APIs for
mobile applications, desktop applications, web services, web applications, and other applications. By
creating a Web API, you make the information in your web application available for other developers to
use in their systems. Each web application has a different functional methodology; this difference can
cause interoperability issues in applications. REST services have a lightweight design, and Web API helps
implement REST services to solve the interoperability issues. You need to know how to use the different
routing methods that ASP.NET Core provides to implement REST services.
Lesson Objectives
After completing this lesson, you will be able to:
• Create a Web API for an ASP.NET Core web application.
• Describe REST services.
• Explain how to use routes and controllers to implement REST in Web APIs.
• Pass parameters to a Web API action.
• Control the response returned from a Web API action.
• Describe data return formats.
• Explore a Web API by using Microsoft Edge as a client.
Defining Routes
Web API uses routing rules to map HTTP requests to the Web API controllers and actions by using HTTP
verbs and the request URL. You can configure routes by using convention-based routing and you can
configure routes by using attributes.
MCT USE ONLY. STUDENT USE PROHIBITED
13-10 Implementing Web APIs
Configuring a Route
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
public string Get()
{
return "Response from Web API";
}
}
In the preceding code sample, observe that the route includes the literal path segment api. This segment
ensures that Web API requests are clearly separate from MVC controller routes. The placeholder variable,
[controller] helps identify the API controller to which to forward the request. As for MVC controllers, Web
API appends the word, Controller, to this value to locate the right API controller class. For example, Web
API routes a request to the api/Home URI to the HomeController controller.
RESTful Services
REST uses URLs and HTTP verbs to uniquely
identify the entity that it operates on and the
action that it performs. REST helps retrieve
business information from the server. However, in
addition to data retrieval, business applications
perform more tasks such as creating, updating,
and deleting information on the database. Web
API and REST facilitate handling such additional
tasks. They use the HTTP method to identify the
operation that the application needs to perform.
The following table provides information on
some HTTP methods that Web API and REST use.
GET Use this method with the following URL to obtain a list of all customers.
/api/customers
GET Use this method with the following URL to obtain a customer by using the ID
detail.
/api/customers/id
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-11
GET Use this method with the following URL to obtain customers by using the
category detail.
/api/customers?country=country
POST Use this method with the following URL to create a customer record.
/api/customers
PUT Use this method with the following URL to update a customer record.
/api/customers/id
DELETE Use this method with the following URL to delete a customer record.
/api/customers/id
Web API allows developers to use a strong typed model for developers to manipulate HTTP requests. The
following code shows how to use the POST, PUT, and DELETE methods for the create, update, and delete
requests to handle the creation, retrieval, updating, and deletion (CRUD) of the customer records:
CRUD Operations
[Route("api/[controller]")]
public class CustomerController : ControllerBase
{
public IEnumerable<Customer> Get()
{
// Fill content here
}
HttpGet GET
HttpPut PUT
HttpPost POST
HttpDelete DELETE
The following code illustrates the use of the HttpGet attribute on the SomeMethod action:
In the preceding code sample, Web API routes a request to the api/Home URI to the SomeMethod
action.
Note: In the preceding code example, notice that if you delete the HttpGet attribute, an
exception will be thrown when there is a request to the api/Home route. The reason is that in
this case both SomeMethod and OtherMethod match the route data and it is impossible to
decide which one should be invoked.
If you want to have multiple methods that are mapped to the same HTTP verb, you can specify a
template parameter to the Http[verb] attribute.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-13
The following code example demonstrates how to specify a template parameter to the HttpGet
attribute:
[HttpGet("Other")]
public string OtherMethod()
{
return "OtherMethod was invoked";
}
}
In the preceding code sample, Web API routes a request to the URI, api/Home/Some, to the action
called SomeMethod. Web API routes a request to the URI, api/Home/Other, to the action called
OtherMethod.
In the preceding code sample, Web API routes a request to the api/Home URI to the SomeMethod
action.
You can use the ActionName attribute to specify the action name to be used in the routing.
The following code shows how to map an action to an HTTP request by using a custom action name:
In the preceding code sample, Web API routes a request to the api/Home/SomeAction URI to the
SomeMethod action.
By default, ASP.NET Core Web API differentiates simple and complex types. Simple types are mapped
from the URI and complex types are mapped from the entity-body of the request.
The following code shows an action that gets the id parameter. The route is configured so the id
parameter is mapped as part of the absolute path of the URI. This action method is chosen when sending
a GET request by using the api/Home/1 path:
You can use the template parameter of the HttpGet, HttpPut, HttpPost, and HttpDelete attributes to
map the route and the parameters of the action.
The following code shows how to pass a template to the HttpGet attribute. The route is configured so the
id parameter is mapped as part of the absolute path of the URI. This action method is chosen when
sending a GET request by using the api/Home/1 path:
The following code shows an action that gets two parameters named id and name. The route is
configured so the id and name parameters are mapped as part of the absolute path of the URI. This
action method is chosen when sending a GET request by using the api/Home/1/Mike path:
• FromForm. Determines that the parameter is bound by using form-data in the request body.
• FromHeader. Determines that the parameter is bound by using the request headers.
The following example demonstrates how to use the FromBody attribute to determine that a parameter
is bound by using the request body:
ApiController Example
[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
[HttpPost]
public void Post(Customer item)
{
}
}
exception was thrown. In case there is an unhandled exception in the action method, a 5xx error is
returned.
The following code shows an action method that returns a value of the string type. In case the id
parameter is not negative, the status code of the response is 200. In case the id parameter is negative, the
status code of the response is 500:
Return IActionResult
An action method can return an object that implements the IActionResult interface. This gives the
flexibility to return a status code that is different from 200. For example, to return a 404 Not Found status
code you can return a NotFoundResult object by using the NotFound method. If the status code of the
response is 200, you can use an Ok method to return the content.
The following example shows an action method named Get that gets a parameter named id. If a
dictionary contains an item with a key that is equal to id then the value is returned by using the Ok
method, otherwise a response with status code 404 is returned:
public ValuesController()
{
_items["key1"] = "value1";
_items["key2"] = "value2";
}
[HttpGet("{id}")]
public IActionResult Get(string id)
{
if (_items.ContainsKey(id) == false)
return NotFound();
return Ok(_items[id]);
}
}
Return ActionResult<T>
In addition to returning an IActionResult, a Web API controller action can return ActionResult<T>.
Returning ActionResult<T> enables you to return a specific type or an object which inherits from
ActionResult.
MCT USE ONLY. STUDENT USE PROHIBITED
13-18 Implementing Web APIs
The following example demonstrates how an action can return ActionResult<T>. This example is
equivalent to the previous example in which the action returned IActionResult:
public ValuesController()
{
_items["key1"] = "value1";
_items["key2"] = "value2";
}
[HttpGet("{id}")]
public ActionResult<string> Get(string id)
{
if (_items.ContainsKey(id) == false)
return NotFound();
return _items[id];
}
}
[Route("api/[controller]")]
[ApiController]
public class PersonController : ControllerBase
{
public IEnumerable<Person> Get()
{
List<Person> people = new List<Person>();
people.Add(new Person() { ID = 1, Name = "Bob" });
people.Add(new Person() { ID = 2, Name = "Mike" });
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-19
return people;
}
}
The following code shows a JSON response from the Get action:
A JSON Response
[{"ID":1,"Name":"Bob"},{"ID":2,"Name":"Mike"}]
Output Formatters
Web API uses an output formatter to format or serialize the information that a Web API REST service
returns. Web API usually uses the default formatter to return a data in JSON format from an action.
However, you can alternatively use other output formatters. To use an output formatter, you need to
configure ASP.NET Core MVC to support it. You can use pre-defined output formatters such as an XML
output formatter. You can also use custom output formatters to format the responses.
Adding XmlSerializerOutputFormatter
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
});
}
Alternately, you can add the XML serializer formatters to MVC by using the AddXmlSerializerFormatters
method, as shown in the following example:
An XML Response
<ArrayOfPerson>
<Person>
<ID>1</ID>
<Name>Bob</Name>
</Person>
<Person>
<ID>2</ID>
<Name>Mike</Name>
</Person>
MCT USE ONLY. STUDENT USE PROHIBITED
13-20 Implementing Web APIs
</ArrayOfPerson>
Demonstration Steps
You will find the steps in the section “Demonstration: How to Develop a Web API“ on the following page:
https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD13_DEMO.md#demonst
ration-how-to-develop-a-web-api.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-21
Lesson 3
Calling a Web API
After you complete the development of the Web API, you can create the client applications that call it.
You need to know how to call a Web API by using jQuery code. You also need to know how to call a Web
API by using server-side code, how to get a complex object from a Web API, and how to pass a complex
object to a Web API.
Lesson Objectives
After completing this lesson, you will be able to:
• Call Web APIs by using jQuery.
• Call Web APIs by using server-side code.
• Get complex objects from a Web API.
• Pass complex objects to a Web API.
• Asynchronous JavaScript and XML (AJAX). Using AJAX, you can send requests from the client after the
browser completes loading the HTML. Based on the result of the calls, you can use JavaScript to
update parts of the HTML page.
Note: jQuery is introduced in Module 8, "Using Layouts, CSS and JavaScript in ASP.NET
Core MVC".
MCT USE ONLY. STUDENT USE PROHIBITED
13-22 Implementing Web APIs
You can use jQuery to generate an HTTP request from a browser to a Web API by using the jQuery ajax
function. In the following example, you will first see a Web API controller that has a Get method and then
you will see how to call the Get method by using the jQuery ajax function.
The following code shows a Web API controller called ValuesController that has a Get method:
public ValuesController()
{
_items["key1"] = "value1";
_items["key2"] = "value2";
}
[HttpGet("{id}")]
public ActionResult<string> Get(string id)
{
if (_items.ContainsKey(id) == false)
return NotFound();
return _items[id];
}
}
In the example above, the Get method can be invoked by using the relative URL api/Values/{id}. The id
segment should be specified by the client. For example, if a client requests the relative URL
api/Values/key1, the content value1 will be returned to the client and the status code of the response
will be 200. On the other hand, in case a client requests the relative URL api/Values/key3, the status code
of the response will be 404 (Not Found).
The following code shows how to use jQuery to call the Get method of the ValuesController Web API
controller by using the relative URL api/Values/key1. Running this code causes the text value1 to be
displayed on the browser:
</html>
If the url parameter is changed to http://localhost:[port]/api/values/key3, running the code will cause
a pop-up window with the text An error has occurred to be displayed in the browser.
public ValuesController()
{
_items["key1"] = "value1";
_items["key2"] = "value2";
}
[HttpGet("{id}")]
public ActionResult<string> Get(string id)
{
if (_items.ContainsKey(id) == false)
return NotFound();
return _items[id];
}
[HttpPost]
public IActionResult Post(Entry entry)
{
if (_items.ContainsKey(entry.Key) == true)
{
return BadRequest();
}
_items.Add(entry.Key, entry.Value);
return CreatedAtAction(nameof(Get), new { id = entry.Key }, entry);
}
}
In the Post method, the BadRequest and CreatedAtAction methods are used to give the client an
indication of whether the Post method succeeded or failed. In case the Post method succeeds, a status
code of 201 (Created) is returned to the client. In case the Post method fails, a status code of 400 (Bad
Request) is returned to the client.
MCT USE ONLY. STUDENT USE PROHIBITED
13-24 Implementing Web APIs
The following code shows how to use jQuery to call the Post method of the ValuesController Web API
controller:
In the preceding code sample, observe the data parameter of the ajax function. You can use the
JSON.stringify method in the data parameter of the ajax function to serialize the JavaScript object into a
JSON object, which will be sent to the Web API method.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Call Web APIs by Using jQuery Code” on the
following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD13_DEMO.md#demonst
ration-how-to-call-web-apis-using-jquery-code.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-25
After the IHttpClientFactory service is registered, dependency injection can be used to inject it. Once
injected, you can use the CreateClient method to get an instance of type HttpClient.
The following example demonstrates how to create an instance of type HttpClient inside a controller:
public ValuesController()
{
_items["key1"] = "value1";
_items["key2"] = "value2";
}
[HttpGet("{id}")]
public ActionResult<string> Get(string id)
{
if (_items.ContainsKey(id) == false)
return NotFound();
return _items[id];
}
}
The following code shows how to use the HttpClient class to call the Get method of the
ValuesController Web API controller by using the relative URL api/Values/key1. Running this code
causes the text value1 to be displayed on the browser:
The following code shows a Web API controller called PersonController that has a Get method. The Get
method returns an instance of type Person:
The following code shows how to use the HttpClient class to call the Get method of the
PersonController Web API controller by using the relative URL api/Person. Running this code causes the
text Mike to be displayed on the browser:
The following code shows a Web API controller called ValuesController that has a Post method. The
Post method gets an instance of type Entry as a parameter:
public ValuesController()
{
_items["key1"] = "value1";
_items["key2"] = "value2";
}
[HttpGet("{id}")]
public ActionResult<string> Get(string id)
{
if (_items.ContainsKey(id) == false)
return NotFound();
return _items[id];
}
[HttpPost]
public IActionResult Post(Entry entry)
{
if (_items.ContainsKey(entry.Key) == true)
{
return BadRequest();
}
_items.Add(entry.Key, entry.Value);
return CreatedAtAction(nameof(Get), new { id = entry.Key }, entry);
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET Core MVC Web Applications 13-29
}
}
The following code shows how to use the HttpClient class to call the Post method of the
ValuesController Web API controller by using the relative URL api/Values. Running this code causes the
text succeeded to be displayed on the browser:
Demonstration Steps
You will find the steps in the section “Demonstration: How to Call Web APIs by Using Server-Side Code”
on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD13_DEMO.md#demonst
ration-how-to-call-web-apis-using-server-side-code.
MCT USE ONLY. STUDENT USE PROHIBITED
13-30 Implementing Web APIs
Objectives
After completing this lab, you will be able to:
• Add actions to a Web API application.
• Call Web API actions by using HttpClient.
• Call Web API actions by using jQuery.
Lab Setup
Estimated Time: 60 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD13_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD13_LAK.md.
Review Question
Question: What are ASP.NET Core Web API controllers used for?
Best Practice
Use ActionResult<T> or IActionResult to return a valid HTTP response message.
Module 14
Hosting and Deployment
Contents:
Module Overview 14-1
Lesson 1: On-Premises Hosting and Deployment 14-2
Lesson 2: Deployment to Microsoft Azure 14-19
Lesson 3: Microsoft Azure Fundamentals 14-29
Lab: Hosting and Deployment 14-42
Module Review and Takeaways 14-44
Module Overview
Microsoft ASP.NET Core MVC applications are designed to simultaneously provide a service to multiple
users with only a server installed, and the clients use their browsers to access the application. This results
in applications that do not require the user to install dedicated software. This ensures that clients can
access these applications on a wide range of devices.
To set up an ASP.NET Core application for a production environment, you will need to compile your code
and compress it, set it up, and then run it on a dedicated server.
Hosting involves using a dedicated server to contain the compiled application and serve it to users as a
web-based service. There are many different technologies that you can use to host your application, and
you should choose one that is appropriate for your requirements.
Deployment is the process where the project code is compiled and then transferred to the hosting server
by using a method that functions within the requirements of the hosting environment.
Microsoft Azure is a cloud service provided by Microsoft. It can be used to host ASP.NET Core applications
and is a popular tool in the cloud technology market. It provides convenient web application related
services with multiple billing options that can be selected according to user requirements.
Objectives
After completing this module, you will be able to:
• Host and Deploy an ASP.NET Core MVC application on Internet Information Services (IIS).
• Host and Deploy an ASP.NET Core MVC application on Azure.
• Use Azure to improve the capabilities of your web applications.
MCT USE ONLY. STUDENT USE PROHIBITED
14-2 Hosting and Deployment
Lesson 1
On-Premises Hosting and Deployment
While developing an ASP.NET Core application, a development environment based in Microsoft Visual
Studio is perfectly reasonable for day-to-day development. However, for a testing environment that
simulates production and for a production environment, you will need to be able to manage standalone
copies of your application.
By deploying your application and creating a hosting environment you can start up a permanent server
that can be accessed through a network, which allows multiple people to work on the application
simultaneously. This is the final part of developing a web application because web applications, by
definition, have to be accessible through a network.
In this lesson, you will learn about web servers and how to differentiate the web servers available in
ASP.NET Core. You will then learn how to publish your existing ASP.NET MVC application by converting it
to a collection of compiled and non-compiled files, which you can then use to publish to a web server.
You will also learn how to set up hosting in an IIS environment, creating a permanent server to host your
applications. Finally, you will also learn how to configure the application to safely interact with files used
by your application.
It is important to understand that web applications rely on being accessible to users. This applies to
applications such as in-company management applications that are designed to work in a closed
environment and applications designed to work on the Internet. By deploying and hosting the application
you can fulfill those requirements.
Lesson Objectives
After completing this lesson, you will be able to:
• Describe the server options provided by ASP.NET Core applications and their differences.
• Create and deploy a deployment build of an ASP.NET Core MVC applications.
• Host an ASP.NET Core application on IIS.
• Add important files, which can be accessed at run time, to your application.
Web Servers
Being a web technology, ASP.NET Core
application will require the setup of dedicated
web servers to run. To set up web servers, you
can choose from several different options based
on your requirements.
ASP.NET core applications run on a process-
based server implementation, which listens to
requests from clients, and uses them to create an
HTTPContext object. This object is then injected
into the middleware, and you can use it to
control the flow of your application.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-3
Kestrel
Kestrel is the default server implementation for ASP.NET Core, and it can be used with or without a
reverse server proxy. Since Kestrel is designed to work cross-platform, it runs on its own process, allowing
the use of technologies such as IIS, NGINX, or Apache. Those technologies cannot be used without Kestrel
in ASP.NET Core.
Kestrel supports features such as HTTPS, WebSockets, and performance improvements while running on
NGINX on Unix platforms. It is cross-platform and can be used across many different operating systems. It
is a lightweight web server designed for efficiency and quick performance but does not support many
features such as windows authentication, port sharing, direct file transmissions, and response caching.
To set up Kestrel, all you need to do is to call WebHost.CreateDefaultBuilder(*arguments*) in the
CreateWebHostBuilder method of the Program.cs file. By default, all new ASP.NET Core applications
are created with this configured, and the CreateDefaultBuilder method will call the UseKestrel()
method behind the scenes.
The following example is a basic Kestrel web server configuration:
You can also further customize the Kestrel options by manually calling the
UseKestrel(*Action<KestrelOptions>*) method as part of the CreateDefaultBuilder pipeline. You can
configure many settings here. For example, by using the Limits property of the Kestrel Options object,
you can set a variety of connection limits such as:
MCT USE ONLY. STUDENT USE PROHIBITED
14-4 Hosting and Deployment
• MaxConcurrentConnections. Used to set how many connections the server can handle at a time.
• MaxConcurrentUpgradedConnections. Used to set the number of upgraded connections, such as
those used in WebSockets.
• MaxRequestBodySize. Limits the size of the body in requests, set in number of bytes.
• KeepAliveTimeout. Sets the timeout for connections.
The following code demonstrates basic configurations for a Kestrel Server:
In this example, you can see that the Kestrel server is configured to support up to 100 concurrent
connections, 50 upgraded connections (such as connections from web sockets), accepts requests of up to
two MB, and times out connections after three minutes.
Note: There are many additional possible configurations for Kestrel according to
requirements. To learn more, go to: https://aka.ms/moc-20486d-m14-pg1
HTTP.sys
HTTP.sys is an alternative to Kestrel if ASP.NET Core is running on Windows. HTTP.sys supports Windows 7
or later and Windows Server 2008 R2 or later. It is a complete and robust web server implementation,
which, unlike Kestrel, does not require external hosting solutions. However, it is not a lightweight solution.
HTTP.sys supports Windows Authentication, running multiple applications on the same port, HTTPS, Direct
transfer of files, caching responses, and WebSockets. However, you can run HTTP.sys only in a Windows
environment, and it does not support reverse proxy. It is also a heavy implementation, intended to handle
all facets of a web server, and HTTP.sys does not work in conjunction with IIS or IIS Express.
Best Practice: For the purposes of compatibility and performance, we recommend using
Kestrel as your web server. However, if a Windows Server is usable for your requirements and you
require some of its features such as Windows Authentication, you can use HTTP.sys
In order to setup HTTP.sys, you will need to perform the following steps:
1. After calling the CreateDefaultBuilder(*arguments*) method in the application builder, you will
need to pipe in a call to the UseHttpSys() as part of the builder. This will set up HTTP.sys with a basic
configuration.
2. Add a new debug environment profile that does not use IIS or IIS Express, such as project in the
project properties debug menu or in the launchsettings.json file. This was covered in Module 10,
"Testing and Troubleshooting".
3. Select the new environment in the debug menu and start the application.
After these steps are completed, your application will be running in HTTP.sys. However, you might want to
configure additional parameters.
You can configure additional parameters by using an Action that returns an HttpSysOptions object. This
object allows you to configure several options that can affect the behavior of the server. As an example,
several properties that you can use include:
• Authentication. Exposes various sub-properties that can affect the application authentication, such
as the Schemas property to set the application authentication schema.
• MaxConnections. Sets the maximum number of concurrent connections.
• MaxRequestBodySize. Sets the limit for the size of the body on requests.
Configuring HTTP.sys
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseHttpSys(options =>
{
options.Authentication.Schemes =
Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes.None;
options.MaxConnections = 50;
options.MaxRequestBodySize = 2 * 1024 * 1024;
options.UrlPrefixes.Add("http://localhost:5250");
})
.UseStartup<Startup>();
In this example, you can see that the application is set to not use an authentication schema, the
application is also set to allow for up to 50 concurrent connections and accept requests with a body that is
up to two MB in size. It is hosted with the URL http://localhost:5250. Attempting to access it with a
different URL, including the default http://localhost:5000 will fail.
Note: There are many additional configurations, and some configurations can only be
updated by using the Windows Registry. For more information go to: https://aka.ms/moc-
20486d-m14-pg2
Note: OWIN is designed around ASP.NET rather than ASP.NET Core. Servers that run OWIN
architecture will use ASP.NET. However, it is possible to create client applications, which interface
with OWIN, in ASP.NET Core.
The ASP.NET Core module is responsible for managing the IIS process and runs in a separate process.
When the initial connection occurs, the ASP.NET Core Module will start the kestrel application, and it will
also restart it the application has crashed.
Forwarding occurs in the ASP.NET Core Module when the client communicates with the IIS server that
hosts the ASP.NET Core Module. The ASP.NET Core Module will then create an internal HTTP connection
(It will use HTTP even when the connection with the client is HTTPS), to the ASP.NET Core application that
runs on a Kestrel server. It will access the ASP.NET Core application by using an environment variable that
is configured in the IIS startup. Kestrel will then handle the request in the normal flow before returning
the response to the IIS server, which will forward it to the original client.
Note: The ASP.NET Core module is an IIS-based solution, which requires an IIS Server. If
you work on a multiplatform or non-Windows environment, you can use Apache or NGINX
instead. You can use either solution as reverse proxies when using a Kestrel server-based
application.
To host a server, use the dotnet run command. This will run the build process, and then run the project in
the current folder. This will allow you to run your project even without access to Visual Studio. This
solution works across different operating systems. It can also be used alongside projects in Visual Studio
Code, which allows you to bypass the need to install related extensions. Note that calling dotnet run will
use any cached NuGet packages if they are available and will only download any which are missing.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-7
Best Practice: While working on a production environment, it is best to use dotnet run
because the faster setup is conducive to rapid testing. However, for production environments, it
is best to use dotnet publish. This can allow you to publish once, create the necessary DLL files
and ensure the latest relevant NuGet packages are used. Also, it is the recommended solution for
hosting on production servers. You will learn about dotnet publish in the second topic of this
lesson, "Hosting ASP.NET Core Application".
dotnet run
dotnet run
As an alternative option to navigating to the project file, you can choose to provide the dotnet run
command a full file path to run from. This will remove the need to browse to the project folder.
If you use FDD, the deployment will not contain any .NET Core specific files because the files installed on
the host will be used instead. You also do not have to specify a target operating system. This creates a
smaller package, while also using the same .NET Core application installation as other applications on the
server. However, using FDD requires the host to have the .NET Core version used by your application or a
newer version, and if the .NET Core libraries are changed significantly enough in a future version, the
application may start working in an unexpected manner.
If you use SCD, the deployment will contain a specific version of .NET Core, which is selected by you and
will be packaged alongside your application. This ensures that you will not need to install it on the hosting
server and your application will keep working even if the host updates its versions of .NET Core. However,
because .NET Core is included as part of your package, you will need to select target platforms in advance,
each additional platform increasing the installation size. This can particularly be troublesome on servers
that run multiple .NET Core-based installations because a lot of space and memory in those servers might
be taken up by multiple .NET Core installations that are running simultaneously.
Note: Note that any third-party files used by your application will also be present in both
FDD and SCD deployments.
By default, a new project in a Visual Studio environment will be set with Debug configurations. However,
builds that are intended for production environments will usually use Release configurations. You will
need to set up the Release configurations to suit your need.
In order to set up the project for a publishing build in Visual Studio, in Solution Explorer, right-click the
project, and then select the properties from the context menu. To update settings related to builds and
publishing, click the Build tab. To publish for a production build, select Release from the Configuration
drop-down list. This will ensure your project is set up for the release environment. Then go to the Output
section of the Build properties. Inside the Output path box, set an output path of your choice or use the
default one. This will determine where the compiled DLL files will be stored for the project. Note that it
will add the version of the .NET Framework as a part of your path in the following format: "*your
path*\*.NET Core version*\". Remember the final path because you will be using it later.
After this is done, browse to the project in your command line console of choice and call the dotnet
publish command, just like you would call the dotnet run command. Rather than immediately running
the application, it will get the latest version of all used NuGet packages and create a publish directory by
using the directory you set in the Output path inside the publish folder. The resulting directory contains
the compiled version of your application, and you can copy it to additional locations such as production
servers.
To test your published build, you can call the dotnet command. The dotnet command accepts a path to
a DLL file that contains a .NET Core application. It will then host the application in the command line
console the command is called from. Remember that calling the dotnet CLI will require you to install
the .NET Core SDK.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-9
dotnet publish
dotnet publish
cd Publish\netcoreapp2.1\publish
dotnet MyApp.dll
In this example, you can see a project named MyApp being published. We publish it to the publish folder
according to the build properties set for it. We publish it by calling the dotnet publish command under
the publish folder. Then, we browse to the publish folder that has the compilation version for the project
(in this case "netcoreapp2.1") and the output path which is set in the project properties, and then use the
dotnet command with the DLL to run the application and ensure it is working.
Note: The process described here covers an extremely basic publishing flow, and you may
require additional settings and configurations. To read more about publishing using the .NET CLI
go to: https://aka.ms/moc-20486d-m14-pg3
IIS
Internet Information Services (IIS) is a server architecture developed by Microsoft for handling requests. It
is a popular option for maintaining web server processes, and natively supports many Windows-related
technologies such as Windows Authentication. It is only supported in Windows Servers, and as such, you
should validate your business requirements before using it to host your application.
IIS is highly flexible and manageable, allowing for features to be enabled and disabled as required. This
can prevent issues caused because of applications running multiple unrequired features, leading to
wasted resources.
Best Practice: Being designed specifically for Windows Servers, IIS can be a good choice
when the web servers are running Windows-based operating systems. However, it does not
support Linux-based servers at all.
Deployment to an IIS Server will be covered in more depth in Topic 3, "Deploying to IIS".
NGINX
NGINX is a process manager that is primarily used on Linux servers. It is a popular web server that can act
as a standard server, as well as a reverse proxy server. In addition, it can act as a general usage TCP/UDP
server. While you can run it on Windows servers, it is not optimized for it, and will generally
underperform.
MCT USE ONLY. STUDENT USE PROHIBITED
14-10 Hosting and Deployment
NGINX works by utilizing a single master process and multiple worker processes. The master process
mostly handles the NGINX configurations and ensures that the configurations are used, as well as ensures
that worker processes are maintained. In turn, the worker processes handle the actual processing of the
various communication requests. The number of workers is dependent on the configuration file.
To find out more details on how to host ASP.NET Core applications on NGINX, go to: https://aka.ms/moc-
20486d-m14-pg4
Note: If you decide to use Linux-based hosting for your application, NGINX is a good
choice to make because it is optimized to run on Linux. However, if a Windows Server is required,
it is a poor choice and should be avoided.
Apache
Apache is an open-source HTTP Server designed to operate effectively across multiple operating systems.
It is designed to be flexible, highly compliant with technologies, and offers great extensibility through
third-party modules because of it being an open-source tool. It is also constantly evolving, with new
technologies being added on a frequent basis. Its main strength is the flexibility of use across different
platforms.
The Apache HTTP server is configured by using various text files. These configurations further specify its
behavior, enabling or disabling features as needed, including the behavior of third-party libraries that are
in use.
For more details on how to host ASP.NET Core applications on NGINX, go to:
https://aka.ms/moc-20486d-m14-pg5
Note: Apache can be a good server solution when multiple servers on different operating
systems are required, such as when multiple clients require their own servers. You can learn how
to deploy applications using Apache faster than learning how to deploy to both IIS and NGINX to
deploy to different servers. In addition, Apache is the easiest solution when trying to support
deployments across multiple different servers.
Windows Services
A less common solution for a process manager is to host your application on a Windows Service. Unlike
the previous options, Windows Service is not designed as a dedicated HTTP Server and therefore lacks
many of the features common in other options. However, all installations of the Windows operating
system include Windows Services as a core feature and as such, it can be used on any Windows
installation.
When installing an ASP.NET Core application on a Windows Service, you can manage it through the
Windows Services, enabling a relatively simple setup.
For more details on how to host ASP.NET Core applications on Windows Services, go to:
https://aka.ms/moc-20486d-m14-pg6
Note: Windows Service hosting can be a reasonable choice if there is a requirement for
minimum external requirements because it can work without additional applications. However, it
is far less configurable than other options and requires Windows. Use this option when targeting
Windows and IIS is not a viable option.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-11
Note: The process for setting up the reverse proxy will vary between the different server
types, and each one works differently.
When possible, it is almost always a good idea to use a reverse proxy, but you should analyze the specific
requirements of your applications before making the decision.
Load Balancing
Often, modern applications are designed to be scalable, leading to a requirement for load balancing. A
load balancer is a system designed to direct incoming HTTP traffic between multiple servers. It will prefer
sending new requests to servers with a lesser load at the time, allowing to optimize the user experience by
providing a connection to available servers, while avoiding creating connections with busy ones.
If you use a reverse proxy, you might use one server to handle both load balancing and act as the reverse
proxy. However, it is possible to keep these two systems separate, in which case, the load balancer will
have to be between the application and the proxy server.
To learn how to add a reverse proxy server and implement load balancing, go to: https://aka.ms/moc-
20486d-m14-pg7
Deploying to IIS
One of the most convenient options for hosting
an ASP.NET Core MVC application is to utilize IIS.
You can easily set up IIS on Windows-based
servers. As such IIS makes for a good host for
your ASP.NET Core applications.
However, the process itself is not immediate, and
you need to perform several important steps to
correctly set up the server. In this topic, we will
discuss the various steps and additional
requirements to ensure that you can correctly
deploy your application.
Note: Note that installing IIS may require administrative permissions and the process
differs between Windows Server and Windows Home editions. If you encounter any permission
trouble trying to set it up, you should ask your system administrators for help.
When IIS is set up, you will need to download and install the .NET Core Runtime. You can download .NET
Core Runtime from the following link: "https://www.microsoft.com/net/download". You will need to install
it after installing IIS. You will also need to reset the IIS process in order for it to load ASP.NET Core
configurations. If you install.NET Core Runtime before you install IIS, you will need to rerun the .NET Core
Runtime installer to repair the installation.
Note: Ensure that you download.NET Core Runtime and not.NET Core SDK. It should
appear next to the text "Run Apps", and the downloaded execution file should contain hosting
as part of its name. Note that .NET Core SDK is not sufficient for IIS configuration.
2. Ensure that a logs folder is present. If it is not present, create one. This will ensure logs from the
stdout will be added to the Logs folder. Alternatively, different configurations can be set from the
web.config file.
3. Open the IIS Manager, and in the connection segment on the left, open the server node by clicking
the arrow next to it.
4. Under the server node, right-click the Sites node, and then select Add Website….
5. In the Add Website window, set the Site name to the name you wish to choose and set the Physical
path to the folder you created in the first step. You can also set the bindings to use either HTTP or
HTTPS as needed, set an IP address from the addresses available to the server and the port. By
default, HTTP uses port 80, and HTTPS uses port 443. If you know what host name the application will
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-13
be served under, you can provide it now, if not you can change the host name later. After filling out
your requirements, click OK.
6. At this point, if a default web site or another web site is already set up on your chosen port, you will
get a notification message that only one site is supported for a single port. You can choose to either
accept (you will only be able to run one site at once) or click No and change the port.
7. If you want the application to run in a process managed by ASP.NET Core instead of through IIS, you
must also complete the following optional steps. This can help preserve the usage of specific versions
of ASP.NET Core Runtime:
a. In the connection type, under the server node, select Application Pools.
b. Right-click the application you added and click the Basic Settings… option from the menu.
c. From the .NET CLR version drop-down list, select No Managed Code, and then click OK.
Once you have completed these steps, you have set up the IIS infrastructure for your application.
File Providers
Sometimes your applications are reliant on
physical files in the project to behave correctly.
These can be additional configuration files, files
used for storage, custom files for specific
deployments, logging files for application log
management and more.
In an ASP.NET Core MVC application, you can
easily access any file or directory within the
project structure by using file providers. This
allows you to focus on the project structure itself,
and not deal with the headaches of project
structures, which may change as part of the build
and publish process. In fact, both the UseStaticFiles() middleware and the Razor engine use the built-in
File providers to locate resources, pages, and views.
In general, the primary interface you will use while working with files is IFileProvider. It provides methods
for reading files and directories.
MCT USE ONLY. STUDENT USE PROHIBITED
14-14 Hosting and Deployment
There are three separate implementations for IFileProvider available in ASP.NET Core applications:
• PhysicalFileProvider. Used to access files within the file system scoped to a specific root directory.
This will be used for reading various non-compiled files such as XML, Text, and even HTML files.
• ManifestEmbeddedFileProvider. Used for accessing files embedded within the project assemblies.
This can allow access to files that are normally embedded to the compiled assemblies of your
ASP.NET Core application.
• CompositeFileProvider. Used for combining multiple other providers. This can allow you to use a
single provider that can handle both compiled and non-compiled files.
Note: While using file providers, you must take great care with sending any information
from files to the client. You have to make sure that no sensitive information is sent.
PhysicalFileProvider
You can use the most commonly used file provider, PhysicalFileProvider, to read physical operating files
such as XML configurations, log text files, JSON based resources, and media files.
PhysicalFileProvider uses the System.IO.File library for file management. However, it limits the file
scope to a base directory and any children files and directories inside it. This makes it a safe option
because it cannot access files from outside the project. Therefore, it cannot be used maliciously to cause
harm to other applications. PhysicalFileProvider uses a directory path in its constructor to set the base
directory, limiting access to subdirectories.
A convenient alternate option to instantiating a PhysicalFileProvider manually is to use the
IHostingEnvironment service, which was covered in Module 10, "Testing and Troubleshooting". The
IHostingEnvironment service exposes the ContentRootFileProvider property. This is a pre-instantiated
PhysicalFileProvider originating in the application root. By using this, you can safely use a
PhysicalFileProvider, which is already configured to use the root of the application.
The following code is an example of getting PhysicalFileProvider from IHostingEnvironment:
After obtaining a PhysicalFileProvider, you can then use the GetFileInfo(*filePath*) method to get an
IFileInfo object, which is used for interacting with files. The file path parameter supplied to GetFileInfo
should direct to the file you wish to read.
In order to read the actual file, you will need to use the CreateReadStream() function on the IFileInfo
object. This will create a memory stream designed to hold the content of the file as it is read. It is advised
to encapsulate this step in a using statement, as the stream implements IDisposable and creates a
connection to the file.
After the stream is established, you will need to use it to instantiate a new StreamReader(*stream*)
object, which is used for performing the actual reading. It requires the previously created stream as a
parameter.
Finally, you can use the various functionalities of the StreamReader to read the file. For example,
ReadToEnd() will read all segments of the stream, which have not yet been read.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-15
The following code is an example of using a file provider to read a text file:
if(fileInfo.Exists)
{
using (Stream fileStream = fileInfo.CreateReadStream())
{
StreamReader streamReader = new StreamReader(fileStream);
content = streamReader.ReadToEnd();
}
}
}
catch (Exception)
{
content = string.Empty;
}
return Content(content);
}
In this example, you can see that a file provider is used to read the "appText.txt" file from the root
directory and return its content.
Best Practice: Due to a dependency on sources which are external operations to the
application, the methods for interacting with files should be encapsulated with a try/catch block.
This can help prevent unexpected failures.
ManifestEmbeddedFileProvider
Another useful option for a file provider, particularly when security is a big concern, is using
ManifestEmbeddedFileProvider. Compared to PhysicalFileProvider, this file provider offers an option
to use files specifically embedded into the application DLL. This can act as an additional layer of security,
allowing you to add sensitive files inside the application and use them internally or expose specific
segments as needed.
The first part of this process is to set up the project configuration to support manifest embedded files. By
right-clicking the project and selecting Edit *Project file*, you can edit the project configuration. Under
the PropertyGroup element, you will need to add a new GenerateEmbeddedFilesManifest element.
Inside the element, set the value to true to support embedding files into the manifest.
You will then need to find the ItemGroup element. Inside it, you can add EmbeddedResource tags to
specify files that will be embedded inside the application. Each EmbeddedResource must contain the
Include attribute, and the value provided for the attribute needs to be a valid file path. You can either use
the wildcard * in your paths, or you can provide specific file paths, so you could use the string '*.txt' to
embed all text files in the root directory, or you can provide a specific file.
The following code is an example of a csproj file with embedded resources:
MCT USE ONLY. STUDENT USE PROHIBITED
14-16 Hosting and Deployment
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="*.txt" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design"
Version="2.1.0" />
</ItemGroup>
</Project>
Once this is done, you can create a new ManifestEmbeddedFileProvider(*assembly*) to receive a file
provider. To get the root of the current assembly, you can use
System.Reflection.Assembly.GetEntryAssembly() as the assembly parameter, which will be used by
the ManifestEmbeddedFileProvider to act as a file provider for embedded files. This will allow you
runtime access to files that are not actually present as physical files.
The following code is an example of ManifestEmbeddedFilerProvider:
ManifestEmbeddedFileProvider
private readonly IFileProvider _fileProvider;
public HomeController()
{
_fileProvider = new ManifestEmbeddedFileProvider(Assembly.GetEntryAssembly());
}
Note that only the method for instantiating the file provider differs from using physical file providers.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-17
Note: It is important to note the distinction that embedded files are added as part of the
assembly at the compilation stage, while physical files remain separate from the assembly.
CompositeFileProvider
Occasionally, you may end up in situations where you need both external resources (such as configuration
files) and embedded files (files with sensitive data). In such a case, rather than separately maintaining
multiple separate file providers inside your application, you can instead use the CompositeFileProvider
to unify several different providers.
Note: Note that you are not limited to using CompositeFileProvider to merging
PhysicalFileProvider and ManifestEmbeddedFileProvided. You can also use it to merge
physical file providers that rely on separate file paths.
In order to set up CompositeFileProvider, you will need to first set up at least one of the providers it will
use. As such, it is recommended to set up CompositeFileProvider as a service in the ConfigureServices
method. To do this, create a new CompositeFileProvider and provide it with an array of file providers.
Note that because IHostingEnvironment is not injected into the ConfigureServices method, you will
need to add a constructor to the Startup class and add it as a property. Once the composite provider
has been instantiated, use services.AddSingleton<IFileProvider>(compositeProvider) to add it as a
service.
IFileProvider compositeProvider =
new CompositeFileProvider(physicalProvider, manifestEmbeddedProvider);
services.AddSingleton<IFileProvider>(compositeProvider);
services.AddMvc();
}
Best Practice: Note that you can add any of the three provider types as an injectable
service in a similar way. However, only a single provider can be used for injection because they all
use the same interface. It is therefore always preferable to inject the CompositeFileProvider
whenever multiple providers are used in the application.
For the next step, inject the composite file provider into the desired location, and then use it to interact
with files in the same way as the other providers.
MCT USE ONLY. STUDENT USE PROHIBITED
14-18 Hosting and Deployment
Using CompositeFileProvider
private readonly IFileProvider _fileProvider;
Note that you can see the difference by changing the files during runtime and refreshing. If a physical file
is changed or removed, the application will throw an error or update after a refresh. Meanwhile, if an
embedded file is removed from the project structure, it will still keep working.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-19
Lesson 2
Deployment to Microsoft Azure
Microsoft Azure is a cloud service provided by Microsoft for organizations to build and manage solutions
easily. This service has multiple hardware and software abstraction levels, which include:
• Infrastructure as a service (IaaS). Virtualization of physical infrastructure components, which means
virtual machines being provided as a service.
• Platform as a service (PaaS). Platform-level resources that provide a running environment to run a
deployed application, such as application servers, which are provided as a service residing on the
cloud.
• Software as a service (SaaS). Using existing software functionality through services residing on the
cloud.
In this lesson, you will learn about various options available on Azure to deploy your web applications.
The options available cover the entire spectrum from a complete-do-it-yourself system (Virtual
Machines) to a completely managed platform as a service (App Service). You will learn the advantages
and limitations of the various options. You will learn how to deploy your application by using App
Service, which is a fully managed platform for application deployment that provides automatic scalability
and infrastructure maintenance.
Azure also provides tools for developers to be able to remotely debug applications deployed on the
cloud. Azure Application Insights provides developers with the ability to perform in-depth monitoring
of their application. Starting from request rates and response times to performance counters, such as CPU,
memory, and network usage, developers can monitor various metrics. Application Insights provides live
streaming of exception traces, a snapshot of data assembled from live operations, and log traces that
allow developers to diagnose and resolve issues quickly.
Lesson Objectives
After completing this lesson, you will be able to :
• Describe Microsoft Azure and its benefits.
• Deploy a completed MVC web application to Microsoft Azure.
• Use Application Insights to resolve issues.
• Understand various deployment options, deployment slots and the benefits of using them.
The most basic of these services is Infrastructure as a Service (IaaS). This allows you to use any kind of IT
infrastructure, such as servers, persistent disks, networks, and operating systems without the hassle of
having to buy any hardware or configure any machines. You can provision these machines over the
internet at the click of a button. Once you are done using them, you can “delete” them, again at the click
of a button. You will be charged for these machines only for the duration that you used them. Some of
the services available under IaaS in Azure are:
• Virtual Machines. Provision Windows and Linux virtual machines in seconds
• Container Instances. Easily run containers with a single command
• Traffic Manager. Route incoming traffic for high performance and availability
Services such as Azure DDoS protection and Azure Advanced Threat Protection help you keep your
applications and systems secure from any malicious or suspicious user or device activity.
Azure has compliances for most of the regulations across the world. The following are a few of them:
• Federal Financial Institutions Examination Council (FFIEC)
• Health Insurance Portability and Accountability Act (HIPAA)/Health Information Trust Alliance
(HITRUST)
Cost Effectiveness:
The most obvious cost-benefit of Azure is that there no upfront payment required for any of the
infrastructure or services being used. The pay-as-you-use model ensures that you pay for the resources
only for the duration that you use them. However, there are additional pricing models, where you can opt
for committed usage and get up to 70% discounts. Azure also offers special Dev/Test pricing for running
your development and QA workloads.
platform, sites can scale quickly to handle high traffic loads, and the built-in load balancing and traffic
manager provide high availability.
• Service Fabric. Service Fabric is a good choice if you’re creating a new app or re-writing an existing
app to use a microservice architecture. Apps, which run on a shared pool of machines, can start small
and grow to massive scale with hundreds or thousands of machines as needed.
In this section, we will work with Azure App Service and deploy web applications to Azure App Service.
The simplest and easiest way to deploy ASP.NET Core applications to Azure is by using Visual Studio
Publishing Wizard.
To deploy your application, in Solution Explorer, right-click the project, and then select Publish. In case
you are deploying your application for the first time, you will be presented with the Pick a publish target
dialog. Use the default value, App Service. Make sure Create New is selected and then click Publish. A
new dialog will appear, which displays the details to create a new app service on Azure. This is a one-time
activity and in subsequent deploys, you just select the service you have already created.
In the Create App Service window, if you have not already done so, sign in to your Azure account by
using the Add an account button in the top-right corner. You will be prompted for an App Name. App
names are globally unique and an immediate check to verify the uniqueness of your app name is
performed. If the name is already used, a notification will appear and you will have to choose a new name.
Choose your subscription, this subscription determines the access to resources in Azure. You will also be
required to choose a Resource Group. A resource group is used to group related resources so that they
can be managed as a group. This is useful to automate monitoring, deployment, etc. If you already have a
resource group, choose that. You can also create a new resource group by, choosing the New option.
Next, you need to choose the App Service Plan. An App Service plan defines a set of compute resources
for a web app to run. The plan specifies the region where you want to host your application. Ideally, this
should be where most of your customers are located. The plan also helps you choose the size of the
virtual machine instance, number of virtual machines, and pricing tier. The pricing tier governs how
scaling is handled. If you do not have a plan, you can create a new one, and then select that.
Once all the necessary options are chosen, a summary of what’s going to happen appears at the bottom
of the window. Make sure everything is as you planned.
Click Create. This will create all the necessary resources in Azure. Right after this step, Visual Studio builds
your project and deploys it. Once deployment is completed, a new tab opens in your browser, and will
automatically point you to your deployed application. The URL to access your application is in the form:
"http://<APP-NAME>.azurewebsites.com" where APP-NAME is the name you entered while publishing
your application.
Demonstration Steps
You will find the steps in the section “Demonstration: How to Deploy a Web Application to Microsoft
Azure“ on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD14_DEMO.md#demonst
ration-how-to-deploy-a-web-application-to-microsoft-azure
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-23
FTP
In order to use the FTP option, you need an existing Web App inside Azure. To use it you need to know
your hostname and credentials, which can be found inside Azure, where your Web App resides. After you
have the credentials, you can use your favorite FTP client to deploy the site directly to Azure.
Azure CLI
The Azure CLI tool lets you make deployments straight from your console. In concept, this is similar to
what the publishing wizard does, except this happens through a command.
While all the above options are fast and easy, they are obviously not suitable to deploy production-level
applications. Some of the drawbacks of this kind of deployment are:
• Such deployments generally require a downtime wherein the old application version is stopped and
the new version is installed and started.
• Such deployments are high risk since if there are any issues, the impact is felt directly on the live
system.
• In case there are any problems with the deployment, there is no easy way to roll back. The rollback is
also a manual process mostly and adds to the downtime.
To handle these issues, Azure provides different deployment strategies.
For example, if your app name is "MyAzureApp", the URL to access the original deployment will be
"http://MyAzureApp.azurewebsites.net". Now if you create a deployment slot with the name "beta", the
URL to access the deployment in this slot will be "http://MyAzureApp-beta.azurewebsites.net". So, in
short, you now have two different deployment environments that are identical to each other but have
their own hosts and configurations.
Once you have additional slots, you can use these to run all kinds of testing on the staging environment.
After all tests have successfully passed, you can swap your environments. Once you swap, your application
that was deployed to the staging deployment slot will now be available in the production deployment slot
and vice-versa. Internally this is achieved by swapping the virtual IP addresses of the source and
destination slots, thereby swapping the URLs of the slots.
All of this is achieved without any downtime. It is also possible to attach certain settings to slots. Database
is a good example; your production and staging environments will connect to different databases. So,
database connection settings can be attached to their own slots. So, while your application gets swapped,
they continue to connect to the correct database.
If after swapping development slots, you think that there are any issues with production deployment, you
can always roll back your deployment by doing another swap. This reverses the original swap. All of this
makes transitioning from staging to production environments very smooth and easy, enabling your
deployments to remain robust.
Live Metrics Stream shows you key metrics of your application in a streaming fashion. This is helpful for
monitoring your application during specified periods to understand live application usage patterns and
behavior.
Metrics Explorer allows you to create your own graphs. Application Insights telemetry collects various
metrics from your application. Metrics Explorer allows you to create charts for any metric you wish to
track. You can create charts with specific granularity, specific metrics, and aggregations. You can also set
alerts from the Metrics Explorer. To create an alert, you need to do the following:
1. Choose the metric
2. Choose the condition
For example, if you are analyzing page views, you can group the data by Performance, Page names,
Application Version, etc.
• View: This lets you decide whether you want to view data points as counts or percentages as well as
whether anomalies (data that is out of normal range) should be shown.
Once all the selections are done, click Analyze Telemetry. This will result in a time series histogram being
shown in the top half of the screen followed by tables with dimension counts in the bottom half. You can
use the dimension counts for further filtering of data.
If you have chosen to view the data with anomalies, the histogram will show the anomalies in red. The size
of the circle shows the size of the anomaly. You select the anomaly to get details such as the deviation.
Double-clicking this will take you to Application Insights Search view. This lets you get into details of
the instance, the request and other fine-grained information of the anomaly.
In this view, you can choose custom time filters and a combination of data types. Since the time range is
custom, this gives you the ability to view time series over a longer period and then narrow down to a
smaller range, by clicking the histogram bars. The bottom part of the window lets you further filter data
with a large number of options. You can also see details for each data point (each request, each page
view) to further drill down and get to any problem areas. For example, if you are looking at page views,
the bottom view will give you a list of page views. Clicking on any of these views gives you further details
of that particular view or request. These details include browser version, the location of the request, the
instance details that handled the request and many more.
On top of this view, you will see the Track Operation tab. Clicking this will show you the breakup of time
taken in various subrequests. This is a very useful view to drill down into to find the time taken by various
components and comes in handy for performance tuning.
Remote Debugging
You can also debug your application running on Azure App Service. To be able to do this, you need to do
two things:
1. Deploy the app to Azure App Service with Debug configuration.
2. Attach a debugger to the deployed app
Let us discuss these steps in detail, starting with attaching a debugger to the deployed app. We can use
two Visual Studio views to perform this task: Cloud Explorer and Server Explorer.
Cloud Explorer
The Cloud Explorer view provides an overview of your Azure resources and resource group, allowing you
to perform resource related diagnostics actions. In Visual Studio, from the View menu, go to Cloud
Explorer. In this window, you should see your Azure subscription. In case you do not, click Account
Management and login to Azure from Visual Studio. Once you are logged in, you should see all your
Azure services in Cloud Explorer. Select the App Services option and choose your application
deployment. In the bottom half of the window, you should see the Actions panel. In this panel, click
Attach Debugger. Once this is done, you can create breakpoints in your code and debug just like your
local application. You can step through functions, examine data, etc.
Note: Use remote debugging for applications in production with a lot of caution. Step-
through and breakpoints could impact the overall performance of applications in production.
Also, debugging in this manner means that all events and data are streamed to your local Visual
Studio. This increases network usage and can impact billing.
MCT USE ONLY. STUDENT USE PROHIBITED
14-28 Hosting and Deployment
Server Explorer
The Server Explorer view allows you to manage your web app running in Azure App Service from Visual
Studio. You can check your applications configuration, settings, etc. from this view. You can navigate to
Server Explorer from the View menu. If you are not already connected to Azure, connect through the
Server Explorer window. Once connected, this window shows you the relevant Azure services. Under App
Service, you will see the relevant resource groups. Expanding a resource group will show the application
under this resource group. Select your application, right-click, and choose View Settings. This will show
you the configuration of your application for Azure App Service such as whether Remote Debugging is
turned on, the connection strings configured for this application, etc.
The server explorer also allows you to attach a debugger. From Server Explorer, you can also view all the
files uploaded to App Service. Under your Web App, if you expand Files, you can see all the files uploaded
to the app service including the config files. You can edit these files to change log levels, debug settings,
etc. and the changes will take effect immediately. This is a very powerful option to be able to see the exact
configuration that your application is running under
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-29
Lesson 3
Microsoft Azure Fundamentals
In the previous chapter, we have had a brief introduction to Azure and seen how to deploy your
applications to Azure and monitor them. In this chapter, we look at some more services of Azure that can
be used to build scalable, distributed, and robust applications.
We first look at the various options available to store and access data from your application. Depending
on your application needs, you can choose the correct option to store data. Since Microsoft Cloud Storage
is a managed service, using this eliminates the need to worry about local storage, backup etc.
We also look at some key points that need to be considered while building distributed applications and
the options that Azure provides to handle these, such as caching as a service, mechanism for deferred
processing, etc.
In some situations, due to regulatory or other concerns, part of applications may have to run on local
datacenters. Azure provides Azure Stack that enables services, similar to Azure to run on a local data
center.
Lesson Objectives
After completing this lesson, students will understand:
• Details about related services in Azure.
• The choice of storage options and which options suit which conditions.
• What is a distributed application and the considerations while building a distributed application.
• How to use Azure Stack to build hybrid applications.
• How to use Key Vault to store secrets and how to access them through an application.
Azure Queues
Azure Queue storage is a service for storing large numbers of messages that can be accessed from
anywhere in the world via authenticated calls by using HTTP or HTTPS. The most common usage of
queues is for asynchronous communication between applications. A queue can contain a set of messages.
Each message can be up to 64KB and can remain in the queue for no more than 7 days.
//Get a reference to a container and create the container if it does not exist.
CloudBlobContainer container = blobClient.GetContainerReference("mydemocontainer");
await container.CreateIfNotExistsAsync();
//Get a reference to the blob. The blob gets created if it is not present.
CloudBlockBlob blob = container.GetBlockBlobReference("hello.txt");
Demonstration Steps
You will find the steps in the section “Demonstration: How to Upload an Image to Microsoft Azure Blob
Storage“ on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD14_DEMO.md#demonst
ration-how-to-upload-an-image-to-microsoft-azure-blob-storage
Scalability Options
One of the critical requirements of applications running in production mode is to be able to scale quickly.
Azure SQL provides a few options based on the deployment model. Azure SQL provides two deployment
models:
MCT USE ONLY. STUDENT USE PROHIBITED
14-32 Hosting and Deployment
• Database Transaction Unit (DTU) based. This deployment model has a set of pre-defined compute
sizes – that include storage, IOPS, CPU and backup size. There are different tiers of DTU that give the
users choices depending on workloads to be run.
• vCore based. In this deployment model, the user can choose CPU, memory, and storage based on
requirements.
In both the deployment models, Azure SQL provides the ability to dynamically scale – the ability to go to
a higher tier in case of DTU or higher configuration in case of vCore deployment model, without any
downtime.
Availability Options
To be able to run your application 24/7, the underlying infrastructure and services should run at the
highest availability. Azure provides a whole lot of features to support this. This includes automatic
backups, replications, failure detection, handling underlying hardware, software and network failures.
Azure SQL also provides active geo-replication allowing you to configure up to four read replicas of the
database in any of the Azure data centers.
In most situations, you would want to use a local database during testing and Azure SQL Database for
production. You can configure this at the time of publishing. At the time of publishing the application to
Azure, in the Publish window, click configure. In the Configure page, click Settings and then expand the
Databases section. Select Use this connection string at runtime check box and type the connection
string for Azure SQL here.
Deferred Processing
To build highly scalable distributed architecture, one key consideration is to have loosely coupled
components and to make use of deferred processing where ever possible. Such systems need ways in
which components can communicate without being dependent. Azure provides various services for this.
One such service is Azure Storage Queues which has been discussed in the section on storage earlier. A
more advanced option that is available is Azure Service Bus.
Microsoft Azure Service Bus is a fully managed enterprise integration message broker. Service Bus is
most commonly used to decouple applications and services from each other and is a reliable and secure
platform for asynchronous data and state transfer. Data is transferred between different applications and
services by using messages. A message is in binary format, which can contain JSON, XML, or just text.
Azure Service Bus supports queues as well as topics. While a queue is often used for point-to-point
communication, topics are useful in publish/subscribe scenarios. Listed below are some important features
of Azure Service Bus:
• Scheduled Delivery. Ability to submit messages for delayed processing. For example, at midnight
every day.
• Batching. All messages sent by a client in a certain period of time are grouped in a single message.
• Transactions. Supports grouping operations against a single messaging entity. For example, several
messages sent to one queue from within a transaction scope will only be committed to the queues
log when the transaction completes.
• Auto-forwarding. Enables chaining a queue or subscription to another queue or topic that is part of
the same namespace.
Microsoft Azure WebJobs is a feature that allows for the creation of programs/scripts that can run as
background tasks within your application context. WebJobs is a feature of an Azure App Service and runs
in the same instance as your application. WebJobs can be run in the following ways:
• Continuously. Starts as soon as the application starts.
• Triggered. Runs when this is triggered explicitly.
• Scheduled. Runs as per a schedule given at creation.
Microsoft Azure Functions is a solution for easily running small pieces of code or functions, in the cloud.
Unlike an application, this is not meant to be an always running website, but a function that gets executed
at the occurrence of any particular event. The events or triggers can be:
• HttpTrigger. Trigger the execution of your code by using an HTTP request.
• TimerTrigger. Execute cleanup or other batch tasks on a predefined schedule.
• BlobTrigger. Process Azure Storage blobs when they are added to containers.
• QueueTrigger. Respond to messages as they arrive in an Azure Storage queue.
The above are a few examples of triggers and there are more such triggers. With these, your application
can trigger a function and it can get executed asynchronously.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-35
Hybrid Environments
In some situations, for regulatory reasons or data location concerns, it might not be possible to use public
cloud such as Azure to host your applications and store data. For such situations, Azure provides Azure
Stack. Azure Stack is a hybrid cloud platform that lets you provide Azure services from your datacenter.
Azure Stack is an extension of Azure. With Azure Stack, developers can now build modern applications
across hybrid cloud environments, balancing the right amount of flexibility and control. Developers can
build applications using a consistent set of Azure services and DevOps processes and tools, then
collaborate with operations to deploy to the location that best meets the business, technical, and
regulatory requirements. In short, use of Azure Services from an application development point of view
will remain the same, while deployment of the application can happen either on Azure, or on-prem with
Azure Stack. Some common use cases where hybrid deployments using Azure Stack are:
• Cloud applications that meet varied regulations: Customers can develop and deploy applications in
Azure, with full flexibility to deploy on-premises on Azure Stack to meet regulatory or policy
requirements, with no code changes needed. Financial and banking related applications are the ones
where regulatory requirements are very strict and can benefit from Azure Stack.
• Edge and disconnected solutions: Customers can address latency and connectivity requirements by
processing data locally in Azure Stack and then aggregating in Azure for further analytics, with
common application logic across both.
• Cloud application model on-premises: Customers can use Azure services, containers, serverless, and
microservice architectures to update and extend existing applications.
Azure Stack has two deployment options:
Azure Stack Integrated Systems. Azure Stack integrated systems are offered through a partnership of
Microsoft and hardware partners, creating a solution that offers cloud-paced innovation and computing
management simplicity. Because Azure Stack is offered as an integrated hardware and software system,
you have the flexibility and control you need, along with the ability to innovate from the cloud. Use Azure
Stack integrated systems to create new scenarios and deploy new solutions for your production
workloads.
Azure Stack Development Kit (ASDK). ASDK is a free single server deployment that’s designed for trial
and proof of concept purposes. The portal, Azure services, DevOps tools, and Marketplace content are the
same across ASDK and integrated systems, so applications built against the ASDK will work when
deployed to an integrated system.
Some of the features of Azure Stack are:
• Azure Stack makes all the same services available in local data centers that are available in Azure.
These include IaaS (Virtual Machines, Storage, Networking etc.), PaaS (Azure App Service, Azure
Functions etc.).
• The user experience for Azure Stack and Azure remains the same, with the same portal being
available in both scenarios. The same tools – for monitoring, deployment, configuration and
administration work on Azure as well as Azure Stack. This makes working in hybrid environments very
easy and hassle-free.
• Any innovations in Azure, including new Azure services, updates to existing services, and additional
Azure Marketplace applications are continuously delivered to Azure stack. This helps in keeping all
environments in sync all the time.
• You can use the same identities (for users as well as applications) across Azure and Azure Stack, since
both work with Active Directory.
MCT USE ONLY. STUDENT USE PROHIBITED
14-36 Hosting and Deployment
So, to conclude use of Azure Stack enables you to use the same scalable, flexible, and modern cloud
services that Azure provides with the choice of using them in a data center and location of your choosing,
such that any regulatory, latency needs are met.
Azure Cache for Redis is an implementation of the open source Redis cache that runs as a service in an
Azure datacenter. It provides a caching service that can be accessed from any Azure application. Azure
Cache for Redis is a high-performance caching solution that provides availability, scalability, and security.
It typically runs as a service spread across one or more dedicated machines. It attempts to store as much
information as it can in memory to ensure fast access. It is compatible with many of the various APIs that
are used by client applications.
You can provision a cache for your application by using the Azure portal. When you create a Redis cache
from the portal you need to provide a DNS name and resource group name. The DNS name is used to
access the cache. Using the Azure portal, you can also configure the eviction policy of the cache, and
control access to the cache, monitoring and alerting policies, firewall rules, etc.
In an earlier section, we have already seen how to use Redis as a session state provider. In addition to this,
Azure Cache for Redis can be used generally for normal caching needs as well. Redis is a key-value store,
where values can contain simple types or complex data structures such as hashes, lists, and sets. Keys can
be permanent or tagged with a limited time-to-live, at which point the key and its corresponding value
are automatically removed from the cache.
To add Redis to your application, in Visual Studio, use NuGet Package Manager to install
StackExchange.Redis.
The following code sample is a snippet that shows how to connect to Azure Cache for Redis and access
data from the cache:
A controller class
ConfigurationOptions config = new ConfigurationOptions();
config.EndPoints.Add("<CACHENAME>.redis.cache.windows.net");
config.Password = "<Redis cache key from management portal>";
//Replace CACHENAME with the DNS Name you provided while creating the cache.
var cm = ConnectionMultiplexer.Connect(config);
var db = connection.GetDatabase();
// You now have a connection to the cache. You can now use this connection, to add data
to the cache or // to retrieve it. Some examples are:
db.StringSet("key", "value");
var key = db.StringGet("key");
• Large scaling to better handle instantaneous high loads, such as the start of a product launch event.
• Distribution of user requests and serving of content directly from edge servers so that less traffic is
sent to the origin server.
MCT USE ONLY. STUDENT USE PROHIBITED
14-38 Hosting and Deployment
To add Azure CDN to your web application, in the Azure portal, navigate to your app in the App services
page. In the App services page, in the Settings section, go to Networking. On the right-hand side pane,
expand the Azure CDN section and in this, you will see the Configure Azure CDN for your app option.
In the Azure Content Delivery Network page, provide the New endpoint settings. You will need to
specify:
• Name of your CDN
• Pricing Tier - Specifies the provider and available features.
• CDN endpoint name - Any name that is unique in the azureedge.net domain. Cached resources are
accessed as <endpointname>.azureedge.net
After entering the above information, click Create and the CDN profile gets created.
After this, you can access all your static content by using <endpointname>.azureedge.net and this will
be served from the CDN.
Note: It takes a while for the CDN registration to take effect, so you might not see
performance improvement immediately.
That CDNs are best for static files is now established, Azure CDN provides an option to improve the
performance of web pages with dynamic content. This can be achieved with Azure CDN dynamic site
acceleration (DSA) optimization. This option can be chosen while creating the CDN endpoint.
One important consideration while using CDNs, is to be aware of the effects of cached files. In case your
application makes changes to any static files, they may not get reflected. Azure CDN edge nodes will
cache assets until the asset's time-to-live (TTL) expires. After the asset's TTL expires, when a client requests
the asset from the edge node, the edge node will retrieve a new updated copy of the asset to serve the
client request and store the new copy inside the cache.
The best practice to make sure your users always obtain the latest copy of your assets is to version your
assets for each update and publish them as new URLs. CDN will immediately retrieve the new assets for
the next client requests. Sometimes you may wish to purge cached content from all edge nodes and force
them all to retrieve newly updated assets. This might be due to updates to your web application or to
quickly update assets that contain incorrect information.
Azure provides Azure Key Vault that can be used by applications to store keys and secrets. Key Vault is a
cloud service that works as a secure secrets store. Key Vault helps safeguard cryptographic keys and
secrets used by cloud applications and services. By using Key Vault, you can encrypt keys and secrets (such
as authentication keys, storage account keys, data encryption keys, and passwords) by using keys
protected by hardware security modules (HSMs).
Using Key Vault provides the following benefits:
• Centralize application secrets: Centralizing storage of application secrets in Key Vault allows you to
control their distribution. This greatly reduces the chances that secrets may be accidentally leaked.
When using Key Vault, application developers no longer need to store security information in their
application. This eliminates the need to make this information part of the code.
• Securely store keys: Secrets and keys are safeguarded by Azure by using industry-standard algorithms,
key lengths, and HSMs.
• Monitor access and use: Since keys and secrets are stored centrally, it is easy to monitor how and
when these are being used.
• Simplified administration: The overhead of having to encrypt data, perform key management by
rotating keys periodically, having to manage an in-house HSM, certificate management, etc. is
removed by Key Vault.
To use Key Vault, the following steps need to be performed:
1. Create a Key Vault.
2. Authorize your application to access Key Vault
3. Generate a token to be used by your application for authenticating itself with the Key Vault
4. Create a Key Vault client to access keys and secrets.
We will look at each of these steps in details.
To create an identity in Azure AD, in the portal search for "App Registrations" and on this page click
New application registration. On the Create page provide your application name, and then select WEB
APPLICATION AND/OR WEB API (the default) and specify the URL to your web application. Click the
Create button. When the app registration is complete, you can see the list of registered apps which will
also contain your application. The list will display the Application ID. If you click your application, a pane
opens on the right-hand side and will have a Settings button on top. Click this button and in the pane
below, click Keys. Enter in a description in the Key description box and select a duration, and then
click SAVE. The page refreshes and now shows a key value. The application Id and key are the identity of
your application.
The next step is to configure Key Vault to allow your application access. For this, in the portal, go to your
key vault. From the menu, choose Access Policies and click Add New. In the window that appears, click
Select Principal. In the list that appears, select the name with which you had created an identity for your
application in AD. Also select the appropriate key, secret and certificate permissions necessary for your
application. Generally, GET operations will be the most often performed operations. Click Save and with
this, your application is now authorized to access Key Vault.
Generate token to be used by your application for authenticating itself with the Key
Vault
Once the above configuration is done, you are now ready to start accessing the keys and secrets from the
Key Vault. We now look at the changes needed in your application to perform these operations. There
are two packages that your web application needs to have installed.
• Active Directory Authentication Library has methods for interacting with Azure Active Directory
and managing user identity.
• Azure Key Vault Library has methods for interacting with Azure Key Vault.
These can be installed using the NuGet package manager.
To use the Key Vault API, you need an access token. The Key Vault client handles calls to the Key Vault API
but you need to supply it with a function that gets the access token.
The following code sample shows the function needed to get the access token:
Util.cs
//the method that will be provided to the KeyVaultClient
public static async Task<string> GetToken(string authority, string resource, string
scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new
ClientCredential(WebConfigurationManager.AppSettings["ClientId"],
WebConfigurationManager.AppSettings["ClientKey"]);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource,
clientCred);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken;
}
In the above snippet, the ClientId and the ClientKey are as obtained from the Azure AD. A general best
practice is to add this configuration in web.config and further to provide actual values for these from the
Azure portal. This can be done in the Application Settings for your application in App Service.
MCT USE ONLY. STUDENT USE PROHIBITED
Developing ASP.NET MVC Web Applications 14-41
Once the client is created, you can invoke any of the get methods to get the necessary
keys/secrets. Sample code as follows:
Note: You can store a key in Key Vault by adding this information from the portal. The
Azure Key Vault service accepts secret data, encrypts and stores it, returning a secret identifier,
that may be used to retrieve the secret at a later time.
MCT USE ONLY. STUDENT USE PROHIBITED
14-42 Hosting and Deployment
Objectives
After completing this lab, you will be able to:
• Create an App Service in Microsoft Azure.
• Deploy an application to Azure.
• Work with Azure SQL Database.
• Create a storage container on Azure.
• Upload images to storage containers from an application.
Lab Setup
Estimated Time: 90 minutes
You will find the high-level steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD14_LAB_MANUAL.md.
You will find the detailed steps on the following page: https://github.com/MicrosoftLearning/20486D-
DevelopingASPNETMVCWebApplications/blob/master/Instructions/20486D_MOD14_LAK.md.
Review Question
Question: How can you set an ASP.NET Core MVC application to dynamically use XML files
which are added after the application is started?
Best Practice
There is no single correct solution in regards to hosting and deployment. Every single case will need to be
handled in a way that is most appropriate for the application requirements. Always analyze your
requirements before choosing solutions.
Course Evaluation
Your evaluation of this course will help Microsoft
understand the quality of your learning experience.
Please work with your training provider to access the
course evaluation form.
Microsoft will keep your answers to this survey
private and confidential and will use your responses
to improve your future learning experience. Your
open and honest feedback is valuable and
appreciated.
MCT USE ONLY. STUDENT USE PROHIBITED
14-46 Hosting and Deployment
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes
MCT USE ONLY. STUDENT USE PROHIBITED
Notes