Включение операций CRUD в веб-API ASP.NET 1

Майк Уосон

Скачивание завершенного проекта

В этом руководстве показано, как поддерживать операции CRUD в службе HTTP с помощью веб-API ASP.NET для ASP.NET 4.x.

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

  • Visual Studio 2012 г.
  • Веб-API 1 (также работает с веб-API 2)

CRUD обозначает "Создание, чтение, обновление и удаление", которые являются четырьмя основными операциями базы данных. Многие службы HTTP также моделируют операции CRUD с помощью интерфейсов REST или аналогичных API.

В этом руководстве вы создадите очень простой веб-API для управления списком продуктов. Каждый продукт будет содержать имя, цену и категорию (например, "игрушки" или "оборудование"), а также идентификатор продукта.

API продуктов предоставляет следующие методы.

Действие Метод HTTP Относительный универсальный код ресурса (URI)
Получение списка всех продуктов GET /api/products
Получение сведений о продукте по идентификатору GET /api/products/id
Получить продукт по категории GET /api/products?category=category
Создание нового продукта ПОСТ /api/products
Обновление продукта ЗАПИСАТЬ /api/products/id
Удаление продукта DELETE /api/products/id

Обратите внимание, что некоторые URI содержат идентификатор продукта в своем пути. Например, чтобы получить продукт, идентификатор которого равен 28, клиент отправляет запрос GET.http://hostname/api/products/28

Ресурсы

API продуктов определяет URI для двух типов ресурсов:

Ресурс URI
Список всех продуктов. /api/products
Отдельный продукт. /api/products/id

Методы

Четыре основных метода HTTP (GET, PUT, POST и DELETE) можно сопоставить с операциями CRUD следующим образом:

  • GET извлекает представление ресурса по указанному URI. GET не должен иметь побочных эффектов на сервере.
  • PUT обновляет ресурс по указанному URI. PUT также можно использовать для создания нового ресурса по указанному URI, если сервер разрешает клиентам указывать новые URI. В этом руководстве API не будет поддерживать создание с помощью PUT.
  • POST создает новый ресурс. Сервер назначает универсальный код ресурса (URI) для нового объекта и возвращает этот универсальный код ресурса (URI) в рамках сообщения ответа.
  • DELETE удаляет ресурс по указанному URI.

Примечание. Метод PUT заменяет всю сущность продукта. То есть, клиент должен отправить полное представление обновленного продукта. Если вы хотите поддерживать частичные обновления, предпочтительным является метод PATCH. В этом руководстве не реализован патч.

Создайте новый проект веб-API

Начните с запуска Visual Studio и выберите новый проект на начальной странице. Или в меню файла выберите Создать, а затем Project.

В области "Шаблоны " выберите "Установленные шаблоны " и разверните узел Visual C# . В разделе Visual C#выберите Web. В списке шаблонов проектов выберите ASP.NET веб-приложение MVC 4. Присвойте проекту имя ProductStore и нажмите кнопку "ОК".

Снимок экрана нового окна проекта с параметрами меню и выделением пути для создания веб-приложения A S P dot NET M V C 4.

В диалоговом окне "Новый ASP.NET проект MVC 4" выберите веб-API и нажмите кнопку "ОК".

Скриншот нового проекта ASP.NET, на котором показаны изображения доступных шаблонов в рамках и выделенный синим цветом шаблон Web API.

Добавление модели

Модель — это объект, представляющий данные в приложении. В веб-API ASP.NET можно использовать строго типизированные объекты CLR в качестве моделей, и они автоматически сериализуются в XML или JSON для клиента.

Для API ProductStore наши данные состоят из продуктов, поэтому мы создадим новый класс с именем Product.

Если обозреватель решений еще не отображается, щелкните меню "Вид " и выберите обозреватель решений. В обозревателе решений щелкните правой кнопкой мыши папку Models . В контекстном меню выберите "Добавить", а затем выберите "Класс". Назовите класс Product.

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

Добавьте следующие свойства в Product класс.

namespace ProductStore.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

Добавление репозитория

Нам нужно хранить коллекцию продуктов. Рекомендуется отделять коллекцию от реализации нашего сервиса. Таким образом, можно изменить резервное хранилище без перезаписи класса службы. Этот тип дизайна называется шаблоном репозитория . Начните с определения универсального интерфейса для репозитория.

В обозревателе решений щелкните правой кнопкой мыши папку Models . Нажмите кнопку "Добавить", а затем выберите "Создать элемент".

