0% found this document useful (0 votes)
97 views

Aspnetcoremvc PDF

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
97 views

Aspnetcoremvc PDF

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 74

.

NET Core, ASP.NET Core,


and ASP.NET Core MVC
brave new world
Outline
• Motivation
• .NET Core
• ASP.NET Core
• ASP.NET Core MVC
Motivation
• .NET has a strong history
• Very popular
• Lots of investments
• There is more than just Windows
• Many more platforms, devices,
and clouds
• .NET is evolving
• .NET needs to learn to run in more
places
• .NET needs modern tooling
.NET runtimes target a platform
• .NET Framework
• Windows-only
• .NET Core
• Cross-platform runtime
• .NET Native (UWP)
• Mono
• Windows Phone
• More…
.NET Core : next gen .NET for server apps
• Cross platform
• Windows, Linux, Mac, FreeBSD
• Portable
• Can be ~/bin deployed
• Can be user or machine installed as well
• Open source
• https://github.com/dotnet/coreclr
• Contains core runtime and mscorlib (e.g. GC, JIT, BCL)
• Does not contain many frameworks (e.g. WCF, WPF)
Development ecosystem
• SDK
• Command-line tooling (dotnet)
• Project system
• File-system based project system (project.json)
• Runtime, libraries, and packaging
• NuGet-focused
• Editors/IDEs
• Any text editor (VS Code, Emacs, Sublime, etc) and OmniSharp (OSS)
• Visual Studio (Microsoft)
• Project Rider (JetBrains)
Installing .NET SDK
• Use nightly builds (until RC2 is released)
• https://github.com/dotnet/cli
dotnet : command line tool
• Create new project
• Install NuGet
dependencies
• Build application
• Load .NET and run
application
• Package library
• Publish application
dotnet new
• Creates new project
• program.cs
• project.json
• Console-based application
using System;

namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
project.json
{
"version": "1.0.0-*",
• Project type "buildOptions": {
"emitEntryPoint": true
• Application or library },
"dependencies": {
• Dependencies "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-*"
},
• Primarily from NuGet "frameworks": {

• Target framework(s) "net46" : {},


"netcoreapp1.0": {
• Target framework "dependencies": {
"Microsoft.NETCore.App": {
moniker (TFM) "type": "platform",
"version": "1.0.0-rc2-3002659"
}
}
}
}
}
.NET platform standard
• Identifier (TFM) for required framework
• Replacement for PCL platform versioning nightmare
• Libraries target an expected API from framework
• "netstandard1.0", "netstandard1.1", …, "netstandard1.5"
• Can use libraries from earlier .NET Standard version
• Applications target a specific platform (and thus framework)
• "net451", "net452", "net46", "net461", "netcoreapp1.0", etc…
• Platforms support a specific .NET Standard version

https://github.com/dotnet/corefx/blob/master/Documentation/architecture/net-platform-standard.md
Platform support for .NET Standard
dotnet restore
• Downloads NuGet dependencies
• Might need a local nuget.config to target nightly builds from myget.org
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="AspNetCI" value="https://www.myget.org/F/aspnetcirelease/api/v3/index.json" />
</packageSources>
</configuration>

• Builds project.json.lock
• Snapshot of dependency versions
• Needed to load application
dotnet build / dotnet run / dotnet app.dll
• Builds project, or builds and runs application
• -c indicates configuration (release/debug)
• -f indicates framework to target
• -v emits verbose log output
• Binaries output to ~/bin/<configuration>/<framework> folder
ASP.NET Core
• The new pipeline
• Middleware
• Dependency Injection
• Configuration
Motivation
• Modern web stack
• Modern package system (NuGet)
• Lightweight/composable runtime
• Dependency injection
• Flexible configuration/deployment
"Empty Web Application"
OWIN Middleware Architecture
• Middleware are linked components that process requests
• Application code targeting a framework

Host

OWIN Server

Some Some Other


