ASP.NET MVC 4
Адам Фриман
Создание 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; }
}
}
Свойство 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
{
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;
}
}
}
}
Изменения в хранилище не сохраняются, так что при каждом запуске приложения в данных модель всегда будет три эталонных объекта.
Создаем контроллер 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>
}
<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 .
Рисунок 25-1: HTML, сгенерированный представлением Index.cshtml
Когда мы закончим работу над приложением, пользователю будет показана только секция Reservations
. Мы загрузим данные с сервера и заполним ими таблицу.
Секция Add New Reservation
содержит форму, которая использует ненавязчивый Ajax для отправки данных к серверу и создания новых объектов Reservation
в хранилище. В объекте AjaxOptions
, с помощью которого мы будем настраивать запрос Ajax, пока еще нет опций, но мы вернемся и создадим их, когда остальные части приложения будут готовы.
Секция Edit Reservation
позволит пользователю изменять существующий в хранилище объект Reservation
. В этой секции мы не использовали форму с ненавязчивым Ajax, потому что для разнообразия хотим использовать поддержку Ajax из библиотеки jQuery напрямую (что в любом случае делает библиотека ненавязчивого Ajax).