Поделиться через


Включение операций 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, подобные REST.

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

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

Действие Метод HTTP Относительный URI
Получить список всех продуктов GET /api/products
Получение сведений о продукте по идентификатору GET /api/products/id
Получение продукта по категориям GET /api/products?category=category
Создание продукта POST /api/products
Обновить продукт PUT /api/products/id
Удалить продукт DELETE /api/products/id

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

Ресурсы

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

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

Методы

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

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

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

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

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

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

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

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

Снимок экрана: новый проект AS P Dot NET, в котором показаны готовые изображения доступных шаблонов и выделен шаблон Web A P I в синем цвете.

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

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

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

Если обозреватель решений не отображается, щелкните меню Просмотр и выберите Обозреватель решений. В Обозреватель решений щелкните правой кнопкой мыши папку Модели. В контекстном меню выберите Добавить, а затем — Класс. Назовите класс "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, вы уже знакомы с контроллерами. В веб-API ASP.NET контроллер — это класс, обрабатывающий HTTP-запросы от клиента. Мастер создания проекта создал два контроллера при создании проекта. Чтобы просмотреть их, разверните папку Контроллеры в Обозреватель решений.

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

Удалите ValuesController, щелкнув правой кнопкой мыши файл в Обозреватель решений и выбрав Удалить. Теперь добавьте новый контроллер следующим образом:

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

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

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

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

Примечание

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

Мастер добавления контроллера создаст файл с именем ProductsController.cs в папке Controllers. Если этот файл еще не открыт, дважды щелкните его. Добавьте следующую инструкцию 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 ответа.

веб-API ASP.NET упрощает управление сообщением ответа 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 вместо Product, мы можем управлять сведениями о сообщении ответа 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. Метод принимает два параметра: идентификатор продукта и обновленный продукт. Параметр id берется из пути URI, а параметр product десериализуется из текста запроса. По умолчанию платформа веб-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 (без содержимого).