User Agent Application
Middleware Middleware
ASP.NET Core
• ASP.NET Core is HTTP pipeline implementation
• sits on top of .NET Core
• uses the middleware concept (but at a higher abstraction level than OWIN)
• comes with its own server (Kestrel)
• adds DI to provide services Host
• ASP.NET Core MVC is .NET Core
Microsoft's application framework
DI ASP.NET Core

User Agent Middleware Middleware MVC


How ASP.NET Core Applications start

IIS ASP.NET Core Module

start process Application.dll Startup.cs

dotnet run

ConfigureServices(…)

Configure(…)
Loading ASP.NET Core
public class Program
{
public static void Main()
{
var host = new WebHostBuilder()
.UseKestrel()
.UseIISIntegration()
.UseStartup<Startup>()
.Build();

host.Run(); public class Startup


} {
} public void Configure(IApplicationBuilder app)
{
...
}
}
Pipeline primitives
Start

app.Use(context, next)

app.Map("/path") app.Use(context, next)

app.Use(context, next) app.Run(context)

app.Run(context)
Run
namespace Microsoft.AspNetCore.Builder
{
public delegate Task RequestDelegate(HttpContext context);
}

app.Run(async context =>


{
await context.Response.WriteAsync("Hello ASP.NET Core");
});
Map

app.Map("/hello", helloApp =>


{
helloApp.Run(async (HttpContext context) =>
{
await context.Response.WriteAsync("Hello ASP.NET Core");
});
});
Use
app.Use(async (context, next) =>
{
if (!context.Request.Path.Value.EndsWith("/favicon.ico"))
{
Console.WriteLine("pre");
Console.WriteLine(context.Request.Path);

await next();

Console.WriteLine("post");
Console.WriteLine(context.Response.StatusCode);
}
else
{
await next();
}
});
Middleware classes
app.UseMiddleware<InspectionMiddleware>();

public class InspectionMiddleware


{
private readonly RequestDelegate _next;

public InspectionMiddleware(RequestDelegate next)


{
_next = next;
}

public async Task Invoke(HttpContext context)


{
Console.WriteLine($"request: {context.Request.Path}");
await _next(context);
}
}
Dependency Injection
• Various places
• Configure
• Middleware classes
• Higher-level frameworks (e.g. MVC controller)

• Host provided dependencies (e.g. IHostingEnvironment,


ILoggerFactory)
• Dependencies set up in ConfigureServices
DI Examples
public class Startup
{
public Startup(IHostingEnvironment environment)
{ /* stuff */ }

public void ConfigureServices(IServiceCollection services)


{ /* register more stuff */ }

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)


{
/* add middleware */
}
}
Registering dependencies
• New instance "per call"
services.AddTransient<IMyCustomService, MyCustomService>();

• New instance per HTTP request

services.AddScoped<IMyCustomService, MyCustomService>();

• Singleton
services.AddSingleton<IMyCustomService, MyCustomService>();
Configuration
• web.config is no more

• New configuration system based on key/value pairs


• command line
• environment variables
• JSON files
• INI files
• Configuration can come from multiple sources
• last source wins
Example
public class Startup
{
public IConfiguration Configuration { get; set; }

public Startup(IHostingEnvironment env)


{
Configuration = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables()
.Build();
}

// more
}
{
Using configuration "copyright": {
"year": "2015",
public class Startup
{
"company": "Foo Industries"
IConfiguration _configuration; }
}
public Startup()
{
_configuration = new ConfigurationBuilder()
...
.Build();
}

public void Configure(IApplicationBuilder app)


{
var copyright = new Copyright
{
Company = _configuration.Get("copyright_company"),
Year = _configuration.Get("copyright_year")
};

app.Run(async (context) =>


{
await context.Response.WriteAsync($"Copyright {copyright.Year}, {copyright.Company}");
});
}
}
ASP.NET Core MVC
• Packaging
• Middleware
• Routing and action selection
• Controller initialization
• Model binding changes
• Razor
• Filters
• APIs
• Error handling
Packaging
• MVC is packaged entirely as a NuGet
• Microsoft.AspNetCore.Mvc