Снимок экрана: меню обозревателя решений, которое выделяет параметр модели и открывает меню для добавления нового элемента.

В области "Шаблоны " выберите "Установленные шаблоны " и разверните узел C#. В разделе C# выберите "Код". В списке шаблонов кода выберите Интерфейс. Назовите интерфейс IProductRepository.

Снимок экрана: панель шаблонов с меню установленных шаблонов, в котором выделены параметры кода и интерфейса серым цветом.

Добавьте следующую реализацию:

namespace ProductStore.Models
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetAll();
        Product Get(int id);
        Product Add(Product item);
        void Remove(int id);
        bool Update(Product item);
    }
}

Теперь добавьте другой класс в папку Models с именем ProductRepository. Этот класс реализует IProductRepository интерфейс. Добавьте следующую реализацию:

namespace ProductStore.Models
{
    public class ProductRepository : IProductRepository
    {
        private List<Product> products = new List<Product>();
        private int _nextId = 1;

        public ProductRepository()
        {
            Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M });
            Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M });
            Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
        }

        public IEnumerable<Product> GetAll()
        {
            return products;
        }

        public Product Get(int id)
        {
            return products.Find(p => p.Id == id);
        }

        public Product Add(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            item.Id = _nextId++;
            products.Add(item);
            return item;
        }

        public void Remove(int id)
        {
            products.RemoveAll(p => p.Id == id);
        }

        public bool Update(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            int index = products.FindIndex(p => p.Id == item.Id);
            if (index == -1)
            {
                return false;
            }
            products.RemoveAt(index);
            products.Add(item);
            return true;
        }
    }
}

Репозиторий хранит список в локальной памяти. Это нормально для руководства, но в реальном приложении вы будете хранить данные вне базы данных или в облачном хранилище. Шаблон репозитория упрощает изменение реализации позже.

Добавление контроллера веб-API

Если вы работали с ASP.NET MVC, вы уже знакомы с контроллерами. В ASP.NET веб-API контроллер — это класс, который обрабатывает HTTP-запросы от клиента. Мастер нового проекта создал два контроллера для вас в процессе создания проекта. Чтобы увидеть их, разверните папку "Контроллеры" в обозревателе решений.

  • HomeController — это традиционный ASP.NET контроллер MVC. Он отвечает за обслуживание HTML-страниц для сайта и не напрямую связан с нашим веб-API.
  • ValuesController является примером контроллера WebAPI.

Перейдите вперед и удалите ValuesController, щелкнув правой кнопкой мыши файл в обозревателе решений и выбрав "Удалить". Теперь добавьте новый контроллер, как показано ниже.

В обозревателе решений щелкните правой кнопкой мыши папку Контроллеров. Нажмите кнопку "Добавить " и выберите "Контроллер".

Снимок экрана: меню обозревателя решений, выделение категории контроллеров, в котором приводится другое меню, выделение пути к добавлению контроллера.

В мастере добавления контроллера назовите контроллер ProductsController. В раскрывающемся списке шаблонов выберите пустой контроллер API. Нажмите кнопку Добавить.

Скриншот окна добавления контроллера, показывающий поле для ввода имени контроллера и выпадающий список шаблонов под параметрами каркаса.

Замечание

Не нужно помещать контроллеры в папку с именем Контроллеры. Имя папки не важно; это просто удобный способ упорядочить исходные файлы.

Мастер добавления контроллера создаст файл с именем ProductsController.cs в папке "Контроллеры". Если этот файл еще не открыт, дважды щелкните файл, чтобы открыть его. Добавьте следующую инструкцию using :

using ProductStore.Models;

Добавьте поле, которое содержит экземпляр IProductRepository .

public class ProductsController : ApiController
{
    static readonly IProductRepository repository = new ProductRepository();
}

Замечание

Вызов new ProductRepository() в контроллере не является лучшим архитектурным решением, так как жёстко привязывает контроллер к определенной реализации IProductRepository. Дополнительные сведения см. в разделе "Использование сопоставителя зависимостей веб-API".

Получение ресурса

API ProductStore предоставляет несколько действий чтения в виде методов HTTP GET. Каждое действие будет соответствовать методу ProductsController в классе.

Действие Метод HTTP Относительный универсальный код ресурса (URI)
Получение списка всех продуктов GET /api/products
Получение сведений о продукте по идентификатору GET /api/products/id
Получить продукт по категории GET /api/products?category=category

Чтобы получить список всех продуктов, добавьте этот метод в ProductsController класс:

