Обмен данными сложной структуры с использованием ASP.NET Web API

Предпосылкой для написания этой статьи стал комментарий к статье «Использование AJAX в ASP.NET MVC», который был оставлен 26 февраля 2018 года одним из гостей сайта.

В комментарии говорилось, что в статье не хватает примера обмена данными сложной структуры.

С одной стороны обмен такими данными в принципе ничем не отличается от того, что было показано в статье. Но, с другой стороны, подвох всегда кроется в деталях. Поэтому я решил осветить эту тему подробно.

Введение

Для начала давайте выясним, что собой представляю те данные сложной структуры, о которых пойдёт речь в этой статье.

Дело в том, что при помощи HTTP запросов можно передавать не только отдельно взятые параметры (как это делалось в статье по ссылке), но также их массивы и сложные объекты.

В чистом виде ASP.NET MVC и Web Forms не способны эффективно обрабатывать подобные HTTP запросы. Поэтому для решения подобных задач следует воспользоваться платформой ASP.NET Web API.

Эта платформа, разрабатываемая Microsoft, позволяет легко построить web сервис на основе HTTP, который будет осуществлять обмен такими данными и может работать совместно с любым клиентским приложением, которое поддерживает HTTP, включая те же web приложения с использованием AJAX.

Первый проект ASP.NET Web API

Создадим проект ASP.NET Web API с помощью стандартного диалогового окна Visual Studio.

Обратите внимание, что в шаблоне проекта по умолчанию уже подключены как Web API, так и ASP.NET MVC. Это на самом деле не случайно.

Дело в том, что в основе платформы Web API лежит паттерн MVC. Поэтому реализация даже шаблонного проекта опирается на соответствующую платформу.

Ниже приведён скриншот со структурой стандартного шаблона WebAPI проекта. На первый взгляд, это самый обычный ASP.NET MVC проект, но отличия заключаются не в структуре (хотя она и включает по умолчанию, так называемую, страницу помощи (папка Areas\HelpPage), которую можно спокойно удалить), а во внутреннем содержании.

Одно из основных отличий состоит в том, что API запросы обрабатываются контроллерами, которые являются наследниками базового класса ApiController. Этот класс позволяет контроллеру обрабатывать не только GET и POST, но и другие типы HTTP запросов. Что позволяет разрабатывать Web API приложения в стиле RESTful.

Предлагаемый Visual Studio, шаблон контроллера по умолчанию уже содержит набор методов для поддержки GET, POST, PUT и DELETE запросов и ниже показан пример такого контроллера.

public class ValuesController : ApiController
{
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
    public string Get(int id)
    {
        return "value";
    }
    public void Post([FromBody]string value)
    {
    }
    public void Put(int id, [FromBody]string value)
    {
    }
    public void Delete(int id)
    {
    }
}

Имена открытых методов контроллера соответствуют виду HTTP запросов, который они обрабатывают. Атрибут параметра FromBody означает, что значение данного параметра следует получать из тела запроса.

Мы не станем рассматривать работу Web API на примере шаблонного контроллера, т. к. его даже при всём обилии методов его работа слишком проста и строится на обмене данными примитивных и строкового типов.

Вместо этого мы создадим сложный объект для обмена данными и контроллер для работы с ним.

Объект для обмена данными

Создадим в папке Models следующий класс.

public class DataTransferModel
{
    public int id { get; set; }
    public double Rate { get; set; }
    public String Title { get; set; }
    public String[] Content { get; set; }
}

Его экземпляры и будут теми объектами, которые мы будем принимать и отдавать в контроллере.

Использование в этом классе относительно простых типов и строк, а также автореализумых свойств обусловлено, не только демонстрационным характером задачи. Практически в любом API обмен данными построен на основе формата JSON. Поэтому очень желательно проектировать классы для обмена данными посредством API с учётом соображений простоты JSON сериализации и десериализации.

В противном случае обработать такой класс стандартными средствами .NET будет крайне сложно или невозможно вовсе. И тогда придётся писать собственный алгоритм преобразования.

Пример реализации обмена

