Темы лекции: ASP.NET. MVC. Часть 3.
Практическое задание: ASP.NET. MVC.
Тренер: Игорь Шкулипа, к.т.н.
Разработка Веб-приложений на платформе
Microsoft .NET Framework.
Занятие 11
http://www.slideshare.net/IgorShkulipa 2
Web API
Одним из преимуществ стека технологий на платформе .NET является возможность
создания сервисов.
Мы можем использовать технологию WCF для создания веб-служб.
Но с MVC 4 и вообще всей платформы .NET 4.5 в нашем распоряжении оказался еще
один инструмент для создания веб-служб - Web API.
Концепция Web API (которую кстати можно использовать не только в MVC, но и в
веб-формах) - новый подход к реализации веб-приложений.
http://www.slideshare.net/IgorShkulipa 3
Создание проекта Web API
Среда создаст обычный mvc-проект со стандартной структурой. По
умолчанию, будет создано два контроллера. Один из них стандартный
контроллер HomeController. Второй контроллер - ValuesController,
который и реализует функционал Web API.
http://www.slideshare.net/IgorShkulipa 4
Контроллер Web API
Определение контроллера Web API отличается от обычного контроллера.
Во-первых, он образован от класса ApiController, который не связан с
базовым классом обычных контроллеров - Controller
Во-вторых, контроллеры Web API применяют стиль REST (Representation
State Transfer или "передача состояния представления") является
одним из основных пунктов технологии Web API.
Для взаимодействия с сервером в REST-архитектуре используются методы
HTTP:
• GET
• POST
• PUT
• DELETE
Здесь нет обычных методов действий, как в традиционных контроллерах,
которые возвращают ActionResult. А определенные в контроллере
ValuesContoller методы сопоставляются с одноименными методами
HTTP.
http://www.slideshare.net/IgorShkulipa 5
Маршрутизация в Web API
Поскольку в Web API методы контроллера не являются прямыми
ресурсами и сопоставляются с методами HTTP, то и весь механизм
маршрутизации действует не как при определении обычных
маршрутов.
Все определения маршрутов для Web API находятся в файле
WebApiConfig.cs (в папке App_Start)
http://www.slideshare.net/IgorShkulipa 6
Условности при именовании методов
При создании методов контроллера Web API действует некоторые
условности.
Имена методов по умолчанию должны начинаться с имени
предназначенного для них метода HTTP. В случае с контроллером по
умолчанию все просто: все методы действий носят названия методов
HTTP.
Однако мы можем использовать и любые другие имена без префиксов, но
в этом случае нам надо будет явно указать метод HTTP в виде атрибута
[HttpPost]
public void CreateItem([FromBody]string value) {
}
[HttpPut]
public void EditItem(int id, [FromBody]string value) {
}
[HttpDelete]
public void RemoveItem(int id) {
}
http://www.slideshare.net/IgorShkulipa 7
Добавление контроллера Web API в существующий
проект
http://www.slideshare.net/IgorShkulipa 8
Контроллер Web API
http://www.slideshare.net/IgorShkulipa 9
Результат
http://www.slideshare.net/IgorShkulipa 10
Мобильные приложения на ASP.NET MVC 4
Можно дать несколько рекомендаций общего характера, которые могут помочь при
создании мобильного веб-приложения:
• Старайтесь избегать фиксированных размеров, отступов, границ, чтобы
общая верстка была в духе так называемого "резинового дизайна"
• Старайтесь не использовать таблицы. Либо желательно, чтобы таблицы
имели как можно меньше столбцов (а в идеале вообще один столбец), а их
содержимое было кратким. Рекомендуется использовать списки вместо
таблиц.
• Старайтесь использовать небольшие формы с минимальным количеством
полей
• Избегайте использования блоков с прокруткой, как горизонтальной, так и
вертикальной, поскольку многие устройства испытывают проблемы с
рендерингом прокрутки
• Оптимизируйте страницы веб-сайта: сжимайте изображения, оптимизируйте
верстку и контент. Избегайте тяжеловесных веб-страниц. Ведь скорость
передачи в мобильных сетях в большинстве случаев далека от скорости,
например, при проводном подключении. А аппаратные возможности
мобильных аппаратов зачастую отстают от возможностей ПК, что будет
влиять на скорость обработки веб-страницы.
http://www.slideshare.net/IgorShkulipa 11
Создание мобильного приложения MVC
http://www.slideshare.net/IgorShkulipa 12
Метатег Viewport
Очевидно, что размеры экрана
мобильных устройств ставят перед
нами одну из наиболее важных
проблем при создании сайта для
мобильных устройств. В данном
случае избежать этой проблемы нам
поможет тег Viewport.
<meta name="viewport“
content="параметры_метатега">
http://www.slideshare.net/IgorShkulipa 13
Параметры Viewport
Параметр Значения Описание
width
Принимает целочисленное
значение в пикселях или
значение device-width
Устанавливает ширину области
viewport
height
Принимает целочисленное
значение в пикселях или
значение device-height
Устанавливает высоту области
viewport
initial-scale Число с плавающей точкой
от 0.1 и выше
Задает коэффициент
масштабирования начального
размера viewport. Значение 1.0
задает отсутствие
масштабирования
user-scalable no/yes
Указывает, может ли пользователь
с помощью жестов масштабировать
страницу
minimum-scale Число с плавающей точкой
от 0.1 и выше
Задает минимальный масштаб
размера viewport. Значение 1.0
задает отсутствие
масштабирования
maximum-scale Число с плавающей точкой
от 0.1 и выше
Задает максимальный масштаб
размера viewport. Значение 1.0
задает отсутствие
масштабирования
http://www.slideshare.net/IgorShkulipa 14
Режимы отображения DisplayMode
Инфраструктура ASP.NET MVC 4 содержит такую функциональность как
режимы отображения или DisplayMode.
Например, у нас есть некое представление Index.cshtml. Оно было
предназначено для десктопов. И мы хотим рядом с ним сделать копию
представления, но только уже для мобильных устройств. Для этого нам
достаточно создать в проекте копию данного представления, добавив
к его имени суффикс Mobile. То есть представление, которое
ориентировано на мобильные устройства, у нас будет называться
Index.Mobile.cshtml.
И больше нам ничего не надо менять: контроллеры и их действия
остаются теми же. Но теперь этот суффикс Mobile будет показывать
фреймворку, что представление Index.Mobile.cshtml надо
использовать в ответ на запросы, исходящие от мобильных устройств.
Во всех остальных случаях будет использоваться обычное
представление Index.cshtml.
http://www.slideshare.net/IgorShkulipa 15
Переопределение режима DisplayMode
Благодаря суффиксу Mobile фреймворк узнает о том, что страница предназначена
для мобильных устройств. Однако этим работа функциональности DisplayMode
не исчерпывается. Так, мы можем переопределить ее действие.
К примеру ряд браузеров и устройств могут не поддерживать какие-то
определенные теги, стили, вследствие этого страницы могут отображаться
некорректно.
Чтобы определить свой режим отображения, надо зарегистрировать в файле
Global.asax в методе Application_Start новый режим отображения:
using System.Web.WebPages;
...
protected void Application_Start() {
DisplayModeProvider.Instance.Modes.Insert(0,
new DefaultDisplayMode("IE8") {
ContextCondition =
(context => context.Request.UserAgent.Contains("MSIE 8"))
});...}
Здесь мы смотрим, содержит ли свойство UserAgent у объекта Request "MSIE 8" - то
есть был ли запрос послан браузером IE 8. В этом случае мы используем новый
режим DefaultDisplayMode("IE8").
Параметр "IE8" означает, что наши специфичные представления будут иметь
названия типа [имя_представления].IE8.cshtml. Собственно также, как и с
мобильными приложениями.
http://www.slideshare.net/IgorShkulipa 16
Мультиязычный сайт и локализация
См. Презентацию №8.
Для использования ресурсов, надо установить
следующие параметры для каждого ресурса:
• Build Action - в качестве типа построения нужно
выбрать значение Embedded Resource
• Custom Tool - в качестве инструмента создания
ресурсов - PublicResXFileCodeGenerator
• Custom Tool Namespace - в качестве
пространства имен укажем Resources. В данном
случае важно указать именно то пространство
имен, которое мы собираемся использовать.
Установка культуры в коде приложения производится следующим образом:
string cultureName=“ru”;
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture(cultureName);
Thread.CurrentThread.CurrentUICulture =
CultureInfo.CreateSpecificCulture(cultureName);
http://www.slideshare.net/IgorShkulipa 17
Эмуляторы для тестирования мобильных сайтов
http://www.mobilexweb.com/emulators
http://www.slideshare.net/IgorShkulipa 18
Эмулятор Firefox Mobile
http://www.slideshare.net/IgorShkulipa 19
Firefox Mobile
http://www.slideshare.net/IgorShkulipa 20
Пример. Ресурсы
http://www.slideshare.net/IgorShkulipa 21
Пример. Использование ресурсов в модели
public class Position: TEntity
{
[Required]
[Display(ResourceType = typeof(Resources.Strings), Name = "PositionDisplayText")]
[Editable(true)]
[MaxLength(200, ErrorMessageResourceType = typeof(Resources.Strings),
ErrorMessageResourceName = "MaxLengthErrorMessage")]
public string Name { get; set; }
}
[Serializable]
public class Persone : TEntity
{
[Required]
[Display(ResourceType = typeof(Resources.Strings), Name = "SurnameDisplayText")]
[MaxLength(50, ErrorMessageResourceType = typeof(Resources.Strings),
ErrorMessageResourceName = "MaxLengthErrorMessage")]
public string Surname { get; set; }
[Display(ResourceType = typeof(Resources.Strings), Name = "NameDisplayText")]
[MaxLength(50, ErrorMessageResourceType = typeof(Resources.Strings),
ErrorMessageResourceName = "MaxLengthErrorMessage")]
public string Name { get; set; }
[Display(ResourceType = typeof(Resources.Strings), Name = "MiddleNameDisplayText")]
[MaxLength(50, ErrorMessageResourceType = typeof(Resources.Strings),
ErrorMessageResourceName = "MaxLengthErrorMessage")]
public string Middle { get; set; }
...
http://www.slideshare.net/IgorShkulipa 22
Web API контроллер на основном сайте
public class WebAPIController : ApiController
{
static TeamContext model = new TeamContext();
static IRepository<Persone> persons =
new Repository<Persone>(model);
static IRepository<Position> positions =
new Repository<Position>(model);
// GET api/webapi
public IEnumerable<string> Get() {
List<Persone> pl =
persons.SelectAll().ToList<Persone>();
var result = new List<string>();
foreach (var p in pl) {
XmlSerializer xs = new XmlSerializer(p.GetType());
StringWriter sw = new StringWriter();
xs.Serialize(sw, p);
result.Add(sw.ToString());
}
return result;
}
...
http://www.slideshare.net/IgorShkulipa 23
Контроллер на мобильном сайте
public class HomeController : Controller
{
public ActionResult Index()
{
HttpClient hc = new HttpClient();
HttpResponseMessage hr =
hc.GetAsync(
new Uri("http://localhost:57553/api/WebAPI")).Result;
while (!hr.IsSuccessStatusCode) {
// Можно сделать что-то полезное, пока выполняется запрос
}
ViewBag.APIResult =
hr.Content.ReadAsStringAsync().Result;
return View();
}
...
http://www.slideshare.net/IgorShkulipa 24
Представление на мобильном сайте
@{
ViewBag.Title = "Home Page";
}
<h2>@ViewBag.APIResult</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>
<ul data-role="listview" data-inset="true">
<li data-role="list-divider">Navigation</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>
http://www.slideshare.net/IgorShkulipa 25
Layout на мобильном сайте
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<meta name="viewport" content="width=device-width" />
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
@Styles.Render("~/Content/mobileCss", "~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div data-role="page" data-theme="b">
<div data-role="header">
@if (IsSectionDefined("Header")) {
@RenderSection("Header")
} else {
<h1>@ViewBag.Title</h1>
@Html.Partial("_LoginPartial")
}
</div>
<div data-role="content">
@RenderBody()
</div>
</div>
...
http://www.slideshare.net/IgorShkulipa 26
Результат. Основной сайт
http://www.slideshare.net/IgorShkulipa 27
Результат. Мобильный сайт
http://www.slideshare.net/IgorShkulipa 28
Лабораторная работа №11
К лабораторной работе №7 добавить использование ресурсов
приложения, локализацию и мобильную версию сайта. А так же
предоставить Web API.

C# Web. Занятие 11.

  • 1.
    Темы лекции: ASP.NET.MVC. Часть 3. Практическое задание: ASP.NET. MVC. Тренер: Игорь Шкулипа, к.т.н. Разработка Веб-приложений на платформе Microsoft .NET Framework. Занятие 11
  • 2.
    http://www.slideshare.net/IgorShkulipa 2 Web API Однимиз преимуществ стека технологий на платформе .NET является возможность создания сервисов. Мы можем использовать технологию WCF для создания веб-служб. Но с MVC 4 и вообще всей платформы .NET 4.5 в нашем распоряжении оказался еще один инструмент для создания веб-служб - Web API. Концепция Web API (которую кстати можно использовать не только в MVC, но и в веб-формах) - новый подход к реализации веб-приложений.
  • 3.
    http://www.slideshare.net/IgorShkulipa 3 Создание проектаWeb API Среда создаст обычный mvc-проект со стандартной структурой. По умолчанию, будет создано два контроллера. Один из них стандартный контроллер HomeController. Второй контроллер - ValuesController, который и реализует функционал Web API.
  • 4.
    http://www.slideshare.net/IgorShkulipa 4 Контроллер WebAPI Определение контроллера Web API отличается от обычного контроллера. Во-первых, он образован от класса ApiController, который не связан с базовым классом обычных контроллеров - Controller Во-вторых, контроллеры Web API применяют стиль REST (Representation State Transfer или "передача состояния представления") является одним из основных пунктов технологии Web API. Для взаимодействия с сервером в REST-архитектуре используются методы HTTP: • GET • POST • PUT • DELETE Здесь нет обычных методов действий, как в традиционных контроллерах, которые возвращают ActionResult. А определенные в контроллере ValuesContoller методы сопоставляются с одноименными методами HTTP.
  • 5.
    http://www.slideshare.net/IgorShkulipa 5 Маршрутизация вWeb API Поскольку в Web API методы контроллера не являются прямыми ресурсами и сопоставляются с методами HTTP, то и весь механизм маршрутизации действует не как при определении обычных маршрутов. Все определения маршрутов для Web API находятся в файле WebApiConfig.cs (в папке App_Start)
  • 6.
    http://www.slideshare.net/IgorShkulipa 6 Условности приименовании методов При создании методов контроллера Web API действует некоторые условности. Имена методов по умолчанию должны начинаться с имени предназначенного для них метода HTTP. В случае с контроллером по умолчанию все просто: все методы действий носят названия методов HTTP. Однако мы можем использовать и любые другие имена без префиксов, но в этом случае нам надо будет явно указать метод HTTP в виде атрибута [HttpPost] public void CreateItem([FromBody]string value) { } [HttpPut] public void EditItem(int id, [FromBody]string value) { } [HttpDelete] public void RemoveItem(int id) { }
  • 7.
  • 8.
  • 9.
  • 10.
    http://www.slideshare.net/IgorShkulipa 10 Мобильные приложенияна ASP.NET MVC 4 Можно дать несколько рекомендаций общего характера, которые могут помочь при создании мобильного веб-приложения: • Старайтесь избегать фиксированных размеров, отступов, границ, чтобы общая верстка была в духе так называемого "резинового дизайна" • Старайтесь не использовать таблицы. Либо желательно, чтобы таблицы имели как можно меньше столбцов (а в идеале вообще один столбец), а их содержимое было кратким. Рекомендуется использовать списки вместо таблиц. • Старайтесь использовать небольшие формы с минимальным количеством полей • Избегайте использования блоков с прокруткой, как горизонтальной, так и вертикальной, поскольку многие устройства испытывают проблемы с рендерингом прокрутки • Оптимизируйте страницы веб-сайта: сжимайте изображения, оптимизируйте верстку и контент. Избегайте тяжеловесных веб-страниц. Ведь скорость передачи в мобильных сетях в большинстве случаев далека от скорости, например, при проводном подключении. А аппаратные возможности мобильных аппаратов зачастую отстают от возможностей ПК, что будет влиять на скорость обработки веб-страницы.
  • 11.
  • 12.
    http://www.slideshare.net/IgorShkulipa 12 Метатег Viewport Очевидно,что размеры экрана мобильных устройств ставят перед нами одну из наиболее важных проблем при создании сайта для мобильных устройств. В данном случае избежать этой проблемы нам поможет тег Viewport. <meta name="viewport“ content="параметры_метатега">
  • 13.
    http://www.slideshare.net/IgorShkulipa 13 Параметры Viewport ПараметрЗначения Описание width Принимает целочисленное значение в пикселях или значение device-width Устанавливает ширину области viewport height Принимает целочисленное значение в пикселях или значение device-height Устанавливает высоту области viewport initial-scale Число с плавающей точкой от 0.1 и выше Задает коэффициент масштабирования начального размера viewport. Значение 1.0 задает отсутствие масштабирования user-scalable no/yes Указывает, может ли пользователь с помощью жестов масштабировать страницу minimum-scale Число с плавающей точкой от 0.1 и выше Задает минимальный масштаб размера viewport. Значение 1.0 задает отсутствие масштабирования maximum-scale Число с плавающей точкой от 0.1 и выше Задает максимальный масштаб размера viewport. Значение 1.0 задает отсутствие масштабирования
  • 14.
    http://www.slideshare.net/IgorShkulipa 14 Режимы отображенияDisplayMode Инфраструктура ASP.NET MVC 4 содержит такую функциональность как режимы отображения или DisplayMode. Например, у нас есть некое представление Index.cshtml. Оно было предназначено для десктопов. И мы хотим рядом с ним сделать копию представления, но только уже для мобильных устройств. Для этого нам достаточно создать в проекте копию данного представления, добавив к его имени суффикс Mobile. То есть представление, которое ориентировано на мобильные устройства, у нас будет называться Index.Mobile.cshtml. И больше нам ничего не надо менять: контроллеры и их действия остаются теми же. Но теперь этот суффикс Mobile будет показывать фреймворку, что представление Index.Mobile.cshtml надо использовать в ответ на запросы, исходящие от мобильных устройств. Во всех остальных случаях будет использоваться обычное представление Index.cshtml.
  • 15.
    http://www.slideshare.net/IgorShkulipa 15 Переопределение режимаDisplayMode Благодаря суффиксу Mobile фреймворк узнает о том, что страница предназначена для мобильных устройств. Однако этим работа функциональности DisplayMode не исчерпывается. Так, мы можем переопределить ее действие. К примеру ряд браузеров и устройств могут не поддерживать какие-то определенные теги, стили, вследствие этого страницы могут отображаться некорректно. Чтобы определить свой режим отображения, надо зарегистрировать в файле Global.asax в методе Application_Start новый режим отображения: using System.Web.WebPages; ... protected void Application_Start() { DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("IE8") { ContextCondition = (context => context.Request.UserAgent.Contains("MSIE 8")) });...} Здесь мы смотрим, содержит ли свойство UserAgent у объекта Request "MSIE 8" - то есть был ли запрос послан браузером IE 8. В этом случае мы используем новый режим DefaultDisplayMode("IE8"). Параметр "IE8" означает, что наши специфичные представления будут иметь названия типа [имя_представления].IE8.cshtml. Собственно также, как и с мобильными приложениями.
  • 16.
    http://www.slideshare.net/IgorShkulipa 16 Мультиязычный сайти локализация См. Презентацию №8. Для использования ресурсов, надо установить следующие параметры для каждого ресурса: • Build Action - в качестве типа построения нужно выбрать значение Embedded Resource • Custom Tool - в качестве инструмента создания ресурсов - PublicResXFileCodeGenerator • Custom Tool Namespace - в качестве пространства имен укажем Resources. В данном случае важно указать именно то пространство имен, которое мы собираемся использовать. Установка культуры в коде приложения производится следующим образом: string cultureName=“ru”; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName); Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName);
  • 17.
    http://www.slideshare.net/IgorShkulipa 17 Эмуляторы длятестирования мобильных сайтов http://www.mobilexweb.com/emulators
  • 18.
  • 19.
  • 20.
  • 21.
    http://www.slideshare.net/IgorShkulipa 21 Пример. Использованиересурсов в модели public class Position: TEntity { [Required] [Display(ResourceType = typeof(Resources.Strings), Name = "PositionDisplayText")] [Editable(true)] [MaxLength(200, ErrorMessageResourceType = typeof(Resources.Strings), ErrorMessageResourceName = "MaxLengthErrorMessage")] public string Name { get; set; } } [Serializable] public class Persone : TEntity { [Required] [Display(ResourceType = typeof(Resources.Strings), Name = "SurnameDisplayText")] [MaxLength(50, ErrorMessageResourceType = typeof(Resources.Strings), ErrorMessageResourceName = "MaxLengthErrorMessage")] public string Surname { get; set; } [Display(ResourceType = typeof(Resources.Strings), Name = "NameDisplayText")] [MaxLength(50, ErrorMessageResourceType = typeof(Resources.Strings), ErrorMessageResourceName = "MaxLengthErrorMessage")] public string Name { get; set; } [Display(ResourceType = typeof(Resources.Strings), Name = "MiddleNameDisplayText")] [MaxLength(50, ErrorMessageResourceType = typeof(Resources.Strings), ErrorMessageResourceName = "MaxLengthErrorMessage")] public string Middle { get; set; } ...
  • 22.
    http://www.slideshare.net/IgorShkulipa 22 Web APIконтроллер на основном сайте public class WebAPIController : ApiController { static TeamContext model = new TeamContext(); static IRepository<Persone> persons = new Repository<Persone>(model); static IRepository<Position> positions = new Repository<Position>(model); // GET api/webapi public IEnumerable<string> Get() { List<Persone> pl = persons.SelectAll().ToList<Persone>(); var result = new List<string>(); foreach (var p in pl) { XmlSerializer xs = new XmlSerializer(p.GetType()); StringWriter sw = new StringWriter(); xs.Serialize(sw, p); result.Add(sw.ToString()); } return result; } ...
  • 23.
    http://www.slideshare.net/IgorShkulipa 23 Контроллер намобильном сайте public class HomeController : Controller { public ActionResult Index() { HttpClient hc = new HttpClient(); HttpResponseMessage hr = hc.GetAsync( new Uri("http://localhost:57553/api/WebAPI")).Result; while (!hr.IsSuccessStatusCode) { // Можно сделать что-то полезное, пока выполняется запрос } ViewBag.APIResult = hr.Content.ReadAsStringAsync().Result; return View(); } ...
  • 24.
    http://www.slideshare.net/IgorShkulipa 24 Представление намобильном сайте @{ ViewBag.Title = "Home Page"; } <h2>@ViewBag.APIResult</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> <ul data-role="listview" data-inset="true"> <li data-role="list-divider">Navigation</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> </ul>
  • 25.
    http://www.slideshare.net/IgorShkulipa 25 Layout намобильном сайте <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>@ViewBag.Title</title> <meta name="viewport" content="width=device-width" /> <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" /> @Styles.Render("~/Content/mobileCss", "~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> <div data-role="page" data-theme="b"> <div data-role="header"> @if (IsSectionDefined("Header")) { @RenderSection("Header") } else { <h1>@ViewBag.Title</h1> @Html.Partial("_LoginPartial") } </div> <div data-role="content"> @RenderBody() </div> </div> ...
  • 26.
  • 27.
  • 28.
    http://www.slideshare.net/IgorShkulipa 28 Лабораторная работа№11 К лабораторной работе №7 добавить использование ресурсов приложения, локализацию и мобильную версию сайта. А так же предоставить Web API.