{
"dependencies": {
"Microsoft.AspNetCore.Mvc": "1.0.0-rc2-*",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-*"
}
}
Middleware
• MVC is configured as middleware
• In ConfigureServices via AddMvc
• In Configure via UseMvc
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}

public void Configure(IApplicationBuilder app)


{
app.UseMvc();
}
}
Overriding default settings
• Delegate callback param used to override defaults
• Also fluent API on result from AddMvc()
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(mvc =>
{
mvc.Filters.Add(...);
mvc.ViewEngines.Add(...);
mvc.InputFormatters.Add(...);
mvc.OutputFormatters.Add(...);
});
}
}
Routing
• Routes configured via UseMvc
• RouteParameters.Optional from MVC 5 removed
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute("old_default",
"{controller}/{action}",
new {
controller = "Home", action="Index"
});

routes.MapRoute("new_default",
"{controller=Home}/{action=Index}/{id?}");
});
}
Controllers
• Controller base class still provided
• Action results now implement IActionResult
• Controller base provides many helpers to create action results
• View(), PartialView(), Content(), Json(), Ok(), Created(), HttpNotFound(),
HttpUnauthorized(), HttpBadRequest(), File(), PhysicalFile(), Redirect(),
RedirectPermanent()

public class HomeController : Controller


{
public IActionResult Index()
{
return View();
}
}
Attribute routing
• Attribute routing enabled by default
public class HomeController : Controller
{
// ~/ or ~/hello-world
[Route("/")]
[Route("/hello-world")]
public IActionResult Index()
{
return View();
}
}
Attribute routing
• Attribute routing can be applied to class
• [controller] and [action] act as tokens

[Route("[controller]/[action]")]
public class HomeController : Controller
{
// ~/Home/Index
public IActionResult Index()
{
return View();
}
}
Combining Route attributes
• Route attributes inherit path [Route("[controller]")]
public class HomeController : Controller
• RoutePrefix from MVC 5 {
// ~/Home/hello
removed [Route("hello")]
public IActionResult Index()
• Can replace inherited path {
• If template starts with "/" or }
return View();

"~/"
// ~/hello
[Route("/hello")]
public IActionResult Index2()
{
return View();
}
}
Route parameters
• [Route] allows parameters [Route("[controller]/[action]")]
public class HomeController : Controller
• With {param} syntax {
// GET ~/Home/Index
• Supports filters public IActionResult Index()
{
• With {param:filter} syntax return View();
}

// GET ~/Home/Index/5
[Route("{id:int}")]
public IActionResult Index(int id)
{
return View();
}
}
HTTP method based routes
• HttpGet, HttpPost, [Route("[controller]/[action]")]
public class HomeController : Controller
HttpPut, HttpDelete, {
HttpPatch // GET ~/Home/Index
[HttpGet]
• Filter action method public IActionResult Index()
{
on request method return View();
• Build on [Route] }
semantics // ~/Submit
[HttpPost("/Submit")]
public IActionResult Submit()
{
return View();
}
}
Areas
• Areas defined with the [Area] attribute
• Used to match an {area} route param
• Attribute routing allows [area] route token
• Views must still reside under ~/Areas/<area>/Views/<controller>
[Area("account")]
public class HomeController : Controller
public void Configure(IApplicationBuilder app) {
{ // ...
app.UseMvc(routes => }
{
routes.MapRoute("new_default",
"{area}/{controller=Home}/{action=Index}/{id?}");
});
}
POCO controllers
• Controller classes can be POCO
• Discovered in projects that reference Microsoft.AspNetCore.Mvc.*
• Identified by "Controller" class name suffix
• [NonController] disables
Dependency injection
public class HomeController
• Can inject dependencies {
into controller ctor IHttpContextAccessor _accessor;

• Special per-request types public HomeController(IHttpContextAccessor accessor)


{
can be property injected _accessor = accessor;
}
with decorator attribute
• ActionContext [ControllerContext]
public ControllerContext ControllerContext { get; set; }
• ControllerContext
// ...
}
Razor
• Shared config
• _ViewStart and _ViewImports
• Chunks
• @ directives
• TagHelpers
• Like WebForms custom controls
• ViewComponents
• Child action replacements
Shared razor configuration
• _ViewStart.cshtml still exists
• Can now easily be put in application root
• Layout assignment no longer is full path
• _ViewImports.cshtml is new
• Allows for sharing @using, @addTagHelper
chunks across views
• Can be located in the same places as
_ViewStart.cshtml
Razor directives (aka chunks)
• @model, @using, @section, @functions still exist
• @helper is gone
• @inject, @addTagHelper are new

