Enterprise development (in .NET), Day 4
ASP.NET MVC
Andrey Shchekin http://ashmind.com Sankt-Petersburg, 2009
Images © Mattahan Productions, Inc | http://mattahan.deviantart.com/ | http://mattahan.insocada.com/
Basics
Web server anatomy
Request
Worker Thread
Response
Worker Process (w3wp)
Worker Thread
Request types
GET POST HEAD PUT DELETE address bar/links submit buttons limited GET semantic POST semantic POST
REST
Rich client
Content Presentation Behavior AJAX COMET (HTML) (CSS) (JavaScript)
methods to do async requests "server push", server events
MVC: Routing
Routing
http://yourapp.com/video/view/q123?key=zyx application: controller: action: id: parameters: http://yourapp.com video view q123 { key = zyx }
ASP.NET MVC project
Route configuration
routes.MapRoute( "Default", "{controller}/{action}/{id}", // Route name // URL with parameters
// Parameter defaults new { controller = "Home", action = "Index", id = "" } );
Route configuration
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Images", "image/{id}.png", new { controller = "Images", action = "Get", id = "" } ); routes.MapRoute( "Default", "{controller}/{action}/{id}",
// Route name // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults ); }
Parameter constraints
routes.MapRoute( "Post", "post/{year}/{month}/{day}", new { controller = "Post", action = "Get" }, new { year = "\d{4}", month = "\d{2}", day = "\d{2}" } );
Custom constraints: implement IRouteConstraint.
MVC: Controllers
Action
http://yourapp.com/person/list http://yourapp.com/person/get/5
public class PersonController : Controller { public ActionResult List() { var persons = personProvider.LoadList(); return View(persons); } public ActionResult Get(int id) { return View(personProvider.Load(id)); } }
Action Results
public ActionResult List() { … return View(persons); } public ActionResult Save(…) { … return RedirectToAction("List"); } public ActionResult Avatar(…) { … return File(imageData, "image/png"); }
Model Binders
public ActionResult Save([Bind(Exclude="Updated")] Person person) { … return RedirectToAction("List"); }
Custom Model Binders: 1. implement IModelBinder 2. add attribute to controller or action
[ModelBinder(typeof(MyPersonBinder))] public class PersonController : Controller
Action Filters
[HandleError] public class PostController : Controller { [Authorize(Roles = "Editor, Administrator")] public ActionResult Edit(int id) { … return View(…); } }
Custom Action Filters: 1. Create a custom attribute 2. Inherit ActionFilterAttribute or FilterAttribute
Action Filters
Stages: 1. IAuthorizationFilter.OnAuthorization 2. IActionFilter.OnActionExecuting 3. IActionFilter.OnActionExecuted 4. (optional) IExceptionFilter.OnException 5. IResultFilter.OnResultExecuting 6. IResultFilter.OnResultExecuted
MVC: Views
Structure
Server markup
<%= Model.Name %>
<% if (Model.HasErrors) { %> …
<%$ Resources: WelcomeMessage %>
<%-- Comment --%>
Html Helpers
Login: <%= Html.TextBox("login") %> Password: <%= Html.TextBox("password") %> <%= Html.CheckBox("remember", false) %> Remember me
Custom Html helpers: Extension methods to HtmlHelper class.
Action Links
<% foreach (var item in Model) { %> - <%= Html.ActionLink(item.Name, "Details", new { id = item.ID }) %>
<% } %>
http://yourapp.com/person/details/5
Html Forms 1
<% using(Html.BeginForm("Login", "Account")) { %> Login: <%= Html.TextBox("login") %> Password: <%= Html.TextBox("password") %> <%= Html.CheckBox("remember", false) %> Remember me
<% } %>
Html Forms 2
<% using(Html.BeginForm("Login", "Account")) { %> Login: <%= Html.TextBox("login") %> Password: <%= Html.TextBox("password") %> <%= Html.CheckBox("remember", false) %> Remember me
<% } %>
public ActionResult Login( string login, string password, bool remember ) { … }
Html Forms 3 (+AcceptVerbs)
<% using(Html.BeginForm()) { %> First Name: <%= Html.TextBox("person.FirstName") %> Last Name: <%= Html.TextBox("person.LastName") %> … <% } %>
[AcceptVerbs(HttpVerbs.Post)] public ActionResult Edit(Person person) { … }
Model / ViewData
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage
" %>
public ActionResult Get(int id) { … return View(person); }
Always use typed ViewPages and Model.
RenderPartial
<% foreach (var item in Model) { %> - <% Html.RenderPartial("Item", item); %>
<% } %>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl- " %>
Common practice – use *.ascx for partials.
RenderAction
<% Html.RenderAction("LoginDetails", "Account"); %>
public ActionResult LoginDetails() { … return PartialView(details); }
MVC: Model
Model / ViewModel
Model
MVC model can be whatever you want. No specific attributes or anything else required.
ViewModel
Architectural concept – simplifying model for the views
Model.Date Model.Title + Model.LastName → ViewModel.FormattedDate → ViewModel.Salutation
MVC: Other
Validation: Markup
<%= Html.TextBox("LastName", Model.LastName) %> <%= Html.ValidationMessage("LastName", "*") %>
<%= Html.TextBox("Age", Model.Age) %> <%= Html.ValidationMessage("Age", "*") %>
Validation: Logic
[AcceptVerbs(HttpVerbs.Post)] public ActionResult Edit(Person person) { if (person.LastName.Trim().Length == 0) ModelState.AddModelError("LastName", "Last name is required."); if (person.Age < 0) ModelState.AddModelError("Age", "Age cannot be less than zero."); if (!ModelState.IsValid) return View(person); personStorage.Save(person); return RedirectToAction("List"); }
It is not generally recommended to put validation in a controller.
AJAX
<%= Ajax.ActionLink( item.Name, "Details", new { id = item.ID }, new AjaxOptions { UpdateTargetId = "details" } ) %>
public ActionResult Details(int id) { if (Request.IsAjaxRequest()) return PartialView("DetailsPart", item); return View(item); }
AJAX
<% using(Ajax.BeginForm("Login", "Account", new AjaxOptions {…})) { %> Login: <%= Html.TextBox("login") %> Password: <%= Html.TextBox("password") %> <%= Html.CheckBox("remember", false) %> Remember me <% } %>
Anyway, javascript (jQuery) is better for any real AJAX.
Caching
[OutputCache(Duration = 20)] public ActionResult List() { return View(…); }
web.config
Application or folder level (similar to .htaccess). A lot of important stuff: /
ASP.NET MVC 2: EditorFor
<% using(Html.BeginForm()) { %> First Name: <%= Html.EditorFor(p => p.FirstName) %> Last Name: <%= Html.EditorFor(p => p.LastName) %> Birthday: <%= Html.EditorFor(p => p.BirthDate) %> … <% } %>
Also: Html.DisplayFor()
Questions