Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

.pdf
Скачиваний:
25
Добавлен:
19.03.2016
Размер:
17.66 Mб
Скачать

Web API

В этой главе мы опишем Web API – новую функцию платформы ASP.NET, позволяющую легко и быстро создавать веб-службы, которые предоставляют API для HTTP-клиентов (известные как Web API).

Функция Web API имеет ту же основу, что и обычные приложения MVC Framework, но не является частью MVC. Microsoft дублировала некоторые ключевые классы и характеристики, связанные с пространством имен System.Web.Mvc, в пространство имен System.Web.Http. Таким образом, Web API является частью ядра платформы ASP.NET и может использоваться в других типах вебприложений или работать как автономный движок веб-служб.

К счастью, нам уже хорошо знакомы основы Web API, потому что мы описали их в предыдущих главах. В этой главе мы покажем вам, как добавить Web API в обычный проект приложения MVC Framework. Этот процесс настолько прост, что большую часть времени мы затратим на создание кода JavaScript, с помощью которого будем использовать созданный API.

Невозможно недооценить то, как Web API упрощает создание веб-служб (это большой шаг вперед по сравнению с другими технологиями для работы с веб-службами Microsoft, которые появлялись за последнее десятилетие). Нам нравится Web API и мы рекомендуем вам использовать его в своих проектах, главным образом потому, что он очень прост и имеет ту же структуру, что и MVC Framework.

Понимание Web API

Чтобы использовать функцию Web API, необходимо добавить специальный контроллер в приложение MVC Framework. Этот контроллер - API Controller - имеет две отличительные характеристики:

1.Методы действий возвращают модель, а не объекты ActionResult.

2.Методы действий выбираются в зависимости от метода HTTP, который использовался в запросе.

Объекты моделей, созданные методами действий контроллера API, преобразовываются в формат JSON и отправляются клиенту. Контроллеры API предназначены для доставки веб-данных сервисов, так что они не поддерживают представления, макеты или любые другие функции, которые создают HTML для отображения в браузере. Контроллер API может поддерживать запросы от любого клиента, но чаще всего он используется для обслуживания запросов Ajax от веб-приложений; это мы и продемонстрируем в данной главе.

Как мы показали в главе 21, методы действий, которые возвращают данные JSON для поддержки Ajax, можно создавать и в обычных контроллерах, но контроллер API предлагает альтернативный подход. Он позволяет отделить действия, которые возвращают данные, от действий, которые возвращают представления, и, таким образом, облегчает и ускоряет создание Web API общего назначения.

Подсказка

Вас никто не обязывает использовать контроллеры API (как и практически все остальные функции MVC Framework). В реальных проектах мы используем техники из главы 21 наряду с контроллерами API. Как правило, мы используем контроллеры API,

631

когда необходимо создать много действий, которые возвращают JSON, или когда мы работаем над проектом, который не имеет компонента HTML и предоставляет только сервисы данных.

Чтобы объяснить, как работают контроллеры API, проще всего будет создать приложение MVC Framework, в котором будут использоваться такие контроллеры; это мы и сделаем позже в этой главе. Тем не менее, Web API - довольно простая функция, и опирается на многие возможности MVC, которые мы подробно рассмотрели в предыдущих главах, так что мы потратим большую часть времени на создание приложения, а в конце просто добавим к нему контроллер API.

С одной стороны, это даже хорошо. Это означает, что вы уже знаете все функции, на которые опирается контроллер API. Но пример все равно получается странным, потому что нам придется создать много стандартных компонентов приложения MVC, прежде чем мы перейдем к контроллеру API. К тому же, нам придется написать код JavaScript, который отправляет запросы Ajax. Для этого мы будем использовать jQuery, но не будем разбирать код подробно, а только объясним, что делает каждая секция кода и как она взаимодействует с веб-службой, которую мы создадим.

Создание Web API приложения

Как мы уже объяснили, приложение Web API - это обычное приложение MVC Framework, в котором есть специальный контроллер. В качестве примера мы создали новый проект MVC Framework под названием WebServices на шаблоне Basic. В следующих разделах мы будем добавлять все регулярные компоненты приложения MVC Framework - объекты моделей, хранилище, контроллеры и представления, а затем добавим контроллер API.

Подсказка

Существует шаблон WebAPI, который создает обычный проект MVC Framework и

добавляет в него пару типичных контроллеров. Как вы уже поняли, мы предпочитаем шаблоны Empty и Basic и считаем, что другие варианты просто

добавляют типичный код, который лучше не использовать.

Создаем модель и хранилище