Создадим Web API контроллер, который будет в случае GET запроса создавать и возвращать новый объект DataTransferModel, а в случае POST запроса получать подобный объект из его тела.

public class DtmController : ApiController
{
    public DataTransferModel Get()
    {
        DataTransferModel model = new DataTransferModel
        {
            id = 2018,
            Rate = 20.18,
            Title = "Год 2018",
            Content = new String[] { "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" }
        };
        return model;
    }
    public void Post([FromBody]DataTransferModel value)
    {
        DataTransferModel model = value;
    }
}

Теперь попробуем запустить приложение и выполнить GET запрос.

Обратите внимание, что по умолчанию адреса относящие к Web API содержат вставку «api/» между адресом и именем контроллера.

На скриншоте показан результат выполнения запроса. Но, он представлен в формате XML, при том, что все API обычно используют JSON.

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

Для этого мы воспользуемся обобщённым классом JsonResult (пространство имён System.Web.Http) и стандартной функцией Json, которая собственно и выполнит сам процесс сериализации.

После доработки метод для обработки GET запроса будет выглядеть так:

public JsonResult<DataTransferModel> Get()
{
    DataTransferModel model = new DataTransferModel
    {
        id = 2018,
        Rate = 20.18,
        Title = "Год 2018",
        Content = new String[] { "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" }
    };
    return Json(model);
}

Теперь результат запроса передаётся в формате JSON.

Что касается отправки данных на сервер, то там всё гораздо проще. Достаточно просто оправить в POST запросе JSON объект соответствующей структуры с HTTP заголовком «ContentTYpe: text/json». В результате на стороне сервера полученный JSON будет автоматически десериализован в требуемый объект.

public void Post([FromBody]DataTransferModel value)
{
    DataTransferModel model = value;
}

Однако такой подход имеет два очень существенных недостатка:

  • Приведённый метод при успешном выполнении вернёт код ответа 204 (no content). В идеале желательно вернуть что-то более информативное (например, код 200 (Ok));
  • Что будет, если полученный JSON не будет соответствовать требуемой структуре и в результате произойдёт критическая ошибка?

В первом случае вопрос можно легко решить при помощи объекта HttpResponseMessage.

Во втором случае Web API будет пытаться обнаружить в поступившем JSON данные, которые по своей структуре соответствуют нужному объекту и в случае успеха сформирует такой объект. В противном случае объект также будет создаваться. Только свойства ссылочных типов будут иметь значения null, а примитивных типов 0 (для bool false). Но, насколько будет корректно такое поведение программы?

Поэтому поступившие данные необходимо обязательно проверять.

Ниже приведён уже доработанный метод, котором в случае успешного получения объекта DataTransferModel возвращается код 200 и присутствует самая простейшая проверка полученных данных.

public void Post([FromBody]DataTransferModel value)
{
    if ((value!=null)&&(value.id!=0)&&(value.Title != null)&&(value.Content!=null))
    {
         DataTransferModel model = value;
         return new HttpResponseMessage(HttpStatusCode.OK);
    }
    else
    {
        HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.BadRequest);
        message.Content = new StringContent("{\"Message\": \"Неверный формат запроса\"");
        return message;
    }
}

Значение свойства Rate не проверяем, так как оно имеет вещественный тип, а значения вещественных типов нельзя проверять на строгое равенство или неравенство.

Заключение

Мы ознакомились с основными принципами обмена данными сложной структуры при помощи ASP.NET Web API.

Вопросы связанные с «клиентской» частью приложения в этой статье были специально опущены, так как в качестве клиента для API сервера может выступать не только Web приложение, как это было в случае AJAX. API сервер отвечает на HTTP запросы от любых клиентов, которые поддерживают данный протокол. Что делает такой подход поистине универсальным.

В частности скриншоты результатов выполнения GET запросов были сделаны при помощи обыкновенного браузера.

Использование Web API позволяет создавать при помощи ASP.NET web приложения совершенно иного уровня, который был почти недоступен ранее в Web Forms и даже «чистом» ASP.NET MVC.

Комментарии
  1. Вах. Классный пример. По возможности еще бы несколько примеров реализации API

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *