Professional Documents
Culture Documents
Module 4
Module 4
Module 4
Module 4
Developing Controllers
Contents:
Module Overview 04-1
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.
PROHIBITED
MCT USE ONLY. STUDENT USE
4-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.
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.
U
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:
@{ Y.
S
Layout = null;
}
<!DOCTYPE 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”.
STUDENT
MCT USE ONLY.
04-4 Developing Controllers
U
ActionResult class that implements the IActionResult interface.
S
The following code shows how to return a ViewResult object from an action:
}
return View();
E PROHIBITED
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 SE ONLY TUDENT USE ROHIBITED
Developing ASP.NET Core MVC Web Applications 04-5
U
The following code shows how to return a ContentResult object from an action:
.
Another useful return type from an action is RedirectToActionResult. The RedirectToActionResult
class derives from the ActionResult class. You can use this action result to return an HTTP 302
response to the browser. This causes the browser to send the request to another action method. To
return a RedirectToActionResult class from an action, you should use the RedirectToAction
method. In the parameter of the RedirectToAction method, you need to specify the name of the
action.
The following code shows how to use the RedirectToAction method to redirect to another action in
the same controller:
The following code shows how to use the RedirectToRouteResult object to redirect to an action in
another controller:
E ONLY. STUDENT
}
USE
An action might also return a StatusCodeResult object. The StatusCodeResult class provides a way to
return an action result with a specific HTTP response status code and description. For example, you might
use a StatusCodeResult object to return an HTTP 404 Not Found error.
The following code shows how to use the StatusCodeResult object to return an HTTP 404 Not
Found error:
PartialViewResult. You can use this action result to generate a section of an HTML page rather
than a complete HTML page. Partial views can be reused in many views throughout a web
application.
RedirectResult. You can use this action result to redirect to a specific URL, either within your
web application or in an external location.
FileContentResult. You can use this action result to return a file from an action method.
Using Parameters
When users request webpages, they often
specify additional information besides the name
of the webpage itself. For example, when they
P
ROHIBITED
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.
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:
O
"default",
Y.
NL
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 SE ONLY. STUDENT
04-8 Developing Controllers
U
The following code shows how the model binder locates parameters in a query string:
USE
Using ViewBag and ViewData to Pass Information to Views
You can pass a model object from the action
method to the view by using the View()
method. This method is frequently used to pass
information from a controller action to a view.
This approach is recommended because it
adheres closely to the MVC pattern, in which
each view renders the properties found in the
model class that it receives from the controller.
You should use this approach wherever possible.
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.
U
The following code shows how to obtain and use the same properties in a view by using Razor:
.
Note: Razor will be covered in Module 5, “Developing Views”.
S
developers who prefer to use dictionary objects. In fact, ViewBag is a dynamic wrapper above the
ViewData dictionary. This means that you could assign a value in a controller action by using
ViewBag and read the same value in the view by using ViewData.
In action methods, you can add data to the ViewData property by using key/value pairs as shown in
the following lines of code:
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#demons
t ration-how-to-write-controllers-and-actions.
STUDENT
MCT USE ONLY.
4-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:
SE PROHIBITED
is received from a web browser:
1. A controller is instantiated to respond to the
request. Routing is used to determine the
right controller class to use.
2. The request URL is examined. Routing is
used to determine the action that needs to
be called in the controller object.
3. Model binding is used to determine the values that should be passed to the action as parameters.
The model binding mechanism consults the routing entries to determine if any segments of the URL
should be passed as parameters. The model binding mechanism can also pass parameters from a
posted form, from the URL query string, or from uploaded files.
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.
STUDENT
MCT USE ONLY.
Developing ASP.NET Core MVC Web Applications 04-
11
To parse the URLs requested by browsers. This analysis ensures that requests are forwarded to the
right controllers and actions. These are called incoming URLs.
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 following code shows how the default route is implemented in the Startup.cs file:
U
S
{
// Code is omitted
}
// Code is omitted
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.
PROHIBITED
MCT USE ONLY. STUDENT USE
4-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.
TUDENT
located in the file Startup.cs.
U
services.AddMvc();
}
SE PROHIBITED
app.UseMvc();
}
}
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.
S
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.
TUDENT USE
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
P
/Display/Some relative URL, the request will succeed.
R
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.
USE
MCT
Developing ASP.NET Core MVC Web Applications 04-
15
NL
{
app.UseMvc(routes =>
Y.
{
routes.MapRoute( name:
"default",
template: "{controller}/{action}/{param}");
});
}
STUDENT SE
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:
U
public class SomeController : Controller
{
public IActionResult ShowParam(string param)
{
return Content(param);
}
}
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.
USE
MCT
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:
Y.
NL
});
S
requests. The constraints property enables you to specify a regular expression for each segment
variable. The route will match a request only if all the segment variables match the regular expressions
that you specify.
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.
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:
.
= "[0-9]+" },
dataTokens: new { locale = "en-US" });
});
S
If a route does not match, it is ignored and the next route is evaluated. For this reason, you should add
routes in the appropriate order. You should add the most specific routes first, such as the routes that
have no segment variables and no default values. Also, routes with constraints should be added before
routes without constraints.
The following code shows how two routes can be added:
U
"[0-9]+" });
routes.MapRoute(
name: "secondRoute",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
SE ROHIBITED
});
}
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.
The following code shows an action that uses the RouteData.Values collection:
.
The RouteData.Values example
public class ExampleController : Controller
{
public IActionResult Print()
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
P
segment value:
If a user requests the /Example/Print/1 relative URL, the following string appears on the screen: id: 1.
ONLY
MCT USE
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.
STUDENT SE PROHIBITED
{
public IActionResult Print(string id)
{
return Content("id: " + id);
}
}
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.
U
A model binding example
public class ExampleController : Controller
{
public IActionResult Print(int id)
{
return Content("id: " + id);
}
}
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 SE ONLY TUDENT
04-20 Developing Controllers
U
You also can allow a parameter in an action to have optional values.
The following code shows an action that gets a parameter with an optional value:
.
}
}
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
S
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:
SE PROHIBITED
public IActionResult Print(string id, string title)
{
return Content("id: " + id + ". title: " + title);
}
}
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 SE
Developing ASP.NET Core MVC Web Applications 04-
21
Y.
NL
routing allows you to configure routes by using
attributes. The primary advantage of attribute-
based routing is that it allows you to define your
routes in the same file as the controller that the
routes refer to.
The following code shows a Startup class that is
configured to use MVC with no centralized routes: S
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");
}
}
U
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:
S
An attribute route with two parameters
public class MyController : Controller
{
[Route("My/{param1}/{param2:int}")]
public IActionResult SomeMethod(string param1, int param2)
{
return Content("param1: " + param1 + ", param2: " + param2);
}
}
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
P
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.
USE
MCT
Developing ASP.NET Core MVC Web Applications 04-
23
L
{
return Content("Method1");
}
[Route("My/Method2")]
public IActionResult Method2()
{
return Content("Method2");
Y.
}
}
STUDENT SE PROHIBITED
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:
U
public class MyController : Controller
{
[Route("Method1")]
public IActionResult Method1()
{
return Content("Method1");
}
[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}");
USE
MCT
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:
L Y STUDENT USE
return Content("Method1");
}
}
return Content("Method2");
.
PROHIBITED
}
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.
The following code shows how a HttpGet attribute can be applied to an action method:
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.
ONLY.
MCT USE
4-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.
Filters run after an action is chosen to run. They run within a filter pipeline. The stage in the filter
pipeline in which the filter is run is determined by the type of the filter. The following are examples of
filter types:
Authorization filters. Authorization filters run before any other filter and before the code in the
action method. They are used to check a user’s access rights for the action.
Resource Filters. Resource filters appear in the filter pipeline after the authorization filters and
before any other filters. They can be used for performance reasons. Resource filters implement
either the IResouceFilter interface or the IAsyncResourceFilter interface.
Action Filters. Action filters run before and after the code in the action method. You can use
action filters to manipulate the parameters that an action receives. They can also be used to
manipulate the result returned from an action. Action filters implement either the IActionFilter
interface or the IAsyncActionFilter interface.
STUDENT
MCT USE ONLY.
Developing ASP.NET Core MVC Web Applications 04-
27
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”.
SE ROHIBITED
implements both the IActionFilter and
IResultFilter
P
interfaces for you. By deriving your filter from the
ActionFilterAttribute class, you can create a
single filter that can run code both before and
after an action is run, and both before and after
the result is returned.
The following code shows how an action filter is used to write text to the Visual Studio Output Window:
In the following lines of code, the SimpleActionFilter attribute is applied to the Index action of the
MyController controller:
.
{
[SimpleActionFilter]
public IActionResult Index()
{
return Content("some text");
STUDENT
}
}
The following code shows how the action name can be retrieved in the OnActionExecuting and
OnActionExecuted event handlers:
U
S
The OnActionExecuting and OnActionExecuted event handlers
public class SimpleActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
}
string actionName = filterContext.ActionDescriptor.RouteValues["action"];
Debug.WriteLine(actionName + " started"); E PROHIBITED
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string actionName = filterContext.ActionDescriptor.RouteValues["action"];
Debug.WriteLine(actionName + " finished");
}
}
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
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
U
OnResultExecuted event handler:
E ONLY TUDENT
public class SimpleActionFilter : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
ContentResult result = (ContentResult)filterContext.Result;
.
Debug.WriteLine("Result: " + result.Content);
}
}
When you navigate to the Index action in the MyController controller, the Result: some text text
is displayed in the Visual Studio Output Window.
S
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:
U
{
sw.WriteLine(actionName + " started");
}
}
}
SE
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
P
string actionName = filterContext.ActionDescriptor.RouteValues["action"];
R
using (FileStream fs = new FileStream("c:\\logs\\log.txt", FileMode.Append))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine(actionName + " finished");
OHIBITED
}
}
}
U
ContentResult result = (ContentResult)filterContext.Result;
using (FileStream fs = new FileStream("c:\\logs\\log.txt", FileMode.Append))
S
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine("Result: " + result.Content);
}
E ONL STUDENT
}
}
}
Y
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:
U
Using dependency injection in a filter
public class LogActionFilter : ActionFilterAttribute
S
E
{
private IHostingEnvironment _environment;
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_DEM
O.md#demonst ration-how-to-create-and-use-action-filters.