В этом примере приложения мы создали особенно простую модель под названием Reservation, которую определили в файле Reservation.cs в папке Models. Определение этой модели показано в листинге 25-1 .

Листинг 25-1: Класс модели Reservation

using System.ComponentModel.DataAnnotations;

namespace WebServices.Models

{

public class Reservation

{

public int ReservationId { get; set; } public string ClientName { get; set; } public string Location { get; set; }

}

}

632

Свойство ReservationId однозначно идентифицирует каждый объект модели. Свойства ClientName и Location содержат значения данных модели. В этой главе нас не интересует значение этих свойств, потому что мы собираемся работать с Web API, так что мы выбрали нечто простое и универсальное.

В последних нескольких главах мы создавали приложения, в которых данные модели создавались внутри контроллера. Мы делали это потому, что были сосредоточены на других аспектах MVC Framework, не связанных с хранением данных модели. В этой главе мы собираемся создать интерфейс хранилища и его простую реализацию. На это стоит потратить время, потому что одна из главных характеристик контроллера API - это его простота, и мы хотим ее продемонстрировать.

Мы создали новый интерфейс в файле IReservationRepository.cs в папке Models, как показано в листинге 25-2.

Листинг 25-2: Интерфейс IReservationRepository

using System.Collections.Generic;

namespace WebServices.Models

{

public interface IReservationRepository

{

IEnumerable<Reservation> GetAll(); Reservation Get(int id); Reservation Add(Reservation item); void Remove(int id);

bool Update(Reservation item);

}

}

Это стандартный интерфейс хранилища; он определяет методы, которые позволяют извлекать (как индивидуально, так и в коллекции), добавлять, обновлять и удалять объекты моделей.

Мы также создали класс ReservationRepository, который определили в файле ReservationRepository.cs в папке Models. Этот класс реализует интерфейс IReservationRepository и определяет несколько объектов модели. Это не тот подход, который вы бы использовали в реальном приложении, но в данной главе он подходит для наших целей. Класс хранилища показан в листинге 25-3 .

Листинг 25-3: Класс ReservationRepository

using System;

using System.Collections.Generic; using System.Linq;

using System.Web;

namespace WebServices.Models

{

public class ReservationRepository : IReservationRepository

{

private List<Reservation> data = new List<Reservation>

{

new Reservation

{

ReservationId = 1, ClientName = "Adam", Location = "London"

},

new Reservation

{

633

ReservationId = 2, ClientName = "Steve", Location = "New York"

},

new Reservation

{

ReservationId = 3, ClientName = "Jacqui", Location = "Paris"

},

};

private static ReservationRepository repo = new ReservationRepository();

public static IReservationRepository getRepository()

{

return repo;

}

public IEnumerable<Reservation> GetAll()

{

return data;

}

public Reservation Get(int id)

{

var matches = data.Where(r => r.ReservationId == id); return matches.Count() > 0 ? matches.First() : null;

}

public Reservation Add(Reservation item)

{

item.ReservationId = data.Count + 1; data.Add(item);

return item;

}

public void Remove(int id)

{

Reservation item = Get(id); if (item != null)

{

data.Remove(item);

}

}

public bool Update(Reservation item)

{

Reservation storedItem = Get(item.ReservationId); if (storedItem != null)

{

storedItem.ClientName = item.ClientName; storedItem.Location = item.Location; return true;

}

else

{

return false;

}

}

}

}

634

Изменения в хранилище не сохраняются, так что при каждом запуске приложения в данных модель всегда будет три эталонных объекта.

Создаем контроллер Home

В проекте можно свободно смешивать обычные контроллеры и контроллеры API. На самом деле, вам придется это делать, если вы хотите обеспечить поддержку HTML-клиентов в приложении, потому что контроллеры API будут возвращать только данные объекта, а не представления. Чтобы начать использовать приложение, мы создали контроллер Home, метод действия Index которого будет визуализировать представление по умолчанию. Мы не передаем в представление объекты модели, потому что хотим реализовать веб-службу и получать все необходимые данные от контроллера API. Контроллер Home показан в листинге 25-4 .

Листинг 25-4: Контроллер Home в проекте WebServices

using System.Web.Mvc;

namespace WebServices.Controllers

{

public class HomeController : Controller

{

public ActionResult Index()

{

return View();

}

}

}

Создаем представление и CSS

Мы добавили несколько стилей CSS в файл /Content/Site.css для элементов HTML, которые будут визуализированы после вызова действия Index. Эти дополнения показаны в листинге 25-5.

Листинг 25-5: Дополнения в файле Site.css

table { margin: 10px 0;} th { text-align: left;}

.nameCol {width: 100px;}

.locationCol {width: 100px;}

