![]() | Тази статия се нуждае от подобрение. Ако желаете да помогнете на Уикипедия, използвайте опцията редактиране в горното меню над статията, за да нанесете нужните корекции. |
ASP.NET MVC е платформа, създадена от Microsoft, която служи за изработване на уеб приложения с шаблон за дизайн Модел-Изглед-Контролер (на английски: Model-View-Controller, MVC). Платформата използва C#, HTML, CSS, JavaScript и бази данни и е съвременно средство за уеб приложения, което обаче не замества изцяло уеб формите. Платформата включва нови тенденции в разработката на уеб приложения, притежава много добър контрол върху HTML и дава възможност за създаване на всякакви приложения. ASP.NET MVC може да бъде много лесно тествана и допълвана, защото е изградена от отделни модули, изцяло независими един от друг. Чрез платформата се създават цялостни приложения, които се стартират, а не единични скриптове (като при PHP например).[1]
Моделът представлява част от приложението, което реализира домейн логиката, също известна като бизнес логика. Домейн логиката обработва данните, които се предават между базата данни и потребителския интерфейс. Например, в една система за инвентаризация, моделът отговаря за това дали елемент от склада е наличен. Моделът може да бъде част от заявлението, което актуализира базата данни когато даден елемент е продаден или доставен в склада. Често моделът съхранява и извлича официална информация в базата данни.
Изглед модела позволява да се оформят няколко изгледа от един или повече модела от данни или източници в един обект. Този модел е оптимизиран за потребление и изпълнение.[2]
MVC (model – view – control) предоставя различни видове и начини за това как да бъде изграден даден модел. Практиката е доказала, че поради липсата на много информация, практики и препоръки, е възможно да се получат разминавания за това как да бъде направен модел. Всъщност, истината е, че няма еднотипно решение, което да задоволява всички възникнали проблеми. Тук ще разгледаме някои от основните модели, които се използват, с описание на конкретен пример. Трябва да се отбележи, че това са модели, използвани при решаването на реални практически проблеми.
Представете си модел, който изглежда по следния начин:
public class Motorcycle
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public string VIN { get; set; }
}
Когато този код премине в изглед, той ни позволява да пишем HTML помощници в стила на нашия избор:
<%=Html.TextBox("Make") %>
<%=Html.TextBoxFor(m => m.Make) %>
Разбира се с този модел по подразбиране може да се върнем обратно при контролера:
public ActionResult Save(Motorcycle motorcycle)
Въпреки че този модел е доста опростен и работи изчистено и елегантно, той се „чупи“ много често дори и при по-опростени изгледи. Свързването към домейн модела в този случай не винаги е достатъчно за пълно представяна на изгледа.
Ако разгледаме пак мотоциклета от горния пример, в реалния живот е много по-реалистично изгледът за даден обект да се нуждае от повече от една характеристика, за да бъде представен правилно. Примерно, характеристиките от горния пример Make и Model ще бъдат извиквани от падащи менюта. Ето защо, този модел е предназначен да представи изгледа като съдържател на всички характеристики, необходими за правилното представяне:
public class MotorcycleViewModel
{
public Motorcycle Motorcycle { get; set; }
public SelectList MakeList { get; set; }
public SelectList ModelList { get; set; }
}
В този случай, контролерът има отговорност за това моделът MotorcycleView да е правилно попълнен с данни от хранилищата (Пример: получаване на Motorcycle от база данни, извличане на колекция Make/Model от база данни). Нашите HTML помощници леко се променят, защото сега се отнасят за MotorcycleMake, а не само за Make:
<%=Html.DropDownListFor(m => m.Motorcycle.Make, Model.MakeList) %>
Когато формата се публикува, все още е необходимо да има Save() метод:
public ActionResult Save([Bind(Prefix = "Motorcycle")]Motorcycle motorcycle)
В този пример трябва атрибутът Motorcycle да се използва като префикс към HTML елемента, от който се интересуваме Този модел е опростен и подходящ за много ситуации. Въпреки това, изгледът става по-сложен, а „чупенето“ отново е проблем тъй като често има разминаване между обектния домейн модел и отдадения модел!
Когато изгледът стане по-сложен, обикновено е трудно да се запази обектният модел на домейна в синхрон с работата на изгледа. Ако разгледаме пак горепосочения пример, нека да предположим, че се изисква да се предостави на потребителя поле, в което той да може да добави мотоциклет, ако желае. Когато формата се публикува, контролерът трябва да прецени според стойностите, кой изглед да бъде показан следващ. Последното нещо, което трябва да се направи е да се добави това свойство на нашия домейн – модел, тъй като това е строго изискване.
Нека информацията за мотоциклета да е MotorcycleData:
public class MotorcycleData
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public string VIN { get; set; }
public bool AddAdditionalCycle { get; set; }
}
Този модел е доста трудоемък и изисква слой за превеждане, за да се очертаят връзките назад и напред между Motorcycle и MotorcycleData, но в случая усилията си струват дори за по-сложни изгледи. Този модел със сериозно застъпен и препоръчан от авторите на книгата MVC in Action.[3] Идеите са разширени и в публикациите на Jimmy Bogard (един от съавторите на книгата) – How we do MVC – View Models.[4]
В реалния живот, този модел може да е много бърз. Освен че трябва да се очертае връзката между Motorcycle и MotorcycleData, също така може да има номерирана колекция, която да бъде представена с падащо меню. Ако целият този код бъде подаден към контролера, той много бързо ще се бъде напълнен с много код, който се отнася само за изграждането на модел – изгледа, което не е желателно, защото контролерът трябва да не се препълва с излишна информация. Ето защо може да създаден отделен клас, чиято роля да бъде само изграждането на модел – изгледа:
public class MotorcycleViewModelBuilder
{
private IMotorcycleRepository motorcycleRepository;
public MotorcycleViewModelBuilder(IMotorcycleRepository repository)
{
this.motorcycleRepository = repository;
}
public MotorcycleViewModel Build()
{
// code here to fully build the view model
// with methods in the repository
}
}
Това позволява кодът на контролера да изглежда по следния начин:
public ActionResult Edit(int id)
{
var viewModelBuilder = new MotorcycleViewModelBuilder(this.motorcycleRepository);
var motorcycleViewModel = viewModelBuilder.Build();
return this.View();
}
Изгледът в този модел може да изглежда почити като изгледа в модел 2, но тук имаме предимството да знаем, че само преминаваме през данните, от които имаме нужда – нито повече, нито по-малко. Когато формата се публикува обратно, Save() методът в контролера ще изглежда по следния начин:
public ActionResult Save([Bind(Prefix ="Motorcycle")]MotorcycleData motorcycleData)
{
var mapper = new MotorcycleMapper(motorcycleData);
Motorcycle motorcycle = mapper.Map();
this.motorcycleRepository.Save(motorcycle);
return this.RedirectToAction("Index");
}
Тази имплементация е много подобна на модела описан от Jimmy Bogard, но без AutoMap. AutoMap ни позволява да запазваме част от кода извън контолера, което може да бъде много полезно.
В сложните реални ситуации, някой вариации на Модел 3 са най-добрия избор, тъй като този модел предоставя най-голяма гъвкавост и вариативност на разработчика.
Как определяме кой е най-подходящият модел в конкретна ситуация? Ето няколко съображения, които да имате предвид:
Модел 1 и модел 2 са подходящи за употреба при повторение на код. Тук трябва да има връзка между изгледа и обектния домейн модел. Това води до по-кратък код, като картографски слой не е задължителен. Обаче, ако изгледът се различава от домейн модела (което е често срещано) модел 1 и модел 2 са неефективни.
Често се получават несъответствия между нашия домейн модел и съображенията на изгледа. В такъв случай модел 3 е най-подходящ.
Ако е използван обичайния модел 3, трябва да сте сигурни, че е създаден модел за картографкси слой. Въпреки че това означава повече код, който трябва да се напише, това предоставя повече гъвкавост, а също така има и библиотеки, които правят написването му по-лесно.
Има много начини за валидация, като един от най-популярните е използването на библиотеки. Въпреки че типичните валидации (напр. задължителни полета), ще бъдат същите между домейн модела и изгледа, не всички валидации винаги ще съвпадат. Освен това, не винаги ще може да се контролира домейн модела (например в някой разработки домейн моделът е изложен чрез услуги, които разработчиците само използват). Ето защо има ограничения как валидациите да се свържат с тези класове. Възможно е да се използва отделен клас „мета данни“ за валидиране. Ето защо клас 3 предоставя абсолютен контрол над валидирането.
Дотук разгледаните модели бяха в контекста на ASP.NET MVC. Проблемът с това как най-добре да се справяме с изглед моделите са актуални и при други рамки, като например уеб формите. Ако е възможно да се свържете директно към домейн модела с обикновен клас, това е най-простото и лесно решение. Но когато сложността нараства, отделните модели предоставят разнообразие от възможности за действие.[5]
Контролерите са класове, които се създават в MVC приложението. Намират се в папка Controllers. Всеки един клас, който е от този тип, трябва да има име завършващо с наставка „Controller“. Контролерите обработват постъпващите заявки, въведени от потребителя и изпълняват подходящата логика за изпълнение на приложението. Класът контролер е отговорен за следните етапи на обработка:
За да създадем контролер натискаме дясното копче на мишката върху папката „controllers“ в прозореца „solution explorer“ на Visual Studio, избираме „add“ и след това „controller“.
След появяването на прозореца „Add controller“ трябва да се въведе име на контролера. То трябва да завършва с наставката „controller“.
Контролера е клас който се наследява от базовия клас System.Web.Mvc.Controller. Всеки публичен метод в контролера е показан като „controller action“. Ако искаме даден метод да не бъде извикан, трябва да сложим „NonAction“ атрибут върху неговото име. По подразбиране „Index()“ действието е извикано за контролера, когато друго такова не е изрично упоменато.
using System;
using System.Collection.Generic;
using System.Linq;
using System.Web;
using System.Web.MVC;
namespace ControllerApplication.Controllers
{
public class TestingController : Controller
{
//
// GET: /Testing/
public ActionResult Index() //Default Action
{
return View();
}
}
}
В ASP.NET приложения, които не ползват MVC Framework, взаимодействията с потребителя са организирани и контролирани чрез страници. За разлика от това в приложения с MVC framework взаимодействията с потребителя са организирани чрез контролери и методи за действие. Контролерът определя метода за действие и може да включва толкова методи за действие, колкото са необходими.
Методи за действие обикновено имат функции, които са пряко свързани с взаимодействието с потребителя. Примери за взаимодействие с потребителите са въвеждане на URL адрес в браузъра, кликване върху линк, и подаването на формуляр. Всяко едно от тези потребителски взаимодействия изпраща заявка към сървъра. Във всеки един от тези случаи, URL адреса от заявката съдържа информация, която MVC Framework използва за да включи метод за действие.[7]
using System;
using System.Collection.Generic;
using System.Linq;
using System.Web;
using System.Web.MVC;
namespace ControllerApplication.Controllers
{
public class TestingController : Controller
{
//
// GET: /Testing/
public ActionResult Index() //Default Action
{
return View();
}
public ActionResult Testing1()
{
return View();
}
public ActionResult Testing2(string x)
{
return View();
}
}
}
В посочения горе пример имаме три действия (Index, Testing1, Testing2), които са дефинирани в контролер клас „TestingController“. За да извикаме различни действия, трябва да напишем следното в полето за адреси:
Начин на изписване: /{controller}/{action}/{id}
Action Results
-actionName: Името на действието. -controllerName: Името на контролера. -routeValues: Стойностите които са предадени на действието.
Метода RedirectToAction() връща „302 found HTTP“ статус код на браузъра, за да може да направи пренасочването към новото действие. Едно от предимствата на това действие е, че когато се прави пренасочване в браузъра – полето за адреси се обновява с новия URL линк. Недостатък е, че трябва да се направи повторна заявка от браузъра към сървъра.
-string: Изобразява string на браузъра. -contentype: Типът MIME на данните (по подразбиране за text/html). -contentEncoding: Текстовото кодиране на данните (например:-Unicode или ASCII)
-filename, contentType, fileDownloadName, fileContents, fileStream.
Валидирането на входните данни, въведени от потребителя (user input), позволява да се уверим, че те съответстват на модела на данните в ASP.NET MVC. По този начин се защитава приложението от потребителски грешки или злонамерени потребители. Има много начини да се включи валидиране в едно MVC приложение. Например, може да бъде използвана публично достъпна библиотека за валидиране, като Microsoft Enterprise Library Validation Block.
Когато потребителят потвърди (submit) формуляр(form), данните от формуляра се предават на метод за действие (action method) с помощта на ViewDataDictionary колекция. ViewDataDictionary има ModelState свойство(property), което съдържа колекция от ModelState обекти. За всеки модел, който е дефиниран в MVC приложението, се създава съответния ModelState обект и добавя в колекцията. Действието, което получава данните от формуляра определя правилата за проверка, прилагани към формуляра с данни. Ако правилото е нарушено, действието използва ModelState свойство за да предаде информация за грешка обратно към изгледа.
Примерът показва клас Person, който дефинира свойства (properties) за съхраняване на лични данни:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zipcode { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
}
Следното дефинира форма за въвеждане на стойности и създаване на инстанция на класа Person. Ако потребителят въведе невалидна стойност за инстанцията на Person, изгледът се генерира отново, този път със съобщение за грешка, което е подадено на изгледа от свойство на ModelState:
<h2>Create</h2>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name") %> Required
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="Age">Age:</label>
<%= Html.TextBox("Age") %> Required
<%= Html.ValidationMessage("Age", "*") %>
</p>
<p>
<label for="Street">Street:</label>
<%= Html.TextBox("Street") %>
<%= Html.ValidationMessage("Street", "*") %>
</p>
<p>
<label for="City">City:</label>
<%= Html.TextBox("City") %>
<%= Html.ValidationMessage("City", "*") %>
</p>
<p>
<label for="State">State:</label>
<%= Html.TextBox("State") %>
<%= Html.ValidationMessage("State", "*") %>
</p>
<p>
<label for="Zipcode">Zipcode:</label>
<%= Html.TextBox("Zipcode") %>
<%= Html.ValidationMessage("Zipcode", "*") %>
</p>
<p>
<label for="Phone">Phone:</label>
<%= Html.TextBox("Phone") %> Required
<%= Html.ValidationMessage("Phone", "*") %>
</p>
<p>
<label for="Email">Email:</label>
<%= Html.TextBox("Email") %> Required
<%= Html.ValidationMessage("Email", "*") %>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
Ако възникне грешка при валидирането, методът за действие извиква метода AddModelError за да добави грешката в свързания обект ModelState. След като се изпълнят правилата за валидиране, методът за действие използва свойството IsValid на колекциата ModelStateDictionary за да се определи дали получената информация е в съответствие с модела:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Person person)
{
if (person.Name.Trim().Length == 0)
{
ModelState.AddModelError("Name", "Name is required.");
}
if (person.Age < 1 || person.Age > 200)
{
ModelState.AddModelError("Age", "Age must be within range 1 to 200.");
}
if ((person.Zipcode.Trim().Length > 0) && (!Regex.IsMatch(person.Zipcode, @"^\d{5}$|^\d{5}-\d{4}$")))
{
ModelState.AddModelError("Zipcode", "Zipcode is invalid.");
}
if (!Regex.IsMatch(person.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
{
ModelState.AddModelError("Phone", "Phone number is invalid.");
}
if (!Regex.IsMatch(person.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
{
ModelState.AddModelError("Email", "Email format is invalid.");
}
if (!ModelState.IsValid)
{
return View("Create", person);
}
people.Add(person);
return RedirectToAction("Index");
}
Изгледите са тези, които определят как ще бъде визуализиран потребителският интерфейс (UI) на приложението. В ASP.NET MVC се поддържат средства (engines) за генериране на изгледи.
Когато потребителт взаимодейства с изглед, данните се рутират от изгледа до метод за действие, който от своя страна може да създаде друг изглед. Едно MVC приложение може да има няколко контролери, всеки от които може да съдържа множество методи за действие, а всяко действие може да създаде различен изглед. Изгледите са организирани в папки, като името им се определя от това на свързания контролер (например изгледите от HomeController са в папката \Views\Home).
Рутирането е метод за валидация и обработка на заявката от потребителя. След като рутирането получи заявката, то проверява дали тази заявка отговаря на рутиращите шаблони и ако бъдат намерени съответствия, първият физически поставен в кода шаблон, отговарящ на дадената заявка, изпълнява функции за подаване на данните от заявката към съответните контролери, действия и параметри.
При стартиране на ASP.NET MVC приложението първият файл, който се стартира, се нарича „Global.asax“ и съответно в него първият метод, който се извиква, е с име „Application_start()“. В този метод има фунции за регистрация на Area, WebApi, Filters, Routing и Bundle. Именно тук се регистрира и нашето рутиране (Routing) чрез извикване на класа RoutingConfig, през метода RegisterRoutes и през него се регистрира рутирането в глобалната таблица RouteTable.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
За да дефинирате рутиранете е нужно да конфигурирате файла RouteConfig.cs, който се намира в папката App_Start заедно с останалите Config файлове.
Тук вече се конфигурира метода RegisterRoutes, който се извиква от „Global.asax“ файла. Този метод използва глобална таблица, която може да бъде извикана отвсякъде в нашето приложение и в нея има методи IgnoreRoute и MapRoute. IgnoreRoute методът се използва за да се пренебрегнат заявки, които не желаем да обработим. Чрез метода MapRoute ние регистрираме нашето рутиране като то си има име, url-шаблон и стойности по подразбиране.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
};
}
Шаблоните в рутирането са case-insensitive, което означава, че шаблонът ще разпознае данните без значение дали са с малки или главни букви. Ако при заявката никой от шаблоните не бъде обхванат, то тогава сървърът връща error-404.
ASP.NET MVC ни предоставя готов дебъгер който може да свалим през NuGet packege и в него потърсите RouteDebugger.
Дебъгерът се появява отдолу на страницата след нейното стартиране.
Ако желаете той да бъде спрян, е нужно да отворите Web.Config файла на вашето приложение и в него да промените този ред „<add key=“RouteDebugger:Enabled" value="true" />", като смените стойността value да е равна на false (<add key="RouteDebugger:Enabled" value="false" />).
<appSettings>
<add key="webpages:Version" value="1.0.0.0" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="RouteDebugger:Enabled" value="true" />
</appSettings>
За пръв път ASP.NET MVC е била представена през октомври 2007 г. от Скот Гътри на първата ALT.NET конференция в Остин, Тексас. Въпреки че Гътри заявява в своя блог,[11] че ASP.NET MVC ще бъде пусната в употреба като напълно поддържана функция на ASP.NET през първата половина на 2008, първата официална версия на продукта се появява на 13 март 2009 г.
През март 2012 г. Скот Гътри съобщава в блога[12] си, че Microsoft са пуснали версия на техния уеб пакет включващ ASP.NET MVC, Web API и ASP.NET Web Pages (още наричан Razor) под лиценза на Apache 2.0.
Дата | Версия |
---|---|
13 март 2009 | ASP.NET MVC 1.0[13] |
16 декември 2009 | ASP.NET MVC 2 RC[14] |
4 февруари 2010 | ASP.NET MVC 2 RC 2[15] |
10 март 2010 | ASP.NET MVC 2[16] |
6 октомври 2010 | ASP.NET MVC 3 Beta[17] |
9 ноември 2010 | ASP.NET MVC 3 RC[17] |
10 декември 2010 | ASP.NET MVC 3 RC 2[17] |
13 януари 2011 | ASP.NET MVC 3[17] |
13 септември 2011 | ASP.NET MVC 4 Developer Preview[18] |
14 февруари 2012 | ASP.NET MVC 4 Beta[19] |
31 май 2012 | ASP.NET MVC 4 RC[20] |
15 август 2012 | ASP.NET MVC 4[21] |
26 юни 2013 | ASP.NET MVC 5 Preview[22] |