• Also, @await Html.PartialAsync() is new


@inject
• Allows dependency injection into view
• @inject <type> <property>

@using Microsoft.Framework.OptionsModel
@inject IOptions<MyConfig> Config

<h2>@Config.Options.SiteName</h2>
Tag helpers
• Like custom controls for MVC
• Allow server-side code to inspect the element
• Can modify attributes, tag, and/or contents
• @addTagHelper Namespace.ClassName, Assembly
• Or @addTagHelper *, Assembly

@addTagHelper SpanTagHelper, YourProjectName

<span emoji="smile" />


Tag helper implementation
• TagHelper base class public class SpanTagHelper : TagHelper
• Class name used to {
public override void Process(
match element TagHelperContext context, TagHelperOutput output)
• Override Process or {
if (context.AllAttributes.ContainsKey("emoji") &&
ProcessAsync "smile" == context.AllAttributes["emoji"].ToString())
{
• Inspect element via output.Attributes.Add("title", "smile");
TagHelperContext output.Content.SetContent(" :) ");
output.SelfClosing = false;
• Alter output via }
TagHelperOutput }
}
Tag helper implementation
• [TargetElement] can be [TargetElement("span", Attributes = "emoji")]
public class EmojiTagHelper : TagHelper
used to match element {
[HtmlAttributeName("emoji")]
• Attributes can be used to public string Emoji { get; set; }
filter
public override void Process(
• [HtmlAttributeName] will TagHelperContext context, TagHelperOutput output)
read incoming attribute {
if ("smile" == Emoji)
• Will remove from output {
output.Attributes.Add("title", "smile");
output.Content.SetContent(" :) ");
output.SelfClosing = false;
}
}
}
MVC tag helpers
<a asp-controller="Manage" asp-action="Index">Manage Your Account</a>

<form asp-controller="Account" asp-action="LogOff" method="post"></form>

<environment names="Staging,Production">
<h1>You're in production!</h1>
</environment>

<link rel="stylesheet"
href="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/css/bootstrap.min.css"
asp-fallback-test-class="hidden"
asp-fallback-test-property="visibility"
asp-fallback-test-value="hidden" />

<script src="//ajax.aspnetcdn.com/ajax/jquery.validation/1.11.1/jquery.validate.min.js"
asp-fallback-src="~/lib/jquery-validation/jquery.validate.js"
asp-fallback-test="window.jquery && window.jquery.validator">
</script>
Validation tag helpers
<form asp-controller="Account" asp-action="ForgotPassword" method="post>
<h4>Enter your email.</h4>

<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>

<div>
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</form>
View components
• Replacement for child actions
• Partial views still exist
• Allow for a partial view that runs controller-like code
• Supports dependency injection

@Component.Invoke("Menu", 3)

Or