.selectCol {width: 30px;}

.display { float: left; border: thin solid black; margin: 10px; padding: 10px;}

.display label {display: inline-block;width: 100px;}

Действие Index контроллера Home визуализирует представление /Views/Home/Index.cshtml, которое показано в листинге 25-6. Здесь мы не установили значения для свойства Layout, что означает, что будет использоваться макет по умолчанию. Следовательно, наши стили CSS и библиотеки jQuery (которые мы будем использовать позже) будут загружены браузером автоматически при визуализации представления. В секцию scripts мы добавили элемент script для библиотеки ненавязчивого Ajax (которую мы также будем использовать позже).

Листинг 25-6: Index.cshtml

@{

ViewBag.Title = "Index";

}

@section scripts {

<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>

635

}

<div id="summaryDisplay" class="display"> <h4>Reservations</h4>

<table>

<thead>

<tr>

<th class="selectCol"></th> <th class="nameCol">Name</th>

<th class="locationCol">Location</th> </tr>

</thead>

<tbody id="tableBody">

<tr><td colspan="3">The data is loading</td></tr> </tbody>

</table>

<div id="buttonContainer">

<button id="refresh">Refresh</button> <button id="add">Add</button> <button id="edit">Edit</button> <button id="delete">Delete</button>

</div>

</div>

<div id="addDisplay" class="display"> <h4>Add New Reservation</h4>