public class ProductsController : ApiController
{
    public IEnumerable<Product> GetAllProducts()
    {
        return repository.GetAll();
    }
    // ....
}

Имя метода начинается с "Get", поэтому по соглашению оно сопоставляется с запросами GET. Кроме того, так как метод не имеет параметров, он сопоставляется с URI, который не содержит сегмент "id" в пути.

Чтобы получить продукт по идентификатору, добавьте этот метод в ProductsController класс:

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound); 
    }
    return item;
}

Это имя метода также начинается с "Get", но метод имеет параметр с именем id. Этот параметр сопоставляется с сегментом "id" в пути URI. Платформа веб-API ASP.NET автоматически преобразует идентификатор в правильный тип данных (int) для параметра.

Метод GetProduct создает исключение типа HttpResponseException , если идентификатор недействителен. Это исключение будет преобразовано платформой в ошибку 404 (не найдено).

Наконец, добавьте метод для поиска продуктов по категориям:

public IEnumerable<Product> GetProductsByCategory(string category)
{
    return repository.GetAll().Where(
        p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
}

Если URI запроса содержит строку запроса, веб-API пытается сопоставить параметры запроса с параметрами метода контроллера. Поэтому URI вида "api/products?category=category" будет сопоставлен с этим методом.

Создание ресурса

Затем мы добавим метод в ProductsController класс для создания нового продукта. Ниже приведена простая реализация метода:

// Not the final implementation!
public Product PostProduct(Product item)
{
    item = repository.Add(item);
    return item;
}

Обратите внимание на две вещи касательно этого метода:

  • Имя метода начинается с "Post...". Чтобы создать новый продукт, клиент отправляет HTTP-запрос POST.
  • Метод принимает параметр типа Product. В веб-API параметры со сложными типами десериализируются из текста запроса. Поэтому мы ожидаем, что клиент отправляет сериализованное представление объекта продукта в формате XML или JSON.

Эта реализация будет работать, но она не совсем завершена. В идеале мы хотели бы, чтобы http-ответ включал следующее:

  • Код ответа: По умолчанию платформа веб-API задает код состояния ответа 200 (ОК). Но в соответствии с протоколом HTTP/1.1, когда запрос POST приводит к созданию ресурса, сервер должен ответить с состоянием 201 (создан).
  • Расположение: Когда сервер создает ресурс, он должен включать URI нового ресурса в заголовок location ответа.

ASP.NET веб-API упрощает управление сообщением ОТВЕТА HTTP. Ниже приведена улучшенная реализация:

public HttpResponseMessage PostProduct(Product item)
{
    item = repository.Add(item);
    var response = Request.CreateResponse<Product>(HttpStatusCode.Created, item);

    string uri = Url.Link("DefaultApi", new { id = item.Id });
    response.Headers.Location = new Uri(uri);
    return response;
}

Обратите внимание, что тип возвращаемого метода теперь httpResponseMessage. Возвращая HttpResponseMessage вместо продукта, мы можем управлять подробностями HTTP-ответа, включая код состояния и заголовок Location.

Метод CreateResponse создает httpResponseMessage и автоматически записывает сериализованное представление объекта Product в текст сообщения ответа.

Замечание

В этом примере не проверяется Product. Сведения о проверке модели см. в разделе "Проверка модели" в веб-API ASP.NET.

Обновление ресурса

Обновление продукта с помощью PUT является простым:

public void PutProduct(int id, Product product)
{
    product.Id = id;
    if (!repository.Update(product))
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
}

Имя метода начинается с "Put...", поэтому веб-API сопоставляет его с запросами PUT. Метод принимает два параметра, идентификатор продукта и обновленный продукт. Параметр идентификатора взят из пути URI, а параметр продукта десериализирован из текста запроса. По умолчанию платформа веб-API ASP.NET принимает простые типы параметров из маршрута и сложных типов из текста запроса.

Удаление ресурса

Чтобы удалить ресурс, определите "Удалить..." Метод.

public void DeleteProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    repository.Remove(id);
}

Если запрос DELETE успешно выполнен, он может вернуть состояние 200 (ОК) с текстом сущности, описывающим состояние; состояние 202 (принято), если удаление по-прежнему ожидается; или состояние 204 (нет содержимого) без тела сущности. В этом случае DeleteProduct метод имеет тип возвращаемого void значения, поэтому веб-API ASP.NET автоматически преобразует его в код состояния 204 (нет содержимого).