@await Component.InvokeAsync("Menu", 3)
View components
• ViewComponent base class public class MenuViewComponent : ViewComponent
{
• Matched by class prefix ICustomMenuService _menu;

• Or can use [ViewComponent] public MenuViewComponent(ICustomMenuService menu)


on POCO {
_menu = menu;
• Implement Invoke or }

InvokeAsync public IViewComponentResult Invoke(int depth)


• Returns {
var menuModel = _menu.GetMenu(depth);
IViewComponentResult or return View("Index", menuModel);
Task<IViewComponentResult> }
}
View components
• View component views are under:
• ~/Views/<controller>/Components/<component>
• ~/Views/Shared/Components/<component>
Filters
• Dependency injection
• Resource filter
• Async support
TypeFilter
• Allows for filters that require dependency injection
• Implemented via IFilterFactory
public class MyActionFilter : Attribute, IActionFilter
{
private IHostingEnvironment _env;
[TypeFilter(typeof(MyFilter))]
public MyActionFilter(IHostingEnvironment env) public IActionResult Index()
{ {
_env = env; // ...
} }

// ...
}
IResourceFilter
• Surrounds model binding, action, and result (including those filters)
public interface IResourceFilter : IFilter
{
void OnResourceExecuting(ResourceExecutingContext context);
void OnResourceExecuted(ResourceExecutedContext context);
}

• ResourceExecutingContext
• Value providers, model binders, input formatters, validation providers
• Can alter these on each request
Async filters
• All filters now have IAsync<Filter> support
• Authorization, Resource, Action, Result, Exception
• Pattern similar to middleware pipeline

public class MyResourceFilter : Attribute, IAsyncResourceFilter


{
public async Task OnResourceExecutionAsync(
ResourceExecutingContext context, ResourceExecutionDelegate next)
{
// pre
var resourceExecutedContext = await next();
// post
}
}
Web API
• Formatters
• Content negotiation
• Format filters
• XML support
Formatters
• Formatters have been split into two groups
• Input formatters triggered via [FromBody]
• Output formatters triggered via ObjectResult
Input formatters
• InputFormatter base class provides starting point
• SupportedMediaTypes property used to match Content-Type header
• [Consumes] filter can be used to limit formatters

Formatter Content type Comment


StringInputFormatter text/plain
JsonInputFormatter application/json, text/json
XmlSerializerInputFormatter application/xml, text/xml Not registered by default
XmlDataContractSerializerInputFormatter application/xml, text/xml Not registered by default
Output formatters
• ObjectResult chooses formatter from Accept header
• OutputFormatter base class has SupportedMediaTypes property
• ContentTypes property or [Produces] filter can be set explicitly to limit
formatters
• If Accept contains "*/*" then rest of Accept values ignored
• RespectBrowserAcceptHeader on MvcOptions can change behavior

Formatter Accept type Comment


StringOutputFormatter text/plain
JsonOutputFormatter application/json, text/json
XmlSerializerOutputFormatter application/xml, text/xml Not registered by default
XmlDataContractSerializerOutputFormatter application/xml, text/xml Not registered by default
FormatFilter & FormatFilterAttribute
• Allows overriding of Accept header
• Looks for "format" route or query param
• FormatterMappings on MvcOptions indicates format to media type mapping
• Sets ContentTypes on ObjectResult
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute("default",
"api/{controller} ");

routes.MapRoute("formatted",
"api/{controller}.{format}");
});
}
[Produces] & [Consumes] attributes
• Used to control what formatters used on request/response

[HttpGet]
[Consumes("application/xml")]
[Produces("application/json"")]
public object Get()
{
return new {...};
}
XML compatibility shim
• Library to help migrate from Web API to Core MVC
• Microsoft.AspNetCore.Mvc.WebApiCompatShim
• Provides old classes that map to the new framework
• ApiController
• FromUriAttribute
• HttpRequestMessage helpers/extensions/model binder
• HttpResponseMessage helpers/extensions/formatter
• HttpResponseException
• HttpError
Error handling
• HandleError from MVC 5 has been removed
• Resource filter's post processing runs after exception filters
• Last chance place to "handle" exceptions with a result
• Or just can log exceptions
Error pages
• Diagnostics middleware
• Microsoft.AspNetCore.Diagnostics
• UseDeveloperExceptionPage useful for development/debugging error info
• UseExceptionHandler useful for production error pages
• Logs error information
• Invokes error path
Summary
• Brave new (yet somewhat familiar) world
• .NET Core is a cross-platform framework
• ASP.NET Core is a flexible HTTP pipeline architecture
• MVC and Web API have had a quite a make over

You might also like