@{

AjaxOptions addAjaxOpts = new AjaxOptions

{

// options will go here };

}

@using (Ajax.BeginForm(addAjaxOpts))

{

@Html.Hidden("ReservationId", 0) <p><label>Name:</label>@Html.Editor("ClientName")</p> <p><label>Location:</label>@Html.Editor("Location")</p> <button type="submit">Submit</button>

}

</div>

<div id="editDisplay" class="display"> <h4>Edit Reservation</h4>

<form id="editForm">

<input id="editReservationId" type="hidden" name="ReservationId" /> <p><label>Name:</label><input id="editClientName" name="ClientName" /></p> <p><label>Location:</label><input id="editLocation" name="Location" /></p>

</form>

<button id="submitEdit" type="submit">Save</button> </div>

HTML, который генерирует это представление, разбит на три секции. Каждая секция определена в элементе div с классом display, и на данный момент пользователю отображаются все три (мы изменим видимость этих элементов, когда мы будем использовать код JavaScript далее в этой главе).

Чтобы увидеть, как HTML отображается в браузере, запустите приложение. Ни одна из кнопок не будет работать, и данные не будут отображаться, но вы можете получить представление о структуре и макете, как показано на рисунке 25-1 .

636

Рисунок 25-1: HTML, сгенерированный представлением Index.cshtml

Когда мы закончим работу над приложением, пользователю будет показана только секция Reservations. Мы загрузим данные с сервера и заполним ими таблицу.

Секция Add New Reservation содержит форму, которая использует ненавязчивый Ajax для отправки данных к серверу и создания новых объектов Reservation в хранилище. В объекте AjaxOptions, с помощью которого мы будем настраивать запрос Ajax, пока еще нет опций, но мы вернемся и создадим их, когда остальные части приложения будут готовы.

Секция Edit Reservation позволит пользователю изменять существующий в хранилище объект Reservation. В этой секции мы не использовали форму с ненавязчивым Ajax, потому что для разнообразия хотим использовать поддержку Ajax из библиотеки jQuery напрямую (что в любом случае делает библиотека ненавязчивого Ajax).

Создание API контроллера

Теперь мы готовы определить Web API, который будем использовать в коде JavaScript для взаимодействия с содержимым хранилища. В этом разделе мы добавим в проект API Controller и объясним, как он работает.

Для создания контроллера кликните правой кнопкой мыши по папке Controllers в окне Solution Explorer и выберите Add - Controller из контекстного меню. В поле Controller name введите

637

ReservationController и выберите Empty API controller из выпадающего списка Template, как показано на рисунке 25-2.

Рисунок 25-2: Добавляем новый контроллер API в проект

Опция Empty API controller создает контроллер API без методов действий. В выпадающем списке доступны несколько других опций, которые будут создавать контроллеры API с шаблонными методами действий, которые можно заполнить. Но мы предпочитаем начинать с пустого класса и определять функции, которые нам нужны. Начальный класс контроллера, созданный Visual Studio, показан в листинге 25-7.

Листинг 25-7: Контроллер, который создает опция Empty API Controller

using System;

using System.Collections.Generic; using System.Linq;

using System.Net; using System.Net.Http; using System.Web.Http;

namespace WebServices.Controllers

{

public class ReservationController : ApiController

{

}

}

Мы выделили базовый класс контроллера Reservation, потому что в нем заметны отличия от обычного контроллера. System.Web.Http.ApiController реализует интерфейс IController,

описанный в главе 15, но совершенно иначе обрабатывает запросы, чем обычный класс System.Web.Mvc.Controller, который был основой для всех других примеров в этой книге (за исключением того, где мы создали собственную реализацию IController). В листинге 25-8

638

показано, как мы добавили в контроллер Reservation хранилище и определили методы действий Web API, которые потребуются для доступа к объектам Reservation.

Листинг 25-8: Завершаем контроллер API Reservation

using System.Collections.Generic; using System.Web.Http;

using WebServices.Models;

namespace WebServices.Controllers

{

public class ReservationController : ApiController

{

private IReservationRepository repo = ReservationRepository.getRepository();

public IEnumerable<Reservation> GetAllReservations()

{

return repo.GetAll();

}

public Reservation GetReservation(int id)

{

return repo.Get(id);

}

public Reservation PostReservation(Reservation item)

{

return repo.Add(item);

}

public bool PutReservation(Reservation item)

{

return repo.Update(item);

}

public void DeleteReservation(int id)

{

repo.Remove(id);

}

}

}

Это все, что требуется для создания Web API. Мы создали экземпляр нашего класса хранилища и пять методов действий, которые обеспечивают доступ к объектам модели Reservation, которые содержаться в хранилище.

Подсказка

Обратите внимание, что для получения IReservationRepository мы вызываем статический метод ReservationRepository.getRepository. Для обслуживания

запросов к API создаются новые экземпляры контроллеров, и, если бы нам нужно было создать новый объект ReservationRepository в самом контроллере, наши

изменения никак не отразились бы на данных, отправляемых клиенту. Это особенность нашего хранилища «в памяти» - такого не будет происходить при работе с реальными хранилищами, которые сохраняют данные в базах.

639

Тестируем контроллер API

Мы вскоре расскажем, как работает контроллер API, но перед этим выполним простой тест. Запустите приложение. Когда браузер загрузит корневой URL проекта, перейдите по ссылке /api/reservation. Результат, который вы увидите, будет зависеть от вашего браузера. Если вы используете Internet Explorer 10, то вам будет предложено сохранить или открыть файл, который содержит следующие данные JSON:

[{"ReservationId":1,"ClientName":"Adam","Location":"London"}, {"ReservationId":2,"ClientName":"Steve","Location":"New York"}, {"ReservationId":3,"ClientName":"Jacqui","Location":"Paris"}]

Если перейти по той же ссылке в другом браузере, например, Google Chrome или Mozilla Firefox, то браузер отобразит следующие данные XML:

<ArrayOfReservation>

<Reservation>

<ClientName>Adam</ClientName>

<Location>London</Location>

<ReservationId>1</ReservationId>

</Reservation>

<Reservation>

<ClientName>Steve</ClientName> <Location>New York</Location> <ReservationId>2</ReservationId>

</Reservation>

<Reservation>

<ClientName>Jacqui</ClientName>

<Location>Paris</Location>

<ReservationId>3</ReservationId>

</Reservation>

</ArrayOfReservation>

Здесь можно отметить несколько интересных моментов. Во-первых, наш запрос к ссылке /api/reservation вернул список всех наших объектов модели и их свойств, из чего мы делаем вывод, что был вызван метод действия GetAllReservations в контроллере Reservation.

Во-вторых, разные браузеры получили разные форматы данных. Если вы сами попробуете перейти по ссылке из разных браузеров, то получите разные результаты, потому что более поздние версии браузеров могут иначе отправлять запросы; в любом случае вы увидите, что один запрос вернул JSON, другой - XML. (Вы также можете понять, почему JSON в значительной степени заменил XML в веб-службах. XML использует более подробную разметку и его труднее обрабатывать, особенно если вы используете JavaScript).

Различные форматы данных используются потому, что Web API использует заголовок HTTP Accept, содержащийся в запросе, чтобы узнать, с каким типом данных клиент предпочел бы работать. Internet Explorer получает JSON, потому что он посылает следующий заголовок:

Accept: text/html, application/xhtml+xml, */*

Браузер указал, что он прежде всего он предпочитает работать с форматом text/html, далее - application/xhtml+xml. В конце заголовка указано */*, что означает, что браузер примет любой тип данных, если первые два недоступны.

Web API поддерживает JSON и XML, но отдает предпочтение JSON, который и использует в ответе, опираясь на часть заголовка Accept */*. Вот заголовок Accept, который отправляет Google Chrome:

640

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]