- PVSM.RU - https://www.pvsm.ru -
Протокол HTTP может использоваться не только для веб-страниц. Это еще и мощная платформа для создания API, предоставляющих сервисы и данные. Протокол HTTP прост, гибок и широко распространен. Практически любая существующая платформа имеет библиотеку HTTP, поэтому службы HTTP доступны в большом числе клиентов, включая браузеры, мобильные устройства и традиционные офисные приложения.
В новой статье из серии ASP.NET Core [1] будет описано создание простого веб-API для работы со списком дел.
ASP.NET Core MVC имеет встроенную поддержку создания веб-API. Объединение двух платформ упрощает создание приложений, включающих как пользовательский интерфейс (HTML), так и API, так как в данном случае у них будет общий код и конвейер.
Примечание: если вы портируете существующее приложение веб-API на ASP.NET Core, прочитайте о том, как перейти с веб-API ASP.NET [2].
В этой статьей будем создавать следующий API:
API | Описание | Тело запроса | Тело ответа |
GET /api/todo | Получить все элементы списка дел | Нет | Массив элементов списка дел |
GET /api/todo/{id} | Получить элемент по идентификатору | Нет | Элемент списка дел |
POST /api/todo | Добавить новый элемент | Элемент списка дел | Элемент списка дел |
PUT /api/todo/{id} | Обновить существующий элемент | Элемент списка дел | Нет |
PATCH /api/todo/{id} | Обновить существующий элемент | Элемент списка дел | Нет |
DELETE /api/todo/{id} | Удалить элемент | Нет | Нет |
На диаграмме ниже показана архитектура приложения:
Запустите Visual Studio. В меню File выберите пункт New > Project. Выберите шаблон проекта ASP.NET Core Web Application (.NET Core). Назовите проект TodoApi
, снимите отметку Host in the cloud и нажмите OK.
В окне New ASP.NET Core Web Application (.NET Core) — TodoApi выберите шаблон Web API. Нажмите OK.
Модель — это объект, который представляет данные в нашем приложении. В данном случае единственная модель — это элемент списка дел.
Добавьте каталог с именем «Models». В обозревателе решений нажмите правую кнопку мыши на проекте. Выберите пункт Add > New Folder. Ведите имя каталога Models.
Примечание: классы модели могут находиться в любом месте проекта, но обычно их размещают в каталоге Models.
Добавьте класс TodoItem
. Нажмите правую кнопку мыши на каталоге Models и выберите пункт Add > Class. Ведите имя класса TodoItem
и нажмите Add.
Замените сформированный код следующим:
namespace TodoApi.Models
{
public class TodoItem
{
public string Key { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
Репозиторий — это объект, который инкапсулирует уровень данных и содержит логику для извлечения данных и направлениях их к модели. Хотя в данном приложении не используется база данных, имеет смысл показать, как можно внедрять репозитории в контроллеры. Создайте код репозитория в каталоге Models.
Начните с определения интерфейса репозитория с названием ITodoRepository
. Используйте шаблон класса (Add New Item > Class).
using System.Collections.Generic;
namespace TodoApi.Models
{
public interface ITodoRepository
{
void Add(TodoItem item);
IEnumerable<TodoItem> GetAll();
TodoItem Find(string key);
TodoItem Remove(string key);
void Update(TodoItem item);
}
}
Этот интерфейс определяет основные операции CRUD.
Затем добавьте класс TodoRepository
, который реализует ITodoRepository
:
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
namespace TodoApi.Models
{
public class TodoRepository : ITodoRepository
{
private static ConcurrentDictionary<string, TodoItem> _todos =
new ConcurrentDictionary<string, TodoItem>();
public TodoRepository()
{
Add(new TodoItem { Name = "Item1" });
}
public IEnumerable<TodoItem> GetAll()
{
return _todos.Values;
}
public void Add(TodoItem item)
{
item.Key = Guid.NewGuid().ToString();
_todos[item.Key] = item;
}
public TodoItem Find(string key)
{
TodoItem item;
_todos.TryGetValue(key, out item);
return item;
}
public TodoItem Remove(string key)
{
TodoItem item;
_todos.TryRemove(key, out item);
return item;
}
public void Update(TodoItem item)
{
_todos[item.Key] = item;
}
}
}
Постройте приложение, чтобы убедиться, что компилятор не выдает ошибок.
При определении интерфейса репозитория мы можем отделить класс репозитория от контроллера MVC, который его использует. Вместо реализации TodoRepository
внутри контроллера мы внедрим TodoRepository
, используя встроенную в ASP.NET Core поддержку внедрения зависимостей [5].
Такой подход упрощает модульное тестирование контроллеров. Модульные тесты внедряют «фиктивную» или «имитационную» версию ITodoRepository
. В этом случае тест нацелен на логику контроллера, а не на уровень доступа к данным.
Для внедрения репозитория в контроллер необходимо зарегистрировать его при помощи контейнеров DI. Откройте файл Startup.cs. Добавьте следующую директиву using:
using TodoApi.Models;
В метод ConfigureServices
добавьте выделенный код:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton<ITodoRepository, TodoRepository>();
}
В обозревателе решений нажмите правую кнопку мыши на каталоге Controllers. Выберите пункт Add > New Item. В окне Add New Item выберите шаблон Web API Controller Class. Введите имя класса TodoController
.
Замените сформированный код следующим:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using TodoApi.Models;
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
public class TodoController : Controller
{
public TodoController(ITodoRepository todoItems)
{
TodoItems = todoItems;
}
public ITodoRepository TodoItems { get; set; }
}
}
Таким образом определяется класс пустого контроллера. В следующих разделах описывается добавление методов для реализации API.
Чтобы получить элементы списка дел, добавьте следующие методы в класс TodoController
:
public IEnumerable<TodoItem> GetAll()
{
return TodoItems.GetAll();
}
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)
{
var item = TodoItems.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
Эти методы реализуют два метода GET:
GET /api/todo
GET /api/todo/{id}
В данном случае HTTP-ответ для метода GetAll
будет следующим:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/10.0
Date: Thu, 18 Jun 2015 20:51:10 GMT
Content-Length: 82
[{"Key":"4f67d7c5-a2a9-4aae-b030-16003dd829ae","Name":"Item1","IsComplete":false}]
Далее расскажем, как можно просматривать HTTP-ответы при помощи Postman [3].
Атрибут HttpGet
(HttpGetAttribute) определяет метод HTTP GET. URL-путь для каждого метода строится следующим образом:
[Route("api/[controller]")]
[HttpGet]
имеет строку шаблона, добавьте ее к пути. В данном примере строка шаблона не используется.
В методе GetById
:
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)
"{id}"
— это величина, заменяемая на идентификатор элемента todo
. Когда GetById
вызывается, значение “{id}” в URL присваивается параметру id
метода.
Name = "GetTodo"
создает именованный маршрут, позволяющий ссылаться на него в HTTP-ответе. В дальнейшем это будет показано на примере.
Метод GetAll
возвращает IEnumerable
. MVC автоматически сериализует объект в JSON и записывает JSON [7] в тело ответа. Код ответа для этого метода — 200, в том случае если нет необработанных исключений (необработанные исключения переводятся в ошибки 5xx.)
В свою очередь метод GetById
возвращает значение более общего типа IActionResult
, который представлен большим количеством типов возвращаемых значений. GetById
имеет два различных типа возвращаемых значений:
NotFound
.ObjectResult
.
Нажмите сочетание клавиш CTRL+F5 в Visual Studio, чтобы запустить приложение. Запустится браузер и откроется веб-страница http://localhost:port/api/values
, где port является произвольно выбранным номером порта. Если используется Chrome, Edge или Firefox, будут отображены данные. При использовании IE будет предложено открыть или сохранить файл values.json.
Добавим методы Create
, Update
и Delete
. Этот процесс аналогичен тому, о чем речь шла ранее, поэтому здесь будет показан код и выделены основные отличия. Создайте проект после добавления или изменения кода.
[HttpPost]
public IActionResult Create([FromBody] TodoItem item)
{
if (item == null)
{
return BadRequest();
}
TodoItems.Add(item);
return CreatedAtRoute("GetTodo", new { id = item.Key }, item);
}
Это метод HTTO POST, указанный в атрибуте [HttpPost] [8]. Атрибут [FromBody] [9] посылает команду MVC получить значение элемента списка дел из тела HTTP-запроса.
Метод CreatedAtRoute [10] возвращает ответ 201, который является стандартным ответом для метода HTTP POST, создающего новый ресурс на сервере. CreateAtRoute
также добавляет в ответ заголовок Location. Заголовок Location указывает URL созданного элемента списка дел. Описание: 10.2.2 201 Created [11].
POST
в качестве метода HTTP.{"Name":"<your to-do item>"}
.Выберите закладку Headers и скопируйте заголовок Location:
Для доступа к ресурсу, который только что создан, можно использовать URL из заголовка Location. Повторно вызовите метод GetById
, создавший именованный маршрут "GetTodo"
:
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)
[HttpPut("{id}")]
public IActionResult Update(string id, [FromBody] TodoItem item)
{
if (item == null || item.Key != id)
{
return BadRequest();
}
var todo = TodoItems.Find(id);
if (todo == null)
{
return NotFound();
}
TodoItems.Update(item);
return new NoContentResult();
}
Update
подобен Create
, но использует HTTP PUT. Ответ 204 (Нет содержимого) [12]. Согласно спецификации по HTTP по запросу PUT требуется, чтобы клиент отправил обновленный объект полностью, а не только дельты. Для поддержки частичных обновлений используйте HTTP PATCH.
Аналогично Update
, но с использованием HTTP PATCH. Ответ 204 (Нет содержимого) [12].
[HttpPatch("{id}")]
public IActionResult Update([FromBody] TodoItem item, string id)
{
if (item == null)
{
return BadRequest();
}
var todo = TodoItems.Find(id);
if (todo == null)
{
return NotFound();
}
item.Key = todo.Key;
TodoItems.Update(item);
return new NoContentResult();
}
[HttpDelete("{id}")]
public IActionResult Delete(string id)
{
var todo = TodoItems.Find(id);
if (todo == null)
{
return NotFound();
}
TodoItems.Remove(id);
return new NoContentResult();
}
Ответ 204 (Нет содержимого) [12].
Серия статей по ASP.NET Core:
1. ASP.NET Core на Nano Server [13].
2. ASP.NET Core: Создание внешнего интерфейса веб-службы для приложения [1].
3. ASP.NET Core: Создание первого веб-API с использованием ASP.NET Core MVC и Visual Studio. [14]
4. ASP.NET Core: Развертывание веб-приложения в службе приложений Azure с помощью Visual Studio.
5. ASP.NET Core: Ваше первое приложение на Mac c использованием Visual Studio Code.
Автор: Microsoft
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/200189
Ссылки в тексте:
[1] ASP.NET Core: https://habrahabr.ru/company/microsoft/blog/311940/
[2] прочитайте о том, как перейти с веб-API ASP.NET: https://docs.asp.net/en/latest/migration/webapi.html
[3] Postman: https://www.getpostman.com/
[4] статье: https://docs.asp.net/en/latest/tutorials/first-mvc-app/index.html
[5] внедрения зависимостей: https://docs.asp.net/en/latest/fundamentals/dependency-injection.html
[6] Маршрутизация: https://docs.asp.net/en/latest/mvc/controllers/routing.html
[7] JSON: http://www.json.org/
[8] [HttpPost]: https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNetCore/Mvc/HttpPostAttribute/index.html
[9] [FromBody]: https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNetCore/Mvc/FromBodyAttribute/index.html
[10] CreatedAtRoute: https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNetCore/Mvc/Controller/index.html
[11] 10.2.2 201 Created: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
[12] 204 (Нет содержимого): http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
[13] ASP.NET Core на Nano Server: https://habrahabr.ru/company/microsoft/blog/310996/
[14] ASP.NET Core: Создание первого веб-API с использованием ASP.NET Core MVC и Visual Studio.: https://habrahabr.ru/company/microsoft/blog/312878/
[15] Источник: https://habrahabr.ru/post/312878/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.