Vitaly Baum @butaji
ASP.NET MVC
Internals
> whoami
Vitaly Baum
Product Manager @ bright box
vitaly.baum@gmail.com
http://twitter.com/butaji
BACKGROUND
Web Application
MVC
What and How
Controller
How do I write Controllers?
How do requests come to Controller?
View
How does ViewEngine render views?
Why do I need to change ViewEngine?
AGENDA
QUESTIONS
• Non-IIS
• Azure in production
• NoSQL
• NGINX
OK. MVC
MVC
• Front Controller
An object that handles all requests
• Page Controller
An object that handles a request for a specific
page or action on a Web site
HTTP
HTTP
1. Request
2. Environment
3. Response
IIS
• System.Web.dll
• IHttpHandler (extensions)
• IHttpModule (HttpApplication events)
using System;
using System.Web;
public class LoggingHttpModule : IHttpModule
{
public void Dispose() {}
public void Init(HttpApplication context)
{
context.BeginRequest += BeginRequest;
}
void BeginRequest(object sender, EventArgs e)
{
Logger.Instance.Log("Hi there!");
}
}
public class RequestHandler : IHttpHandler
{
public RequestHandler() { }
public bool IsReusable { get { return false; } }
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
context.Response.Write("{ id : 1 }");
}
}
ASP.NET MVC?
• Were implemented on HttpHandlers
• https://servicestack.net/features
• http://www.castleproject.org/projects/monorail/
• http://mvc.fubu-project.org
IIS + ASP.NET
• Highly coupled
• Hard to manage (ain’t my job)
• New features only with OS updates
a. Web Server
b. Middleware
c. Application
SERVER INTEROP
• authentication
• cookies
• authorization
• output caching
• localisation
• request servicing (app)
MIDDLEWARE
SERVER INTEROP
• Rack (ruby)
• WSGI (Python)
• Node.js
• OWIN (.NET)
require 'rack'
app = Proc.new do |env| # ENV HASH
[
'200', # HTTP CODE
{'Content-Type' => 'text/html'},
['A barebones rack app.’] # BODY
]
end
Rack::Handler::WEBrick.run app
MIDDLEWARE
var http = require('http');
var server = http.createServer(function (req, res) {
res.writeHead(200,
{'Content-Type': 'text/plain'});
res.end('Hello Worldn');
})
server.listen(1337, '127.0.0.1');
MIDDLEWARE
var express = require('express')
var app = express()
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
MIDDLEWARE
//OWIN Implementation
using AppFunc = Func<
IDictionary<string, object>,
Task
>;
MIDDLEWARE
OWIN
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Run(context => {
return context
.Response.WriteAsync("Hi OWIN/Katana");
});
}
}
ASP.NET MVC
ASP.NET MVC LIFECYCLE
Routing
UrlRoutingModule -> IRouteHandler ->
IHttpHandler
Controller
Execute -> Filters -> Model Binding -> Action
Action
Your Code
Result
Views -> TypeFormater
ROUTE HANDLER
public class MyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(
RequestContext requestContext)
{
var handler = new MyMVCHandler();
handler.RequestContext = requestContext;
return handler;
}
}
ROUTE HANDLER
• In case of dynamic routes
• CMS
• Blog with custom Slugs
MVC HANDLER
public class MyMVCHandler : MvcHandler
{
protected override void ProcessRequest(IHttpContext
httpContext)
{
var controllerName =
GetRequiredString(this.RequestContext.RouteData, "controller");
var controller = CreateController(controllerName);
var controllerContext = new
ControllerContext(base.RequestContext, controller);
controller.Execute(controllerContext);
}
private IController CreateController(string controllerName)
{
return _controllerFactory.Create(controllerName);
}
}
DEPENDENCY MGMT
public class CustomControllerFactory : IControllerFactory
{
public IController CreateController(RequestContext
requestContext, string name)
{
return _serviceLocator.Resolve<IController>(name);
}
}
DEPENDENCY MGMT
//ASP.NET vNext has new way to inject dependencies
public class Startup
{
public void Configure(IBuilder app)
{
var services = new ServiceCollection();
services.AddTransient<IMessageGenerator,
HelloMessageGenerator>();
app.UseMiddleware(typeof(ContainerMiddleware));
}
}
OK. CTRL
CONTROLLER
FAT VS SKINNY
CONTROLLER
[HttpPost, ActionName("Delete")]
public async Task<ActionResult> DeleteConfirmed(int id)
{
var album = await _storeContext.Albums.FindAsync(id);
if (album == null)
{
return HttpNotFound();
}
_storeContext.Albums.Remove(album);
await _storeContext.SaveChangesAsync();
return RedirectToAction("Index");
}
CONTROLLER
[HttpPost, ActionName("Delete")]
public async Task<ActionResult> DeleteConfirmed(int id)
{
var removed = await _albumService.Remove(id);
return Status.NotFound == removed.Status
? HttpNotFound()
: RedirectToAction(“Index");
}
CONTROLLER
//vNext can host POCOs controllers
public List<Data> GetData()
{
return null; //will return 204 (NoContent)
}
def {action_name}( {params} )
app_res = {domain_service}.{call}( {params} )
return app_res
end
OK. VIEW
SEO OF SPA
SEO OF SPA
MIDDLEWARE
Middleware
Application
GET /
User-Agent: Chrome
GET /
User-Agent: Google
JSON
Middleware
Application
GET /
User-Agent: Google
GET /
User-Agent: Chrome
HTML
SEO OF SPA
SHARED TEMPLATES
SHARED TEMPLATES
Server Client
Template
http://mustache.github.io
{{#repo}}
<b>{{name}}</b>
{{/repo}}
{
"repo": [
{ "name": "resque" },
{ "name": "hub" },
{ "name": "rip" }
]
}
<b>resque</b>
<b>hub</b>
<b>rip</b>+ =
YET ANOTHER TEMPLATE
VIEW ENGINE
public interface IViewEngine
{
ViewEngineResult FindPartialView(ControllerContext ctx, string
name, bool useCache);
ViewEngineResult FindView(ControllerContext ctx, string viewName,
string masterName, bool useCache);
void ReleaseView(ControllerContext ctx, IView view);
}
public interface IView
{
void Render(ViewContext ctx, TextWriter writer);
}
http://buildstarted.com/2013/12/16/javascript-view-engine-for-aspnet/
VIEW ENGINE
services.AddMvc().Configure<MvcOptions>(
options =>
{
options.ViewEngines.Add(
new MyCustomerViewEngine ());
});
COVERED TOPICS
Web Application
MVC
What and How
Controller
How do I write Controllers?
How do requests come to Controller?
View
How does ViewEngine render views?
Why do I need to change ViewEngine?
THANK
YOU
Vitaly Baum
@butaji

ASP.NET Internals