ASP Net Core
ASP Net Core
net-core
#asp.net-
core
Table of Contents
About 1
Remarks 2
Versions 2
Examples 2
Controllers 6
Conclusion 7
Using Visual Studio code to develop Cross plateform aspnet core application 7
Examples 16
Quick tutorial for an Angular 2 Hello World! App with .Net Core in Visual Studio 2015 16
Expected errors when generating Angular 2 components in .NET Core project (version 0.8.3) 41
Chapter 3: ASP.NET Core - Log both Request and Response using Middleware 43
Introduction 43
Remarks 43
Examples 43
Logger Middleware 43
Chapter 4: Authorization 45
Examples 45
Simple Authorization 45
Examples 47
Using BundlerMinifier.Core 50
Automated Bundling 51
Available Commands 51
Chapter 6: Caching 52
Introduction 52
Examples 52
Distributed Caching 53
Chapter 7: Configuration 54
Introduction 54
Syntax 54
Examples 54
Getting Started 54
Examples 57
Remarks 62
Examples 62
Introduction 65
Syntax 65
Remarks 65
Examples 66
Register dependencies 66
Lifetime control 67
Enumerable dependencies 67
Generic dependencies 67
Remarks 70
IServiceCollection 72
IServiceProvider 73
Result 73
Examples 75
Syntax 77
Examples 77
Example Usage 77
Required Configuration 77
Examples 78
Middleware Registration 87
Examples 89
Remarks 91
Examples 91
Examples 95
Examples 97
Examples 101
Remarks 103
Examples 103
Setup 103
Behavior 106
Setup 108
Behavior 112
Examples 114
Chapter 21: Sending Email in .Net Core apps using MailKit 116
Introduction 116
Examples 116
Introduction 118
Examples 118
Parameters 120
Examples 120
Examples 126
Introduction 129
Examples 129
Prerequisites 129
NuGet 129
npm 129
Configuring 129
Prerequisites 130
Configuration 130
Credits 132
About
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: asp-net-core
It is an unofficial and free asp.net-core ebook created for educational purposes. All the content is
extracted from Stack Overflow Documentation, which is written by many hardworking individuals at
Stack Overflow. It is neither affiliated with Stack Overflow nor official asp.net-core.
The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.
Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]
https://riptutorial.com/ 1
Chapter 1: Getting started with asp.net-core
Remarks
.NET Core is a general purpose development platform maintained by Microsoft and the .NET
community on GitHub. It is cross-platform, supporting Windows, macOS and Linux, and can be
used in device, cloud, and embedded/IoT scenarios.
• Flexible deployment: Can be included in your app or installed side-by-side user- or machine-
wide.
• Cross-platform: Runs on Windows, macOS and Linux; can be ported to other OSes. The
supported Operating Systems (OS), CPUs and application scenarios will grow over time,
provided by Microsoft, other companies, and individuals.
• Command-line tools: All product scenarios can be exercised at the command-line.
• Compatible: .NET Core is compatible with .NET Framework, Xamarin and Mono, via the -
.NET Standard Library.
• Open source: The .NET Core platform is open source, using MIT and Apache 2 licenses.
Documentation is licensed under CC-BY. .NET Core is a .NET Foundation project.
• Supported by Microsoft: .NET Core is supported by Microsoft, per .NET Core Support
Versions
Examples
Installation and Setup
https://riptutorial.com/ 2
Creating an ASP.NET Core MVC Application.
1. Open Visual Studio.
2. Select File > New Project.
3. Select Web under the language of your choice within the Templates section on the left.
4. Choose a preferred Project type within the dialog.
5. Optional: Choose a .NET Framework you would like to target
6. Name your project and indicate if you want to create a Solution for the project.
7. Click OK to create the project.
You will be presented with another dialog to select the template you want to use for the project :
https://riptutorial.com/ 3
Each of the descriptions are self-explanatory. For this first project, select Web Application, which
will contain all of the default configurations, authentication, and some existing content.
Since this is an introduction application and doesn't require any security or authentication, you can
change the authentication option to No Authentication on the right-side of the dialog and click
OK to create the project.
You should then see the new project within the Solution Explorer :
Press the F5 key to run the application and begin a debugging session, which will launch the
application within your default browser :
https://riptutorial.com/ 4
You can now see that your project is up and running locally and is ready as a starting point for you
to build your application.
It's possible to create a new ASP.NET Core project entirely from the command line using the
dotnet command.
dotnet new web scaffolds a new "empty" web project. The web parameter tells the dotnet tool to use
the ASP.NET Core Empty template. Use dotnet new -all to show all the available templates currently
installed. Other key templates include console, classlib, mvc and xunit.
Once the template has been scaffolded out, you can restore the packages required to run the
project (dotnet restore), and compile and start it (dotnet run).
Once the project is running, it will be available on the default port: http://localhost:5000
With ASP.NET Core 1.0, the MVC and Web API framework have been merged into one
framework called ASP.NET Core MVC. This is a good thing, since MVC and Web API share a lot
https://riptutorial.com/ 5
of functionality, yet there always were subtle differences and code duplication.
However, merging these two into framework one also made it more difficult to distinguish one from
another. For example, the Microsoft.AspNet.WebApi represents the Web API 5.x.x framework, not
the new one. But, when you include Microsoft.AspNetCore.Mvc (version 1.0.0), you get the full blown
package. This will contain all the out-of-the-box features the MVC framework offers. Such as
Razor, tag helpers and model binding.
When you just want to build a Web API, we don't need all this features. So, how do we build a
minimalistic Web API? The answer is: Microsoft.AspNetCore.Mvc.Core. In the new world MVC is split
up into multiple packages and this package contains just the core components of the MVC
framework, such as routing and authorization.
For this example, we're gonna create a minimal MVC API. Including a JSON formatter and CORS.
Create an empty ASP.NET Core 1.0 Web Application and add these packages to your
project.json:
"Microsoft.AspNetCore.Mvc.Core": "1.0.0",
"Microsoft.AspNetCore.Mvc.Cors": "1.0.0",
"Microsoft.AspNetCore.Mvc.Formatters.Json": "1.0.0"
Controllers
The 'old' Web API comes with its own controller base class: ApiController. In the new world there
is no such thing, only the default Controller class. Unfortunately, this is a rather large base class
and it's tied to model binding, views and JSON.NET.
Fortunately, in the new framework controller classes don't have to derive from Controller to be
picked up by the routing mechanism. Just appending the name with Controller is enough. This
https://riptutorial.com/ 6
allows us to build our own controller base class. Let's call it ApiController, just for old times sake:
/// <summary>
/// Base class for an API controller.
/// </summary>
[Controller]
public abstract class ApiController
{
[ActionContext]
public ActionContext ActionContext { get; set; }
The [Controller] attribute indicates that the type or any derived type is considered as a controller
by the default controller discovery mechanism. The [ActionContext] attribute specifies that the
property should be set with the current ActionContext when MVC creates the controller. The
ActionContext provides information about the current request.
ASP.NET Core MVC also offers a ControllerBase class which provides a controller
base class just without views support. It's still much larger than ours though. Use it if
you find it convenient.
Conclusion
We can now build a minimal Web API using the new ASP.NET Core MVC framework. The
modular package structure allows us to just pull in the packages we need and create a lean and
simple application.
Using Visual Studio code to develop Cross plateform aspnet core application
With AspNetCore you can develop the application on any platform including Mac,Linux,Window
and Docker.
Now you have all the tools available. To develop the application. Now you need some scaffolding
option. For that you should consider using Yeoman. To install Yeoman
1. Install NPM. For this you need Node on your machine. Install from here
https://riptutorial.com/ 7
2. Install Yeoman by using NPM
npm install -g yo
Now we have all the setup on your machine. First let's create a new project with DotNetCore basic
command and then create a new project using Yo.
2. Scaffold a very basic dotnet project using default command line option
dotnet New
https://riptutorial.com/ 8
Use Yeoman as Scaffolding Option
yo aspnet
Yeoman will ask some inputs like Project Type, Project Name etc like
https://riptutorial.com/ 9
Now restore the packages by running dotnet restore command and Run the application
https://riptutorial.com/ 10
Now open the files and run the application. You can also search the extension for your help.
ASP.NET Core uses the ASPNETCORE_ENVIRONMENT environment variable to determine the current
environment. By default, if you run your application without setting this value, it will automatically
https://riptutorial.com/ 11
default to the Production environment.
You can easily set an environment variable from a command prompt using the setx.exe command
included in Windows. You can use it to easily set a user variable:
Note that the environment variable is not set in the current open window. You will need to open a
new command prompt to see the updated environment. It is also possible to set system variables
(rather than just user variables) if you open an administrative command prompt and add the /M
switch:
Using PowerShell Alternatively, you can use PowerShell to set the variable. In PowerShell, as
well as the normal user and system variables, you can also create a temporary variable using the
$Env: command:
$Env:ASPNETCORE_ENVIRONMENT = "Development"
The variable created lasts just for the duration of your PowerShell session - once you close the
window the environment reverts back to its default value.
Alternatively, you could set the user or system environment variables directly. This method does
not change the environment variables in the current session, so you will need to open a new
PowerShell window to see your changes. As before, changing the system (Machine) variables will
require administrative access
Using the windows control panel If you're not a fan of the command prompt, you can easily
update your variables using your mouse!Click the windows start menu button (or press the
Windows key), search for environment variables, and choose Edit environment variables for your
https://riptutorial.com/ 12
account:
https://riptutorial.com/ 13
Click Environment Variables to view the list of current environment variables on your system.
https://riptutorial.com/ 14
Assuming you do not already have a variable called ASPNETCORE_ENVIRONMENT, click the New... button
and add a new account environment variable:
Click
OK to save all your changes. You will need to re-open any command windows to ensure the new
environment variables are loaded.
https://riptutorial.com/ 15
Chapter 2: Angular2 and .Net Core
Examples
Quick tutorial for an Angular 2 Hello World! App with .Net Core in Visual
Studio 2015
Steps:
https://riptutorial.com/ 16
2. Go to wwwroot, and create a normal html page called Index.html:
https://riptutorial.com/ 17
3. Configure Startup.cs to accept static files (this will require to add
"Microsoft.AspNetCore.StaticFiles": "1.0.0" library in the “project.json” file):
https://riptutorial.com/ 18
4. Add NPN File:
• Right click the WebUi project and add NPN Configuration File (package.json):
https://riptutorial.com/ 19
• Verify the last versions of the packages:
https://riptutorial.com/ 20
Note: If visual studio does not detect the versions of the packages (Check all
packages, because some of them does show the version, and some others don't), it
might be because the Node version coming in visual studio is not working correctly, so
it will probably require to install node js externally and then link that installation with
visual studio.
https://riptutorial.com/ 21
iii. (Optional) after saving the package.json it will install the dependencies in the
project, if not, run "npm install" using a command prompt from the same location as the
package.json file.
https://riptutorial.com/ 22
Note: Recommended to install "Open Command Line", an extension that can be added
to Visual Studio:
5. Add typescript:
• Create a TsScript folder inside the WebUi project, just for organization (The
TypeScripts won't go to the browser, they will be transpiled into a normal JS file, and
this JS file will be the one going to the wwwroot foder using gulp, this will be explained
later):
https://riptutorial.com/ 23
• Inside that folder add "TypeScript JSON Configuration File" (tsconfig.json):
https://riptutorial.com/ 24
And add the next code:
https://riptutorial.com/ 25
• In the WebUi Project’s root, add a new file called typings.json:
https://riptutorial.com/ 26
And add the next code:
https://riptutorial.com/ 27
• In the Web Project root open a command line and execute "typings install", this will
create a typings folder (This requires “Open Command Line” explained as an optional
step in the Note inside Step 4, numeral iii).
https://riptutorial.com/ 28
https://riptutorial.com/ 29
https://riptutorial.com/ 30
6. Add gulp to move files:
• Add "Gulp Configuration File" (gulpfile.js) at the root of the web project:
https://riptutorial.com/ 31
• Add Code:
https://riptutorial.com/ 32
7. Add Angular 2 bootstrapping files inside the “tsScripts” folder:
https://riptutorial.com/ 33
app.component.ts
https://riptutorial.com/ 34
app.module.ts
main.ts
9. Inside the scripts folder (but outside app), add the systemjs.config.js:
https://riptutorial.com/ 35
And add the next code:
https://riptutorial.com/ 36
10. Execute Gulp Task to generate the scripts in wwwroot.
https://riptutorial.com/ 37
i. If the tasks are not loaded ("Fail to load. See Output window") Go to output window
and take a look at the errors, most of the time are syntax errors in the gulp file.
• Right Click "default" task and "Run" (It will take a while, and the confirmation messages
are not very precise, it shows it finished but the process is still running, keep that in
mind):
https://riptutorial.com/ 38
11. Modify Index.html like:
https://riptutorial.com/ 39
12. Now run and enjoy.
Notes:
• In case there are compilation errors with typescript, for example "TypeScript Virtual
Project", it is an indicator that the TypeScript version for Visual Studio is not updated
according to the version we selected in the “package.json”, if this happens please
install: https://www.microsoft.com/en-us/download/details.aspx?id=48593
References:
https://riptutorial.com/ 40
• Deborah Kurata's "Angular 2: Getting Started" course in Pluralsight:
https://www.pluralsight.com/courses/angular-2-getting-started-update
https://angular.io/
http://www.mithunvp.com/angular-2-in-asp-net-5-typescript-visual-studio-2015/
http://www.mithunvp.com/using-angular-2-asp-net-mvc-5-visual-studio/
When generating new Angular 2 components in a .NET Core project, you may run into the
following errors (as of version 0.8.3):
OR
No app module found. Please add your new Class to your component.
Identical ClientApp/app/app.module.ts
[SOLUTION]
2. Open app.client.module.ts: prepend the declaration with 3 dots “...” and wrap the declaration
in brackets.
3. Open boot-client.ts: update your import to use the new app.client.module reference.
[EXPLANATION]
Angular CLI looks for a file named app.module.ts in your project, and tries to find a references for
the declarations property to import the component. This should be an array (as the
sharedConfig.declarations is), but the changes do not get applied
[SOURCES]
https://riptutorial.com/ 41
• https://github.com/angular/angular-cli/issues/2962
• https://www.udemy.com/aspnet-core-angular/learn/v4/t/lecture/6848548 (section 3.33 lecture
contributor Bryan Garzon)
https://riptutorial.com/ 42
Chapter 3: ASP.NET Core - Log both Request
and Response using Middleware
Introduction
For some time I've searched for the best way to log requests and response in an ASP.Net Core. I
was developing services and one of the requirements was to record request with its response in
one record the the database. So many topics out there but none worked for me. it's either for
request only, response only or simply didn't work. When I was able to finally do it, and it had
evolved during my project to better error handling and logging exceptions so I thought of sharing.
Remarks
some of the topics that was helpful to me:
• http://www.sulhome.com/blog/10/log-asp-net-core-request-and-response-using-middleware
• http://dotnetliberty.com/index.php/2016/01/07/logging-asp-net-5-requests-using-middleware/
• How to log the HTTP Response Body in ASP.NET Core 1.0
Examples
Logger Middleware
using Microsoft.AspNetCore.Http;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNetCore.Http.Internal;
https://riptutorial.com/ 43
Stream originalResponseBody = context.Response.Body;
try
{
await context.Request.Body.CopyToAsync(requestBodyStream);
requestBodyStream.Seek(0, SeekOrigin.Begin);
requestBodyStream.Seek(0, SeekOrigin.Begin);
context.Request.Body = requestBodyStream;
context.Response.Body = responseBodyStream;
responseBodyStream.Seek(0, SeekOrigin.Begin);
responseBody = new StreamReader(responseBodyStream).ReadToEnd();
AuditLogger.LogToAudit(context.Request.Host.Host,
context.Request.Path, context.Request.QueryString.ToString(),
context.Connection.RemoteIpAddress.MapToIPv4().ToString(),
string.Join(",", context.Request.Headers.Select(he => he.Key +
":[" + he.Value + "]").ToList()),
requestBodyText, responseBody, DateTime.Now,
watch.ElapsedMilliseconds);
responseBodyStream.Seek(0, SeekOrigin.Begin);
await responseBodyStream.CopyToAsync(originalResponseBody);
}
catch (Exception ex)
{
ExceptionLogger.LogToDatabse(ex);
byte[] data = System.Text.Encoding.UTF8.GetBytes("Unhandled Error
occured, the error has been logged and the persons concerned are notified!! Please, try again
in a while.");
originalResponseBody.Write(data, 0, data.Length);
}
finally
{
context.Request.Body = originalRequestBody;
context.Response.Body = originalResponseBody;
}
}
}
}
}
Read ASP.NET Core - Log both Request and Response using Middleware online:
https://riptutorial.com/asp-net-core/topic/9510/asp-net-core---log-both-request-and-response-
using-middleware
https://riptutorial.com/ 44
Chapter 4: Authorization
Examples
Simple Authorization
[Authorize]
public class SomeController : Controller
{
public IActionResult Get()
{
}
[Authorize]
public IActionResult Post()
{
}
}
If you want to allow all users to access one of the actions you can use AllowAnonymousAttribute
[Authorize]
public class SomeController: Controller
{
public IActionResult Get()
{
}
[AllowAnonymous]
public IActionResult Post()
{
}
}
Now Post can be accessed by any user. AllowAnonymous always comes as a priority to authorize, so
https://riptutorial.com/ 45
if a controller is set to AllowAnonymous then all it's actions are public, regardless of if they have an
AuthorizeAttribute or not.
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
})
This is done by adding a default authorization policy to each controller - any Authorize/
AllowAnonymous Attributes over a controller/action will override these settings.
https://riptutorial.com/ 46
Chapter 5: Bundling and Minification
Examples
Grunt and Gulp
In ASP.NET Core apps, you bundle and minify the client-side resources during design-time using
third party tools, such as Gulp and Grunt. By using design-time bundling and minification, the
minified files are created prior to the application’s deployment. Bundling and minifying before
deployment provides the advantage of reduced server load. However, it’s important to recognize
that design-time bundling and minification increases build complexity and only works with static
files.
This is done in ASP.NET Core by configuring Gulp via a gulpfile.js file within your project :
// Defining dependencies
var gulp = require("gulp"),
rimraf = require("rimraf"),
concat = require("gulp-concat"),
cssmin = require("gulp-cssmin"),
uglify = require("gulp-uglify");
// Defining paths
var paths = {
js: webroot + "js/**/*.js",
minJs: webroot + "js/**/*.min.js",
css: webroot + "css/**/*.css",
minCss: webroot + "css/**/*.min.css",
concatJsDest: webroot + "js/site.min.js",
concatCssDest: webroot + "css/site.min.css"
};
This approach will properly bundle and minify your existing Javascript and CSS files respectively
accordingly to the directories and globbing patterns that are used.
https://riptutorial.com/ 47
Bundler and Minifier Extension
Visual Studio also features an available Bundler and Minifier Extension that is capable of handling
this process for you. The extension allows you to easily select and bundle the files you need
without writing a line of code.
This will prompt to you name your bundle and choose a location to save it at. You'll then notice a
new file within your project called bundleconfig.json which looks like the following :
[
{
https://riptutorial.com/ 48
"outputFileName": "wwwroot/app/bundle.js",
"inputFiles": [
"wwwroot/lib/jquery/dist/jquery.js",
"wwwroot/lib/bootstrap/dist/js/bootstrap.js",
"wwwroot/lib/jquery-validation/dist/jquery.validate.js",
"wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"
]
}
]
NOTE: The order in which the files are selected will determine the order that they
appear in within the bundle, so if you have any dependencies, ensure you take that into
account.
[
{
"outputFileName": "wwwroot/app/bundle.js",
"inputFiles": [
"wwwroot/lib/jquery/dist/jquery.js",
"wwwroot/lib/bootstrap/dist/js/bootstrap.js",
"wwwroot/lib/jquery-validation/dist/jquery.validate.js",
"wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"
],
"minify": {
"enabled": true
}
}
]
• Open the Task Runner Explorer (via Tools > Task Runner Explorer).
• Right-click on the Update All Files option below bundleconfig.json.
• Select your preferred binding from the Bindings context menu.
https://riptutorial.com/ 49
After doing this, your bundles should be automatically updated at the preferred step that you
selected.
The ASP.NET Core RTM release introduced BundlerMinifier.Core, a new Bundling and Minification
tool that can be easily integrated into existing ASP.NET Core applications and doesn't require any
external extensions or script files.
Using BundlerMinifier.Core
To use this tool, simply add a reference to BundlerMinifier.Core within the tools section of
your existing project.json file as seen below :
"tools": {
"BundlerMinifier.Core": "2.0.238",
"Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
}
[
{
"outputFileName": "wwwroot/css/site.min.css",
"inputFiles": [
"wwwroot/css/site.css"
]
},
{
"outputFileName": "wwwroot/js/site.min.js",
"inputFiles": [
"wwwroot/js/site.js"
],
"minify": {
https://riptutorial.com/ 50
"enabled": true,
"renameLocals": true
},
"sourceMap": false
},
{
"outputFileName": "wwwroot/js/semantic.validation.min.js",
"inputFiles": [
"wwwroot/js/semantic.validation.js"
],
"minify": {
"enabled": true,
"renameLocals": true
}
}
]
dotnet bundle
Automated Bundling
The Bundling and Minification process can be automated as part of the build process by adding
the dotnet bundle command in the precompile section of your existing project.json file :
"scripts": {
"precompile": [
"dotnet bundle"
]
}
Available Commands
You can see a list of all of the available commands and their descriptions below :
• dotnet bundle - Executes the bundle command using the bundleconfig.json file to bundle
and minify your specified files.
• dotnet bundle clean - Clears all of the existing output files from disk.
• dotnet bundle watch - Creates a watchers that will automatically run dotnet bundle
whenever an existing input file from the bundleconfig.json configuration to bundle your files.
• dotnet bundle help - Displays all available help options and instructions for using the
command-line interface.
https://riptutorial.com/ 51
Chapter 6: Caching
Introduction
Caching helps in improving performance of an application by maintaining easily accessible copy of
the data. Aspnet Core comes with two easy to use and testing friendly caching abstractions.
Distributed Cache will hold the data cache in a centralized location which is accessible by
servers in cluster. It comes with three implementations out of the box : In Memory (for unit testing
and local dev), Redis and Sql Server.
Examples
Using InMemory cache in ASP.NET Core application
To use an in memory cache in your ASP.NET application, add the following dependencies to your
project.json file:
"Microsoft.Extensions.Caching.Memory": "1.0.0-rc2-final",
services.AddMemoryCache();
To add items to the cache in our application, we will use IMemoryCache which can be injected to any
class (for example Controller) as shown below.
Get will return the value if it exists, but otherwise returns null.
Use the Set method to write to the cache. Set accepts the key to use to look up the value, the
value to be cached, and a set of MemoryCacheEntryOptions. The MemoryCacheEntryOptions allow you to
specify absolute or sliding time-based cache expiration, caching priority, callbacks, and
https://riptutorial.com/ 52
dependencies. One of the sample below-
_memoryCache.Set(cacheKey, greeting,
new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(1)));
Distributed Caching
To leverage distributed cache, you'll have to reference one of the available implementations :
• Redis
• Sql server
[HttpGet]
public async Task<Books[]> GetAllBooks() {
var serialized = this.distributedCache.GetStringAsync($"allbooks");
Books[] books = null;
if (string.IsNullOrEmpty(serialized)) {
books = await Books.FetchAllAsync();
this.distributedCache.SetStringAsync($"allbooks",
JsonConvert.SerializeObject(books));
} else {
books = JsonConvert.DeserializeObject<Books[]>(serialized);
}
return books;
}
}
https://riptutorial.com/ 53
Chapter 7: Configuration
Introduction
Asp.net core provides configuration abstractions. They allow you to load configuration settings
from various sources and build a final configuration model which can then be consumed by your
application.
Syntax
• IConfiguration
• string this[string key] { get; set; }
• IEnumerable<IConfigurationSection> GetChildren();
• IConfigurationSection GetSection(string key);
Examples
Accessing Configuration using Dependency Injection
The recommended approach would be to avoid doing so and rather use IOptions<TOptions> and
IServiceCollection.Configure<TOptions>.
That said, this is still pretty straightforward to make IConfigurationRootavailable application wide.
In the Startup.cs constructor you should have the following code to build the configuration,
Configuration = builder.Build();
Getting Started
In this example we will describe what happens when you scaffold a new project.
First thing, the following dependencies will be added to you project (currently project.json file) :
https://riptutorial.com/ 54
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
"Microsoft.Extensions.Configuration.Json": "1.0.0",
It will also create a constructor in your Startup.cs file which will be in charge of building the
configuration using ConfigurationBuilder fluent api:
Configuration = builder.Build();
}
If a same setting is set in several sources, the latest source added will win and its value will be
selected.
Configuration can then be consumed using the indexer property. The colon : character serve a
path delimiter.
Configuration["AzureLogger:ConnectionString"]
It will load environment variables prefixed with APPSETTING_ It will then use colon : as the key path
separator.
APPSETTING_Security:Authentication:UserName = a_user_name
APPSETTING_Security:Authentication:Password = a_user_password
{
"Security" : {
https://riptutorial.com/ 55
"Authentication" : {
"UserName" : "a_user_name",
"Password" : "a_user_password"
}
}
}
** Note that Azure Service will transmit settings as environment variables. Prefix will be set for you
transparently. So to do the same in Azure just set two Application Settings in AppSettings blade :
Security:Authentication:UserName a_user_name
Security:Authentication:Password a_user_password
When dealing with large configuration sets of value, it might become quite unhandy to load them
one buy one.
Option model which comes with asp.net offers a convenient way to map a section to a dotnet poco:
For instance, one might hydrate StorageOptions directly from a configuration section b adding
Microsoft.Extensions.Options.ConfigurationExtensions package and calling the
Configure<TOptions>(IConfiguration config) extension method.
services.Configure<StorageOptions>(Configuration.GetSection("Storage"));
You can also source configuration from an in memory object such as a Dictionary<string,string>
https://riptutorial.com/ 56
Chapter 8: Configuring multiple
Environments
Examples
Having appsettings per Environment
• appsettings.Development.json
• appsettings.Staging.json
• appsettings.Production.json
Then open project.json file and include them into "include" in "publishOptions" section. This lists all
the files and folders that will be included when you publish:
"publishOptions": {
"include": [
"appsettings.Development.json",
"appsettings.Staging.json",
"appsettings.Production.json"
...
]
}
env.EnvironmentName
• for predefined Development, Staging, Production environments the best way is to use extension
methods from HostingEnvironmentExtensions class
https://riptutorial.com/ 57
env.IsDevelopment()
env.IsStaging()
env.IsProduction()
env.IsEnvironment("environmentname")
env.EnvironmentName == "Development"
This example shows how to configure multiple environments with different Dependency Injection
configuration and separate middlewares in one Startup class.
Using this pattern avoids putting to much if/else logic withing one single method/Startup class
and keep it clean and separated.
The same semantics also apply to the Startup class. StartupProduction will be used when the
ASPNETCORE_ENVIRONMENT variable is set to Production and will fall back to Startup when it's Stagging or
Development
A complete example:
https://riptutorial.com/ 58
.AddJsonFile($"appsettings.{hostEnv.EnvironmentName}.json", optional: true,
reloadOnChange: true);
if (hostEnv.IsDevelopment())
{
// This will push telemetry data through Application Insights pipeline faster,
allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
// This method gets called by the runtime. Use this method to add services to the
container
public static void RegisterCommonServices(IServiceCollection services)
{
services.AddScoped<ICommonService, CommonService>();
services.AddScoped<ICommonRepository, CommonRepository>();
}
services.AddOptions();
services.AddMvc();
}
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseMvc();
}
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseMvc();
}
}
https://riptutorial.com/ 59
Render environment specific content in view
You may need to render some content in view, which is specific to some environment only. To
achieve this goal you can use Environment tag helper:
<environment names="Development">
<h1>This is heading for development environment</h1>
</environment>
<environment names="Staging,Production">
<h1>This is heading for Staging or production environment</h1>
</environment>
The Environment tag helper will only render its contents if the current environment matches one of
the environments specified using the names attribute.
SET ASPNETCORE_ENVIRONMENT=Development
Note
When using PowerShell, you can use setx.exe to set environment variables permanently.
1. Start PowerShell
3. Restart PowerShell
If you do not want to use ASPNETCORE_ENVIRONMENT from environment variables and use it
from web.config of your application then modify web.config like this-
https://riptutorial.com/ 60
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>
</aspNetCore>
https://riptutorial.com/ 61
Chapter 9: Cross-Origin Requests (CORS)
Remarks
Browser security prevents a web page from making AJAX requests to another domain. This
restriction is called the same-origin policy, and prevents a malicious site from reading sensitive
data from another site. However, sometimes you might want to let other sites make cross-origin
requests to your web app.
Cross Origin Resource Sharing (CORS) is a W3C standard that allows a server to relax the same-
origin policy. Using CORS, a server can explicitly allow some cross-origin requests while rejecting
others. CORS is safer and more flexible than earlier techniques such as JSONP.
Examples
Enable CORS for all requests
Use the UseCors() extension method on the IApplicationBuilder in the Configure method to apply
the CORS policy to all requests.
app.UseCors(builder =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
// Other middleware..
app.UseMvc();
}
To enable a certain CORS policy for specific controllers you have to build the policy in the AddCors
extension within the ConfigureServices method:
https://riptutorial.com/ 62
.AllowAnyMethod()
.AllowAnyHeader();
}));
[EnableCors("AllowAll")]
public class HomeController : Controller
{
// ...
}
app.UseCors(builder =>
{
builder.WithOrigins("http://localhost:5000", "http://myproductionapp.com")
.WithMethods("GET", "POST", "HEAD")
.WithHeaders("accept", "content-type", "origin")
.SetPreflightMaxAge(TimeSpan.FromDays(7));
});
This policy only allows the origins http://localhost:5000 and http://myproductionapp.com with only
the GET, POST and HEAD methods and only accepts the accept, content-type and origin HTTP
headers. The SetPreflightMaxAge method causes the browsers to cache the result of the preflight
request (OPTIONS) to be cached for the specified amount of time.
To enable a CORS policy across all of your MVC controllers you have to build the policy in the
AddCors extension within the ConfigureServices method and then set the policy on the
CorsAuthorizationFilterFactory
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Cors.Internal;
...
public void ConfigureServices(IServiceCollection services) {
// Add AllowAll policy just like in single controller example.
services.AddCors(options => {
options.AddPolicy("AllowAll",
builder => {
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.Configure<MvcOptions>(options => {
options.Filters.Add(new CorsAuthorizationFilterFactory("AllowAll"));
https://riptutorial.com/ 63
});
}
This CORS policy can be overwritten on a controller or action basis, but this can set the default for
the entire application.
https://riptutorial.com/ 64
Chapter 10: Dependency Injection
Introduction
Aspnet core is built with Dependency Injection as one of its key core concepts. It introduces one
conforming container abstraction so you can replace the builtin one with a third-party container of
your choice.
Syntax
• IServiceCollection.Add(ServiceDescriptor item);
• IServiceCollection.AddScoped(Type serviceType);
• IServiceCollection.AddScoped(Type serviceType, Type implementationType);
• IServiceCollection.AddScoped(Type serviceType, Func<IServiceProvider, object>
implementationFactory);
• IServiceCollection.AddScoped<TService>()
• IServiceCollection.AddScoped<TService>(Func<IServiceProvider, TService>
implementationFactory)
• IServiceCollection.AddScoped<TService, TImplementation>()
• IServiceCollection.AddScoped<TService, TImplementation>(Func<IServiceProvider,
TImplementation> implementationFactory)
• IServiceCollection.AddSingleton(Type serviceType);
• IServiceCollection.AddSingleton(Type serviceType, Func<IServiceProvider, object>
implementationFactory);
• IServiceCollection.AddSingleton(Type serviceType, Type implementationType);
• IServiceCollection.AddSingleton(Type serviceType, object implementationInstance);
• IServiceCollection.AddSingleton<TService>()
• IServiceCollection.AddSingleton<TService>(Func<IServiceProvider, TService>
implementationFactory)
• IServiceCollection.AddSingleton<TService>(TService implementationInstance)
• IServiceCollection.AddSingleton<TService, TImplementation>()
• IServiceCollection.AddSingleton<TService, TImplementation>(Func<IServiceProvider,
TImplementation> implementationFactory)
• IServiceCollection.AddTransient(Type serviceType);
• IServiceCollection.AddTransient(Type serviceType, Func<IServiceProvider, object>
implementationFactory);
• IServiceCollection.AddTransient(Type serviceType, Type implementationType);
• IServiceCollection.AddTransient<TService>()
• IServiceCollection.AddTransient<TService>(Func<IServiceProvider, TService>
implementationFactory)
• IServiceCollection.AddTransient<TService, TImplementation>()
• IServiceCollection.AddTransient<TService, TImplementation>(Func<IServiceProvider,
TImplementation> implementationFactory)
• IServiceProvider.GetService(Type serviceType)
• IServiceProvider.GetService<T>()
• IServiceProvider.GetServices(Type serviceType)
• IServiceProvider.GetServices<T>()
Remarks
To use generic variants of IServiceProvider methods you have to include the following namespace:
https://riptutorial.com/ 65
using Microsoft.Extensions.DependencyInjection;
Examples
Register and manually resolve
The preferred way of describing dependencies is by using constructor injection which follows
Explicit Dependencies Principle:
ITestService.cs
TestService.cs
Startup.cs (ConfigureServices)
services.AddTransient<ITestService, TestService>();
}
HomeController.cs
using Microsoft.Extensions.DependencyInjection;
namespace Core.Controllers
{
public class HomeController : Controller
{
public HomeController(ITestService service)
{
int rnd = service.GenerateRandom();
}
}
}
Register dependencies
https://riptutorial.com/ 66
Builtin container comes with a set of builtin features :
Lifetime control
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<ITestService, TestService>();
// or
services.AddScoped<ITestService, TestService>();
// or
services.AddSingleton<ITestService, TestService>();
// or
services.AddSingleton<ITestService>(new TestService());
}
Enumerable dependencies
It is also possible to register enumerable dependencies :
services.TryAddEnumerable(ServiceDescriptor.Transient<ITestService, TestServiceImpl1>());
services.TryAddEnumerable(ServiceDescriptor.Transient<ITestService, TestServiceImpl2>());
Generic dependencies
You can also register generic dependencies :
services.Add(ServiceDescriptor.Singleton(typeof(IKeyValueStore<>), typeof(KeyValueStore<>)));
https://riptutorial.com/ 67
public class HomeController : Controller
{
public HomeController(IKeyValueStore<UserSettings> userSettings)
{
// do something with services.
}
}
// ...
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Core.Controllers
{
public class HomeController : Controller
{
public HomeController(ITestService service)
{
int rnd = service.GenerateRandom();
}
}
}
A less known builtin feature is Controller Action injection using the FromServicesAttribute.
[HttpGet]
public async Task<IActionResult> GetAllAsync([FromServices]IProductService products)
{
return Ok(await products.GetAllAsync());
}
An important note is that the [FromServices] can not be used as general "Property Injection" or
"Method injection" mechanism! It can only be used on method parameters of an controller action
or controller constructor (in the constructor it's obsolete though, as ASP.NET Core DI system
already uses constructor injection and there are no extra markers required).
It can not be used anywhere outside of a controllers, controller action. Also it is very specific
to ASP.NET Core MVC and resides in the Microsoft.AspNetCore.Mvc.Core assembly.
Original quote from the ASP.NET Core MVC GitHub issue (Limit [FromServices] to apply only to
parameters) regarding this attribute:
@rynowak:
@Eilon:
https://riptutorial.com/ 68
The problem with properties is that it appears to many people that it can be
applied to any property of any object.
Agreed, we've had a number of issues posted by users with confusion around how this
feature should be used. There's really been a fairly large amount of feedback both of
the kinds " [FromServices] is weird and I don't like it" and " [FromServices] has
confounded me". It feels like a trap, and something that the team would still be
answering questions about years from now.
To anyone in love with the current [FromServices] , I'd strongly recommend looking into
a DI system that can do property injection (Autofac, for example).
Notes:
• Any services registered with the .NET Core Dependency Injection system can be injected
inside an controller's action using the [FromServices] attribute.
• Most relevant case is when you need a service only in a single action method and don't want
to clutter your controller's constructor with another dependency, which will only be used
once.
• Can't be used outside of ASP.NET Core MVC (i.e. pure .NET Framework or .NET Core
console applications), because it resides in Microsoft.AspNetCore.Mvc.Core assembly.
• For property or method injection you must use one of third-party IoC containers available
(Autofac, Unity, etc.).
With ASP.NET Core the Microsoft team also introduced the Options pattern, which allows to have
strong typed options and once configured the ability to inject the options into your services.
First we start with a strong typed class, which will hold our configuration.
{
"mysettings" : {
"value1": "Hello",
"value2": "World"
https://riptutorial.com/ 69
}
}
Next we initialize it in the Startup class. There are two ways to do this
services.Configure<MySettings>(Configuration.GetSection("mysettings"));
2. Do it manually
services.Configure<MySettings>(new MySettings
{
Value1 = "Hello",
Value2 = Configuration["mysettings:value2"]
});
Finally we can inject the options into our services, using the IOptions<T> interface
Remarks
If the IOptions<T> isn't configured during the startup, injecting IOptions<T> will inject the default
instance of T class.
Resolving scoped services during application startup can be difficult, because there is no request
and hence no scoped service.
The following pattern solves the issue by first creating a new scope and then resolving the scoped
https://riptutorial.com/ 70
services from it, then once the work is done, disposing the scoped container.
if (await db.Database.EnsureCreatedAsync())
{
await SeedDatabase(db);
}
}
}
This is a semi-official way of the Entity Framework core team to seed data during application
startup and is reflected in the MusicStore sample application.
By default Controllers, ViewComponents and TagHelpers aren't registered and resolved via the
dependency injection container. This results in the inability to do i.e. property injection when using
a 3rd party Inversion of Control (IoC) container like AutoFac.
In order to make ASP.NET Core MVC resolve these Types via IoC too, one needs to add the
following registrations in the Startup.cs (taken from the official ControllersFromService sample on
GitHub)
manager.FeatureProviders.Add(new AssemblyMetadataReferenceFeatureProvider());
})
.AddControllersAsServices()
.AddViewComponentsAsServices()
.AddTagHelpersAsServices();
services.AddTransient<QueryValueService>();
services.AddTransient<ValueService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
https://riptutorial.com/ 71
Plain Dependency Injection example (Without Startup.cs)
This shows you how to use Microsoft.Extensions.DependencyInjection nuget package without the
use of the WebHostBuilder from kestrel (e.g. when you want to build something else then a
webApp):
services.AddTransient<ServiceDependingOnIMyInterface>();
var spWithService = services.BuildServiceProvider(); //Generaly its bad practise to
rebuild the container because its heavey and promotes use of anti-pattern.
spWithService.GetService<ServiceDependingOnIMyInterface>(); //only now i can resolve
}
}
interface IMyInterface
{
}
class ServiceDependingOnIMyInterface
{
private readonly IMyInterface _dependency;
IServiceCollection
To start building an IOC container with Microsoft's DI nuget package you start with creating an
IServiceCollection. You can use the already provided Collection: ServiceCollection:
https://riptutorial.com/ 72
This IServiceCollection is nothing more than an implementation of: IList<ServiceDescriptor>,
ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable
All the following methods are only extension methods to add ServiceDescriptor instances to the
list:
IServiceProvider
The serviceprovider is the one 'Compiling' all the registrations so that they can be used quickly,
this can be done with services.BuildServiceProvider() which is basically an extension mehtod for:
var provider = new ServiceProvider( services, false); //false is if it should validate scopes
Behind the scenes every ServiceDescriptor in the IServiceCollection gets compiled to a factory
method Func<ServiceProvider, object> where object is the return type and is: the created instance
of the Implementation type, the Singleton or your own defined factory method.
These registrations get added to the ServiceTable which is basically a ConcurrentDictionary with the
key being the ServiceType and the value the Factory method defined above.
Result
Now we have a ConcurrentDictionary<Type, Func<ServiceProvider, object>> which we can use
concurrently to ask to create Services for us. To show a basic example of how this could have
looked.
https://riptutorial.com/ 73
injection
https://riptutorial.com/ 74
Chapter 11: Error Handling
Examples
Redirect to custom error page
ASP.NET Core provides the status code pages middleware, that supports several different
extension methods, but we are interesting in UseStatusCodePages and
UseStatusCodePagesWithRedirects:
});
app.UseStatusCodePagesWithRedirects("~/errors/{0}");
UseExceptionHandler can be used to handle exceptions globally. You can get all the details of
exception object like Stack Trace, Inner exception and others. And then you can show them on
screen. You can easily implement like this.
app.UseExceptionHandler(
options => {
options.Run(
async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "text/html";
var ex = context.Features.Get<IExceptionHandlerFeature>();
if (ex != null)
{
var err = $"<h1>Error: {ex.Error.Message}</h1>{ex.Error.StackTrace }";
await context.Response.WriteAsync(err).ConfigureAwait(false);
}
});
https://riptutorial.com/ 75
}
);
https://riptutorial.com/ 76
Chapter 12: Injecting services into views
Syntax
• @inject<NameOfService><Identifier>
• @<Identifier>.Foo()
• @inject <type> <name>
Examples
The @inject Directive
ASP.NET Core introduces the concept of dependency injection into Views via the @inject directive
via the following syntax :
Example Usage
Adding this directive into your View will basically generate a property of the given type using the
given name within your View using proper dependency injection as demonstrated in the example
below :
<!-- This would call the service, which is already populated and output the results -->
There are <b>@WidgetService.GetWidgetCount()</b> Widgets here.
Required Configuration
Services that use dependency injection are still required to be registered within the
ConfigureServices() method of the Startup.cs file and scoped accordingly :
services.AddTransient<IWidgetService, WidgetService>();
}
https://riptutorial.com/ 77
Chapter 13: Localization
Examples
Localization using JSON language resources
In ASP.NET Core there are several different ways we can localize/globalize our app. It's important
to pick a way that suits your needs. In this example you'll see how we can make a multilingual
ASP.NET Core app that reads language specific strings from .json files and store them in memory
to provide localization in all sections of the app as well as maintaining a high performance.
Notes:
1. The namespace for this project is DigitalShop that you may change to your projects own
namespace
2. Consider creating a new project so that you don't run into weird errors
3. By no means this example show the best practices, So if you think it can be improved please
kindly edit it
To begin let's add the following packages to the existing dependencies section in the project.json
file:
"Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"
Now let's replace the Startup.cs file with: (using statements are removed as they can be easily
added later)
Startup.cs
namespace DigitalShop
{
public class Startup
{
public static string UiCulture;
public static string CultureDirection;
public static IStringLocalizer _e; // This is how we access language strings
https://riptutorial.com/ 78
.AddEnvironmentVariables();
Configuration = builder.Build();
LocalConfig = Configuration;
}
// This method gets called by the runtime. Use this method to add services to the
container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddViewLocalization().AddDataAnnotationsLocalization();
// IoC Container
// Add application services.
services.AddTransient<EFStringLocalizerFactory>();
services.AddSingleton<IConfiguration>(Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP
request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory
loggerFactory, EFStringLocalizerFactory localizerFactory)
{
_e = localizerFactory.Create(null);
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
https://riptutorial.com/ 79
throw new ArgumentNullException(nameof(httpContext));
}
culture = culture ?? "fa-IR"; // Use the value defined in config files or the
default value
uiCulture = uiCulture ?? culture;
Startup.UiCulture = uiCulture;
In the above code, we first add three public static field variables that we will later initialize using
the values read from the settings file.
In the constructor for Startup class we add a json settings file to the builder variable. The first file
is required for the app to work, so go ahead and create appsettings.json in your project root if it
doesn't already exist. Using Visual Studio 2015, this file is created automatically, so just change its
content to: (You may omit the Logging section if you don't use it)
appsettings.json
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"AppOptions": {
"Culture": "en-US", // fa-IR for Persian
"UICulture": "en-US", // same as above
"CultureDirection": "ltr" // rtl for Persian/Arabic/Hebrew
}
}
Models, Services and Languages. In the Models folder create another folder named Localization.
In the Services folder we create a new .cs file named EFLocalization. The content would be: (Again
using statements are not included)
https://riptutorial.com/ 80
EFLocalization.cs
namespace DigitalShop.Services
{
public class EFStringLocalizerFactory : IStringLocalizerFactory
{
private readonly LocalizationDbContext _db;
public EFStringLocalizerFactory()
{
_db = new LocalizationDbContext();
// Here we define all available languages to the app
// available languages are those that have a json and cs file in
// the Languages folder
_db.AddRange(
new Culture
{
Name = "en-US",
Resources = en_US.GetList()
},
new Culture
{
Name = "fa-IR",
Resources = fa_IR.GetList()
}
);
_db.SaveChanges();
}
https://riptutorial.com/ 81
get
{
var format = GetString(name);
var value = string.Format(format ?? name, arguments);
return new LocalizedString(name, value, resourceNotFound: format == null);
}
}
https://riptutorial.com/ 82
{
CultureInfo.DefaultThreadCurrentCulture = culture;
return new EFStringLocalizer(_db);
}
In the above file we implement the IStringLocalizerFactory interface from Entity Framework Core
in order to make a custom localizer service. The important part is the constructor of
EFStringLocalizerFactory where we make a list of all available languages and add it to the
database context. Each one of these language files act as a separate database.
Culture.cs
namespace DigitalShop.Models.Localization
{
public class Culture
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Resource> Resources { get; set; }
}
}
Resource.cs
namespace DigitalShop.Models.Localization
{
public class Resource
{
public int Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
public virtual Culture Culture { get; set; }
}
}
LocalizationDbContext.cs
https://riptutorial.com/ 83
namespace DigitalShop.Models.Localization
{
public class LocalizationDbContext : DbContext
{
public DbSet<Culture> Cultures { get; set; }
public DbSet<Resource> Resources { get; set; }
The above files are just models that will be populated with language resources, cultures and
there's also a typical DBContext used by EF Core.
The last thing we need to make all of this work is to create the language resource files. The JSON
files used to store a key-value pair for different languages available in your app.
In this example our app only has two languages available. English and Persian. For each of the
languages we need two files. A JSON file containing key-value pairs and a .cs file that contains a
class with the same name as JSON file. That class has one method, GetList that deserializes the
JSON file and returns it. This method is called in the constructor of EFStringLocalizerFactory that
we created earlier.
en-US.cs
namespace DigitalShop.Languages
{
public static class en_US
{
public static List<Resource> GetList()
{
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.MissingMemberHandling = MissingMemberHandling.Ignore;
return
JsonConvert.DeserializeObject<List<Resource>>(File.ReadAllText("Languages/en-US.json"),
jsonSerializerSettings);
}
}
}
en-US.json
[
{
"Key": "Welcome",
"Value": "Welcome"
},
{
"Key": "Hello",
"Value": "Hello"
https://riptutorial.com/ 84
},
]
fa-IR.cs
fa-IR.json
[
{
"Key": "Welcome",
"Value": ""دیدمآ شوخ
},
{
"Key": "Hello",
"Value": ""مالس
},
]
We are all done. Now in order to access the language strings (key-value pairs) anywhere in your
code (.cs or .cshtml) you can do the following:
<h1>@Startup._e["Welcome"]</h1>
• If you try to access a Key that doesn't exist in the JSON file or loaded, you will just get the key
literal (in the above example, trying to access Startup._e["How are you"] will return How are
you no matter the language settings because it doesn't exist
• If you change a string value in a language .json file, you will need to RESTART the app.
Otherwise it will just show the default value (key name). This is specially important when
you're running your app without debugging.
• The appsettings.json can be used to store all kinds of settings that your app may need
• Restarting the app is not necessary if you just want to change the language/culture
settings from appsettings.json file. This means that you can have an option in your apps
https://riptutorial.com/ 85
interface to let users change the language/culture at runtime.
By default the built-in Request Localization middleware only supports setting culture via query,
cookie or Accept-Language header. This example shows how create a middleware which allows to
set the culture as part of the path like in /api/en-US/products.
This example middleware assumes the locale to be in the second segment of the path.
https://riptutorial.com/ 86
// Right now it's not possible to use httpContext.GetRouteData()
// since it uses IRoutingFeature placed in httpContext.Features when
// Routing Middleware registers. It's not set when the Localization Middleware
// is called, so this example simply assumes the locale will always
// be located in the second segment of a path, like in /api/en-US/products
var parts = httpContext.Request.Path.Value.Split('/');
if (parts.Length < 3)
{
return Task.FromResult<ProviderCultureResult>(null);
}
if (!LocalePattern.IsMatch(parts[2]))
{
return Task.FromResult<ProviderCultureResult>(null);
}
Middleware Registration
var localizationOptions = new RequestLocalizationOptions
{
SupportedCultures = new List<CultureInfo>
{
new CultureInfo("de-DE"),
new CultureInfo("en-US"),
new CultureInfo("en-GB")
},
SupportedUICultures = new List<CultureInfo>
{
new CultureInfo("de-DE"),
new CultureInfo("en-US"),
new CultureInfo("en-GB")
},
DefaultRequestCulture = new RequestCulture("en-US")
};
app.UseRequestLocalization(localizationOptions);
https://riptutorial.com/ 87
Registering the route
Example of registering the routes without using a custom constraints
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "api/{culture::regex(^[a-z]{{2}}-[A-Za-z]{{4}}$)}}/{controller}/{id?}");
routes.MapRoute(
name: "default",
template: "api/{controller}/{id?}");
});
https://riptutorial.com/ 88
Chapter 14: Logging
Examples
Using NLog Logger
NLog.Extensions.Logging is the official NLog provider for Microsoft's in .NET Core and ASP.NET
Core. Here and here are instruction and example respectively.
Instead of requesting an ILoggerFactory and creating an instance of ILogger explicitly, you can
request an ILogger (where T is the class requesting the logger).
"Serilog": "2.2.0",
"Serilog.Extensions.Logging": "1.2.0",
"Serilog.Sinks.RollingFile": "2.0.0",
"Serilog.Sinks.File": "3.0.0"
loggerFactory.AddSerilog();
https://riptutorial.com/ 89
ILogger<HomeController> _logger = null;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
try
{
throw new Exception("Serilog Testing");
}
catch (System.Exception ex)
{
this._logger.LogError(ex.Message);
}
https://riptutorial.com/ 90
Chapter 15: Middleware
Remarks
Middleware is a software component that will determine how to process the request and decide
whether to pass it to the next component in the application pipeline. Each middleware has a vary
specific role and actions to preform on the request.
Examples
Using the ExceptionHandler middleware to send custom JSON error to Client
// other fields
Then put next ExceptionHandler middleware to Configure method. Pay attention that middleware
order is important.
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // or another Status
context.Response.ContentType = "application/json";
https://riptutorial.com/ 91
Middleware to set response ContentType
The idea is to use HttpContext.Response.OnStarting callback, as this is the last event that is fired
before the headers are sent. Add the following to your middleware Invoke method.
await nextMiddleware.Invoke(context);
}
From documentation:
The HttpContext.Items collection is the best location to store data that is only needed
while processing a given request. Its contents are discarded after each request. It is
best used as a means of communicating between components or middleware that
operate at different points in time during a request, and have no direct relationship with
one another through which to pass parameters or return values.
You can access it by simply assigning a value to a keyed entry, or by requesting the value for a
given key.
For example, some simple Middleware could add something to the Items collection:
and later in the pipeline, another piece of middleware could access it:
https://riptutorial.com/ 92
app.Run(async (context) =>
{
await context.Response.WriteAsync("Verified request? " + context.Items["isVerified"]);
});
Run
Terminates chain. No other middleware method will run after this. Should be placed at the end of
any pipeline.
Use
MapWhen
https://riptutorial.com/ 93
Enables branching pipeline. Runs specified middleware if condition is met.
Map
Similar to MapWhen. Runs middleware if path requested by user equals path provided in
parameter.
https://riptutorial.com/ 94
Chapter 16: Models
Examples
Model Validation with Validation Attrributes
If the built in attributes are not sufficient to validate your model data, then you can place your
validation logic in a class derived from ValidationAttribute. In this example only odd numbers are
valid values for a model member.
https://riptutorial.com/ 95
}
catch (Exception)
{
return new ValidationResult("Not a number.");
}
}
}
Model Class
https://riptutorial.com/ 96
Chapter 17: project.json
Introduction
Project json is a project configuration file structure, temporarily used by asp.net-core projects,
before Microsoft moved back to the csproj files in favor of msbuild.
Examples
Simple Library project example
{
"version": "1.0.0",
"dependencies": {
"NETStandard.Library": "1.6.1", //nuget dependency
},
"frameworks": { //frameworks the library is build for
"netstandard1.6": {}
},
"buildOptions": {
"debugType": "portable"
}
}
{
"name": String, //The name of the project, used for the assembly name as well as the name of
the package. The top level folder name is used if this property is not specified.
"version": String, //The Semver version of the project, also used for the NuGet package.
"description": String, //A longer description of the project. Used in the assembly properties.
"copyright": String, //The copyright information for the project. Used in the assembly
properties.
"title": String, //The friendly name of the project, can contain spaces and special characters
not allowed when using the `name` property. Used in the assembly properties.
"entryPoint": String, //The entrypoint method for the project. `Main` by default.
"testRunner": String, //The name of the test runner, such as NUnit or xUnit, to use with this
project. Setting this also marks the project as a test project.
"authors": String[], // An array of strings with the names of the authors of the project.
"language": String, //The (human) language of the project. Corresponds to the "neutral-
language" compiler argument.
"embedInteropTypes": Boolean, //`true` to embed COM interop types in the assembly; otherwise,
`false`.
"preprocess": String or String[], //Specifies which files are included in preprocessing.
"shared": String or String[], //Specifies which files are shared, this is used for library
export.
"dependencies": Object { //project and nuget dependencies
version: String, //Specifies the version or version range of the dependency. Use the \*
https://riptutorial.com/ 97
wildcard to specify a floating dependency version.
type: String, //type of dependency: build
target: String, //Restricts the dependency to match only a `project` or a `package`.
include: String,
exclude: String,
suppressParent: String
},
"tools": Object, //An object that defines package dependencies that are used as tools for the
current project, not as references. Packages defined here are available in scripts that run
during the build process, but they are not accessible to the code in the project itself. Tools
can for example include code generators or post-build tools that perform tasks related to
packing.
"scripts": Object, // commandline scripts: precompile, postcompile, prepublish & postpublish
"buildOptions": Object {
"define": String[], //A list of defines such as "DEBUG" or "TRACE" that can be used in
conditional compilation in the code.
"nowarn": String[], //A list of warnings to ignore.
"additionalArguments": String[], //A list of extra arguments that will be passed to the
compiler.
"warningsAsErrors": Boolean,
"allowUnsafe": Boolean,
"emitEntryPoint": Boolean,
"optimize": Boolean,
"platform": String,
"languageVersion": String,
"keyFile": String,
"delaySign": Boolean,
"publicSign": Boolean,
"debugType": String,
"xmlDoc": Boolean,
"preserveCompilationContext": Boolean,
"outputName": String,
"compilerName": String,
"compile": Object {
"include": String or String[],
"exclude": String or String[],
"includeFiles": String or String[],
"excludeFiles": String or String[],
"builtIns": Object,
"mappings": Object
},
"embed": Object {
"include": String or String[],
"exclude": String or String[],
"includeFiles": String or String[],
"excludeFiles": String or String[],
"builtIns": Object,
"mappings": Object
},
"copyToOutput": Object {
"include": String or String[],
"exclude": String or String[],
"includeFiles": String or String[],
"excludeFiles": String or String[],
"builtIns": Object,
"mappings": Object
}
},
"publishOptions": Object {
"include": String or String[],
"exclude": String or String[],
https://riptutorial.com/ 98
"includeFiles": String or String[],
"excludeFiles": String or String[],
"builtIns": Object,
"mappings": Object
},
"runtimeOptions": Object {
"configProperties": Object {
"System.GC.Server": Boolean,
"System.GC.Concurrent": Boolean,
"System.GC.RetainVM": Boolean,
"System.Threading.ThreadPool.MinThreads": Integer,
"System.Threading.ThreadPool.MaxThreads": Integer
},
"framework": Object {
"name": String,
"version": String,
},
"applyPatches": Boolean
},
"packOptions": Object {
"summary": String,
"tags": String[],
"owners": String[],
"releaseNotes": String,
"iconUrl": String,
"projectUrl": String,
"licenseUrl": String,
"requireLicenseAcceptance": Boolean,
"repository": Object {
"type": String,
"url": String
},
"files": Object {
"include": String or String[],
"exclude": String or String[],
"includeFiles": String or String[],
"excludeFiles": String or String[],
"builtIns": Object,
"mappings": Object
}
},
"analyzerOptions": Object {
"languageId": String
},
"configurations": Object,
"frameworks": Object {
"dependencies": Object {
version: String,
type: String,
target: String,
include: String,
exclude: String,
suppressParent: String
},
"frameworkAssemblies": Object,
"wrappedProject": String,
"bin": Object {
assembly: String
}
},
"runtimes": Object,
https://riptutorial.com/ 99
"userSecretsId": String
}
{
"version": "1.0.0",
"buildOptions": {
"emitEntryPoint": true // make sure entry point is emitted.
},
"dependencies": {
},
"tools": {
},
"frameworks": {
"netcoreapp1.1": { // run as console app
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.1.0"
}
},
"imports": "dnxcore50"
}
},
}
https://riptutorial.com/ 100
Chapter 18: Publishing and Deployment
Examples
Kestrel. Configuring Listening Address
Windows
SET ASPNETCORE_URLS=https://0.0.0.0:5001
OS X
export ASPNETCORE_URLS=https://0.0.0.0:5001
{
"server.urls": "http://<ip address>:<port>"
}
• listen 5000 on any IP4 and IP6 addresses from any interface:
"server.urls": "http://*:5000"
or
"server.urls": "http://::5000;http://0.0.0.0:5000"
https://riptutorial.com/ 101
• listen 5000 on every IP4 address:
"server.urls": "http://0.0.0.0:5000"
"publishOptions": {
"include": [
"hosting.json",
...
]
}
and in entry point for the application call .UseConfiguration(config) when creating WebHostBuilder:
host.Run();
}
https://riptutorial.com/ 102
Chapter 19: Rate limiting
Remarks
AspNetCoreRateLimit is an open source ASP.NET Core rate limiting solution designed to control
the rate of requests that clients can make to a Web API or MVC app based on IP address or client
ID.
Examples
Rate limiting based on client IP
With IpRateLimit middleware you can set multiple limits for different scenarios like allowing an IP
or IP range to make a maximum number of calls in a time interval like per second, 15 minutes, etc.
You can define these limits to address all requests made to an API or you can scope the limits to
each URL path or HTTP verb and path.
Setup
NuGet install:
Install-Package AspNetCoreRateLimit
Startup.cs code:
https://riptutorial.com/ 103
loggerFactory.AddDebug();
app.UseIpRateLimiting();
app.UseMvc();
}
You should register the middleware before any other components except loggerFactory.
if you load balance your app you'll need to use IDistributedCache with Redis or SQLServer so that
all kestrel instances will have the same rate limit store. Instead of the in memory stores you should
inject the distributed stores like this:
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 100
},
{
"Endpoint": "*",
"Period": "12h",
"Limit": 1000
},
{
"Endpoint": "*",
"Period": "7d",
"Limit": 10000
}
]
}
If EnableEndpointRateLimiting is set to false then the limits will apply globally and only rules that
have as endpoint * will apply. For example if you set a limit of 5 calls per second, any HTTP call to
any endpoint will count towards that limit.
https://riptutorial.com/ 104
If EnableEndpointRateLimiting is set to true then the limits will apply for each endpoint as in
{HTTP_Verb}{PATH}. For example if you set a limit of 5 calls per second for *:/api/values a client can
call GET /api/values 5 times per second but also 5 times PUT /api/values.
If StackBlockedRequests is set to false rejected calls are not added to the throttle counter. If a client
makes 3 requests per second and you've set a limit of one call per second, other limits like per
minute or per day counters will only record the first call, the one that wasn't blocked. If you want
rejected requests to count towards the other limits, you'll have to set StackBlockedRequests to true.
The RealIpHeader is used to extract the client IP when your Kestrel server is behind a reverse
proxy, if your proxy uses a different header then X-Real-IP use this option to set it up.
The ClientIdHeader is used to extract the client id for white listing, if a client id is present in this
header and matches a value specified in ClientWhitelist then no rate limits are applied.
"IpRateLimitPolicies": {
"IpRules": [
{
"Ip": "84.247.85.224",
"Rules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 200
}
]
},
{
"Ip": "192.168.3.22/25",
"Rules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 5
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 150
},
{
"Endpoint": "*",
"Period": "12h",
"Limit": 500
}
]
}
]
}
https://riptutorial.com/ 105
The IP field supports IP v4 and v6 values and ranges like "192.168.0.0/24", "fe80::/10" or
"192.168.0.0-192.168.0.255".
Endpoint format is {HTTP_Verb}:{PATH}, you can target any HTTP verb by using the asterix symbol.
Period format is {INT}{PERIOD_TYPE}, you can use one of the following period types: s, m, h, d.
Examples:
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
}
If, from the same IP, in the same second, you'll make 3 GET calls to api/values, the last call will
get blocked. But if in the same second you call PUT api/values too, the request will go through
because it's a different endpoint. When endpoint rate limiting is enabled each call is rate limited
based on {HTTP_Verb}{PATH}.
Rate limit calls with any HTTP Verb to /api/values to 5 calls per 15 minutes:
{
"Endpoint": "*:/api/values",
"Period": "15m",
"Limit": 5
}
{
"Endpoint": "get:/api/values",
"Period": "1h",
"Limit": 5
}
If, from the same IP, in one hour, you'll make 6 GET calls to api/values, the last call will get
blocked. But if in the same hour you call GET api/values/1 too, the request will go through
because it's a different endpoint.
Behavior
When a client make a HTTP call the IpRateLimitMiddleware does the following:
https://riptutorial.com/ 106
• extracts the IP, Client id, HTTP verb and URL from the request object, if you want to
implement your own extraction logic you can override the IpRateLimitMiddleware.SetIdentity
• searches for the IP, Client id and URL in the white lists, if any matches then no action is
taken
• searches in the IP rules for a match, all rules that apply are grouped by period, for each
period the most restrictive rule is used
• searches in the General rules for a match, if a general rule that matches has a defined
period that is not present in the IP rules then this general rule is also used
• for each matching rule the rate limit counter is incremented, if the counter value is greater
then the rule limit then the request gets blocked
If the request gets blocked then the client receives a text response like this:
You can customize the response by changing these options HttpStatusCode and
QuotaExceededMessage, if you want to implement your own response you can override the
IpRateLimitMiddleware.ReturnQuotaExceededResponse. The Retry-After header value is expressed in
seconds.
If the request doesn't get rate limited then the longest period defined in the matching rules is used
to compose the X-Rate-Limit headers, these headers are injected in the response:
info: AspNetCoreRateLimit.IpRateLimitMiddleware[0]
Request get:/api/values from IP 84.247.85.224 has been blocked, quota 2/1m exceeded by
3. Blocked by rule *:/api/value, TraceIdentifier 0HKTLISQQVV9D.
At application startup the IP rate limit rules defined in appsettings.json are loaded in cache by
either MemoryCacheClientPolicyStore or DistributedCacheIpPolicyStore depending on what type of
cache provider you are using. You can access the Ip policy store inside a controller and modify the
IP rules like so:
https://riptutorial.com/ 107
public IpRateLimitController(IOptions<IpRateLimitOptions> optionsAccessor, IIpPolicyStore
ipPolicyStore)
{
_options = optionsAccessor.Value;
_ipPolicyStore = ipPolicyStore;
}
[HttpGet]
public IpRateLimitPolicies Get()
{
return _ipPolicyStore.Get(_options.IpPolicyPrefix);
}
[HttpPost]
public void Post()
{
var pol = _ipPolicyStore.Get(_options.IpPolicyPrefix);
pol.IpRules.Add(new IpRateLimitPolicy
{
Ip = "8.8.4.4",
Rules = new List<RateLimitRule>(new RateLimitRule[] {
new RateLimitRule {
Endpoint = "*:/api/testupdate",
Limit = 100,
Period = "1d" }
})
});
_ipPolicyStore.Set(_options.IpPolicyPrefix, pol);
}
}
This way you can store the IP rate limits in a database and push them in cache after each app
start.
With ClientRateLimit middleware you can set multiple limits for different scenarios like allowing a
Client to make a maximum number of calls in a time interval like per second, 15 minutes, etc. You
can define these limits to address all requests made to an API or you can scope the limits to each
URL path or HTTP verb and path.
Setup
NuGet install:
Install-Package AspNetCoreRateLimit
Startup.cs code:
https://riptutorial.com/ 108
// needed to store rate limit counters and ip rules
services.AddMemoryCache();
services.Configure<ClientRateLimitOptions>(Configuration.GetSection("ClientRateLimiting"));
services.Configure<ClientRateLimitPolicies>(Configuration.GetSection("ClientRateLimitPolicies"));
app.UseClientRateLimiting();
app.UseMvc();
}
You should register the middleware before any other components except loggerFactory.
if you load balance your app you'll need to use IDistributedCache with Redis or SQLServer so that
all kestrel instances will have the same rate limit store. Instead of the in memory stores you should
inject the distributed stores like this:
"ClientRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
},
{
"Endpoint": "*",
https://riptutorial.com/ 109
"Period": "15m",
"Limit": 100
},
{
"Endpoint": "*",
"Period": "12h",
"Limit": 1000
},
{
"Endpoint": "*",
"Period": "7d",
"Limit": 10000
}
]
}
If EnableEndpointRateLimiting is set to false then the limits will apply globally and only rules that
have as endpoint * will apply. For example if you set a limit of 5 calls per second, any HTTP call to
any endpoint will count towards that limit.
If EnableEndpointRateLimiting is set to true then the limits will apply for each endpoint as in
{HTTP_Verb}{PATH}. For example if you set a limit of 5 calls per second for *:/api/values a client can
call GET /api/values 5 times per second but also 5 times PUT /api/values.
If StackBlockedRequests is set to false rejected calls are not added to the throttle counter. If a client
makes 3 requests per second and you've set a limit of one call per second, other limits like per
minute or per day counters will only record the first call, the one that wasn't blocked. If you want
rejected requests to count towards the other limits, you'll have to set StackBlockedRequests to true.
The ClientIdHeader is used to extract the client id, if a client id is present in this header and
matches a value specified in ClientWhitelist then no rate limits are applied.
"ClientRateLimitPolicies": {
"ClientRules": [
{
"ClientId": "client-id-1",
"Rules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 200
}
]
},
{
"Client": "client-id-2",
"Rules": [
{
"Endpoint": "*",
https://riptutorial.com/ 110
"Period": "1s",
"Limit": 5
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 150
},
{
"Endpoint": "*",
"Period": "12h",
"Limit": 500
}
]
}
]
}
Endpoint format is {HTTP_Verb}:{PATH}, you can target any HTTP verb by using the asterix symbol.
Period format is {INT}{PERIOD_TYPE}, you can use one of the following period types: s, m, h, d.
Examples:
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
}
If in the same second, a client make 3 GET calls to api/values, the last call will get blocked. But if
in the same second he calls PUT api/values too, the request will go through because it's a
different endpoint. When endpoint rate limiting is enabled each call is rate limited based on
{HTTP_Verb}{PATH}.
Rate limit calls with any HTTP Verb to /api/values to 5 calls per 15 minutes:
{
"Endpoint": "*:/api/values",
"Period": "15m",
"Limit": 5
}
https://riptutorial.com/ 111
{
"Endpoint": "get:/api/values",
"Period": "1h",
"Limit": 5
}
If in one hour, a client makes 6 GET calls to api/values, the last call will get blocked. But if in the
same hour he calls GET api/values/1 too, the request will go through because it's a different
endpoint.
Behavior
When a client make a HTTP call the ClientRateLimitMiddleware does the following:
• extracts the Client id, HTTP verb and URL from the request object, if you want to implement
your own extraction logic you can override the ClientRateLimitMiddleware.SetIdentity
• searches for the Client id and URL in the white lists, if any matches then no action is taken
• searches in the Client rules for a match, all rules that apply are grouped by period, for each
period the most restrictive rule is used
• searches in the General rules for a match, if a general rule that matches has a defined
period that is not present in the Client rules then this general rule is also used
• for each matching rule the rate limit counter is incremented, if the counter value is greater
then the rule limit then the request gets blocked
If the request gets blocked then the client receives a text response like this:
You can customize the response by changing these options HttpStatusCode and
QuotaExceededMessage, if you want to implement your own response you can override the
ClientRateLimitMiddleware.ReturnQuotaExceededResponse. The Retry-After header value is expressed
in seconds.
If the request doesn't get rate limited then the longest period defined in the matching rules is used
to compose the X-Rate-Limit headers, these headers are injected in the response:
info: AspNetCoreRateLimit.ClientRateLimitMiddleware[0]
Request get:/api/values from ClientId client-id-1 has been blocked, quota 2/1m exceeded
by 3. Blocked by rule *:/api/value, TraceIdentifier 0HKTLISQQVV9D.
https://riptutorial.com/ 112
Update rate limits at runtime
At application startup the client rate limit rules defined in appsettings.json are loaded in cache by
either MemoryCacheClientPolicyStore or DistributedCacheClientPolicyStore depending on what type
of cache provider you are using. You can access the client policy store inside a controller and
modify the rules like so:
[HttpGet]
public ClientRateLimitPolicy Get()
{
return _clientPolicyStore.Get($"{_options.ClientPolicyPrefix}_cl-key-1");
}
[HttpPost]
public void Post()
{
var id = $"{_options.ClientPolicyPrefix}_cl-key-1";
var clPolicy = _clientPolicyStore.Get(id);
clPolicy.Rules.Add(new RateLimitRule
{
Endpoint = "*/api/testpolicyupdate",
Period = "1h",
Limit = 100
});
_clientPolicyStore.Set(id, clPolicy);
}
}
This way you can store the client rate limits in a database and push them in cache after each app
start.
https://riptutorial.com/ 113
Chapter 20: Routing
Examples
Basic Routing
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Routing constraints
It is possible to create custom routing constraint which can be used inside routes to constraint a
parameter to specific values or pattern.
This constrain will match a typical culture/locale pattern, like en-US, de-DE, zh-CHT, zh-Hant.
return LocalePattern.IsMatch(locale);
}
}
services.Configure<RouteOptions>(options =>
{
options.ConstraintMap.Add("locale", typeof(LocaleConstraint));
});
https://riptutorial.com/ 114
Using it on Controllers
[Route("api/{culture:locale}/[controller]")]
public class ProductController : Controller { }
Using it on Actions
[HttpGet("api/{culture:locale}/[controller]/{productId}"]
public Task<IActionResult> GetProductAsync(string productId) { }
https://riptutorial.com/ 115
Chapter 21: Sending Email in .Net Core apps
using MailKit
Introduction
Currently .Net Core does not include support to send emails like System.Net.Mail from .Net. MailKit
project (which is available on nuget) is a nice library for this purpose.
Examples
Installing nuget package
Install-Package MailKit
using MailKit.Net.Smtp;
using MimeKit;
using MimeKit.Text;
using System.Threading.Tasks;
namespace Project.Services
{
/// Using a static class to store sensitive credentials
/// for simplicity. Ideally these should be stored in
/// configuration files
public static class Constants
{
public static string SenderName => "<sender_name>";
public static string SenderEmail => "<sender_email>";
public static string EmailPassword => "email_password";
public static string SmtpHost => "<smtp_host>";
public static int SmtpPort => "smtp_port";
}
public class EmailService : IEmailSender
{
public Task SendEmailAsync(string recipientEmail, string subject, string message)
{
MimeMessage mimeMessage = new MimeMessage();
mimeMessage.From.Add(new MailboxAddress(Constants.SenderName,
Constants.SenderEmail));
mimeMessage.To.Add(new MailboxAddress("", recipientEmail));
mimeMessage.Subject = subject;
https://riptutorial.com/ 116
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.AuthenticationMechanisms.Remove("XOAUTH2");
client.Send(mimeMessage);
client.Disconnect(true);
return Task.FromResult(0);
}
}
}
Read Sending Email in .Net Core apps using MailKit online: https://riptutorial.com/asp-net-
core/topic/8831/sending-email-in--net-core-apps-using-mailkit
https://riptutorial.com/ 117
Chapter 22: Sessions in ASP.NET Core 1.0
Introduction
Using Sessions in ASP.NET Core 1.0
Examples
Basic example of handling Session
2)In startup.cs and add AddSession() and AddDistributedMemoryCache() lines to the ConfigureServices
like this-
services.AddDistributedMemoryCache(); //This way ASP.NET Core will use a Memory Cache to store
session variables
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromDays(1); // It depends on user requirements.
options.CookieName = ".My.Session"; // Give a cookie name for session which will
be visible in request payloads.
});
using Microsoft.AspNetCore.Http;
return View();
}
}
5. If you are using cors policy then sometimes it may give errors, after enabling
session regarding headers about enabling AllowCredentials header and using
https://riptutorial.com/ 118
WithOrigins header instead of AllowAllOrigins.
https://riptutorial.com/ 119
Chapter 23: Tag Helpers
Parameters
Name Info
asp-action The name of the action method to which the form should be posted to
asp- The name of the controller where the action method specified in asp-action
controller exists
Custom route values you want to add as querystring to the form action attribute
asp-route-*
value. Replace 8 with the querystring name you want
Examples
Form Tag Helper - Basic example
<form asp-action="create"
asp-controller="Home"
asp-route-returnurl="dashboard"
asp-route-from="google">
<!--Your form elements goes here-->
</form>
https://riptutorial.com/ 120
And you are passing an object of this to the view from your action method.
@model CreateProduct
<form asp-action="create" asp-controller="Home" >
</form>
</form>
If you want the input field to be rendered with a default value, you can set the Name property value
of your view model in the action method.
Model binding will work fine if you use CreateProduct as your HttpPost action method parameter/a
parameter named name
And in your GET action method, you are creating an object of this view model, setting the
Categories property and sending to the view
https://riptutorial.com/ 121
new SelectListItem {Text = "Furniture", Value = "2"}
};
return View(vm);
}
@model CreateProduct
This will render the below markup(included only relevant parts of form/fields)
You can use the same view model as your HttpPost action method parameter
[HttpPost]
public ActionResult Create(CreateProduct model)
{
//check model.SelectedCategory value
/ /to do : return something
}
If you want to set an option as the selected option, you may simply set the SelectedCategory
property value.
https://riptutorial.com/ 122
}
If you want to render a multi select dropdown, you can simply change your view model property
which you use for asp-for attribute in your view to an array type.
In the view
@model CreateProduct
You can create your own tag helpers by implementing ITagHelper or deriving from the convenience
class TagHelper.
• The default convention is to target an html tag that matches the name of the helper without
the optional TagHelper suffix. For example WidgetTagHelper will target a <widget> tag.
• The [HtmlTargetElement] attribute can be used to further control the tag being targetted
• Any public property of the class can be given a value as an attribute in the razor markup. For
example a public property public string Title {get; set;} can be given a value as <widget
title="my title">
• By default, tag helpers translates Pascal-cased C# class names and properties for tag
helpers into lower kebab case. For example, if you omit using [HtmlTargetElement] and the
class name is WidgetBoxTagHelper, then in Razor you'll write <widget-box></widget-box>.
• Process and ProcessAsync contain the rendering logic. Both receive a context parameter with
information about the current tag being rendered and an output parameter used to
https://riptutorial.com/ 123
customize the rendered result.
Any assembly containing custom tag helpers needs to be added to the _ViewImports.cshtml file
(Note it is the assembly being registered, not the namespace):
@addTagHelper *, MyAssembly
<div class="widget-box">
<div class="widget-header">My Title</div>
<div class="widget-body">This is my content: some message</div>
</div>
[HtmlTargetElement("widget-box")]
public class WidgetTagHelper : TagHelper
{
public string Title { get; set; }
Label Tag Helper can be used to render label for a model property. It replaces method
https://riptutorial.com/ 124
Html.LabelFor in previous versions of MVC.
In the view you can use label HTML element and asp-for tag helper:
<form>
<label asp-for="Name"></label>
<input asp-for="Name" type="text" />
</form>
<form>
@Html.LabelFor(x => x.Name)
@Html.TextBoxFor(x => x.Name)
</form>
<form>
<label for="Name">Name</label>
<input name="Name" id="Name" type="text" value="">
</form>
Anchor tag helper is used generate href attributes to link to a particular controller action or MVC
route. Basic example
Sometimes, we need to specify additional parameters for the controller action that you are binding
to. We can specify values for these parameters by adding attributes with the asp-route- prefix.
https://riptutorial.com/ 125
Chapter 24: View Components
Examples
Create a View Component
View components encapsulate reusable pieces of logic and views. They are defined by:
• A ViewComponent class containing the logic for fetching and preparing the data for the view
and deciding which view to render.
• One or more views
Since they contain logic, they are more flexible than partial views while still promoting a good
separation of concerns.
//renders ~/Views/Shared/Components/MyCustom/Default.cshtml
return View(new MyCustomModel{ ... });
}
}
They can be invoked from any view (or even a controller by returning a ViewComponentResult)
The default project template creates a partial view _LoginPartial.cshtml which contains a bit of
logic for finding out whether the user is logged in or not and find out its user name.
Since a view component might be a better fit (as there is logic involved and even 2 services
injected) the following example shows how to convert the LoginPartial into a view component.
https://riptutorial.com/ 126
private readonly UserManager<ApplicationUser> userManager;
@model WebApplication1.Models.ApplicationUser
@await Component.InvokeAsync("Login")
When inheriting from base Controller class provided by the framework, you can use the
convenience method ViewComponent() to return a view component from the action:
https://riptutorial.com/ 127
return ViewComponent("Login", new { param1 = "foo", param2 = 42 });
}
If using a POCO class as a controller, you can manually create an instance of the
ViewComponentResult class. This would be equivalent to the code above:
https://riptutorial.com/ 128
Chapter 25: Working with JavascriptServices
Introduction
According to official documentation:
Examples
Enabling webpack-dev-middleware for asp.net-core project
Let's say you use Webpack for front end bundling. You can add webpack-dev-middleware to serve your
statics through tiny and fast server. It allows you to automatically reload your assets when content
has changed, serve statics in memory without continuously writing intermediate versions on disk.
Prerequisites
NuGet
Install-Package Microsoft.AspNetCore.SpaServices
npm
npm install --save-dev aspnet-webpack, webpack-dev-middleware, webpack-dev-server
Configuring
Extend Configure method in your Startup class
if (env.IsDevelopment())
{
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions()
{
ConfigFile = "webpack.config.js" //this is defualt value
});
}
https://riptutorial.com/ 129
Hot Module Replacement allows to add, change or delete app module when application is running.
Page reloading is not needed in this case.
Prerequisites
In addition to webpack-dev-middleware packages:
Configuration
Simply update configuration of UseWebpackDevMiddleware with new options:
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions()
{
ConfigFile = "webpack.config.js", //this is defualt value
HotModuleReplacement = true,
ReactHotModuleReplacement = true, //for React only
});
You can use aspnetcore-spa generator for Yeoman to create brand-new single page application with
asp.net core.
This allows you to choose one of the popular front end frameworks and generates project with
webpack, dev server, hot module replacement and server-side rendering features.
Just run
https://riptutorial.com/ 130
Read Working with JavascriptServices online: https://riptutorial.com/asp-net-
core/topic/9621/working-with-javascriptservices
https://riptutorial.com/ 131
Credits
S.
Chapters Contributors
No
Bundling and
5 Rion Williams, Zach Becknell
Minification
Configuring multiple
8 dotnetom, Johnny, Robert Paulsen, Sanket, Set, Tseng
Environments
Cross-Origin
9 Henk Mollema, Sanket, Saqib Rokadia, Tseng
Requests (CORS)
Injecting services
12 Alex Logan, Rion Williams
into views
https://riptutorial.com/ 132
16 Models Alex Logan, Ralf Bönning
Publishing and
18 Set
Deployment
Sending Email in
21 .Net Core apps using Ankit
MailKit
Sessions in
22 ravindra, Sanket
ASP.NET Core 1.0
23 Tag Helpers Ali, Daniel J.G., dotnetom, Shyju, tmg, Zach Becknell
Working with
25 hmnzr
JavascriptServices
https://riptutorial.com/ 133