Руководство по созданию веб-API с помощью ASP.NET Core

Примечание.

Это не последняя версия этой статьи. Сведения о текущем выпуске см. в ASP.NET версии Core 8.0 этой статьи.

Авторы: Рик Андерсон (Rick Anderson) и Кирк Ларкин (Kirk Larkin)

В этом руководстве описаны основы создания веб-API на основе контроллера, использующего базу данных. Еще одним подходом к созданию API в ASP.NET Core является создание минимальных API. Сведения о выборе между минимальными API и API на основе контроллера см . в обзоре API. Руководство по созданию минимального API см . в руководстве по созданию минимального API с помощью ASP.NET Core.

Обзор

В этом руководстве создается следующий API-интерфейс:

API Description Текст запроса Текст ответа
GET /api/todoitems Получение всех элементов задач нет Массив элементов задач
GET /api/todoitems/{id} Получение объекта по идентификатору нет Элемент задачи
POST /api/todoitems Добавление нового элемента Элемент задачи Элемент задачи
PUT /api/todoitems/{id} Обновление существующего элемента Элемент задачи нет
DELETE /api/todoitems/{id}     Удаление элемента нет нет

На следующем рисунке показана структура приложения.

The client is represented by a box on the left. It submits a request and receives a response from the application, a box drawn on the right. Within the application box, three boxes represent the controller, the model, and the data access layer. The request comes into the application's controller, and read/write operations occur between the controller and the data access layer. The model is serialized and returned to the client in the response.

Необходимые компоненты

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

  • В меню Файл выберите пункт Создать>Проект.
  • В поле поиска введите Веб-API.
  • Выберите шаблон Веб-API ASP.NET Core и нажмите кнопку Далее.
  • В диалоговом окне Настроить новый проект присвойте проекту имя TodoApi и нажмите кнопку Далее.
  • В диалоговом окне "Дополнительные сведения":
    • Убедитесь, что платформа .NET 8.0 (долгосрочная поддержка).
    • Убедитесь, что флажок Use controllers (uncheck to use minimal APIs) (Использовать контроллеры (снимите этот флажок для использования минимальных API)) установлен.
    • Убедитесь, что проверка box для включения поддержки OpenAPI проверка.
    • Нажмите кнопку создания.

Добавление пакета NuGet

Пакет NuGet необходимо добавить для поддержки базы данных, используемой в этом руководстве.

  • В меню Средства выберите Диспетчер пакетов NuGet > Управление пакетами NuGet для решения.
  • Откройте вкладку Browse (Обзор).
  • Введите Microsoft.EntityFrameworkCore.InMemory в поле поиска и щелкните Microsoft.EntityFrameworkCore.InMemory.
  • Установите флажок Проект в области справа и выберите Установить.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Тестирование проекта

Шаблон проекта создает API WeatherForecast с поддержкой Swagger.

Нажмите клавиши CTRL+F5, чтобы выполнить запуск без отладчика.

Visual Studio отображает следующее диалоговое окно, если проект еще не настроен для использования SSL:

This project is configured to use SSL. To avoid SSL warnings in the browser you can choose to trust the self-signed certificate that IIS Express has generated. Would you like to trust the IIS Express SSL certificate?

Выберите Да, чтобы сделать SSL-сертификат IIS Express доверенным.

Отобразится следующее диалоговое окно.

Security warning dialog

Выберите Да, если согласны доверять сертификату разработки.

Сведения о доверии к браузеру Firefox см. в разделе Ошибка сертификата браузера Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.

Visual Studio запускает браузер по умолчанию и переходит https://localhost:<port>/swagger/index.htmlв папку, где <port> выбран случайный номер порта при создании проекта.

Откроется страница Swagger /swagger/index.html. Выберите Get (Получить)>Try it out (Попробовать)>Execute (Выполнить). На странице отобразятся:

  • команда Curl для тестирования API WeatherForecast;
  • URL-адрес для тестирования API WeatherForecast;
  • код, текст и заголовки ответа;
  • Раскрывающийся список с типами носителей и примером значения и схемы.

Если страница Swagger не отображается, см. эту проблему на сайте GitHub.

Swagger используется для создания полезной документации и страниц справки для веб-API. В этом руководстве используется Swagger для тестирования приложения. Дополнительные сведения о Swagger см. в статье Документация по веб-API ASP.NET Core с использованием Swagger (OpenAPI).

Скопируйте и вставьте URL-адрес запроса в адресную строку браузера: https://localhost:<port>/weatherforecast

Возвращаемые данные JSON будут примерно такими:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

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

Модель — это набор классов, представляющих данные, которыми управляет приложение. Для этого приложения используется класс модели TodoItem.

  • В обозревателе решений щелкните проект правой кнопкой мыши. Выберите Добавить>Новая папка. Назовите папку Models.
  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Присвойте классу имя TodoItem и выберите Добавить.
  • Замените код шаблона следующим кодом:
namespace TodoApi.Models;

public class TodoItem
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Свойство Id выступает в качестве уникального ключа реляционной базы данных.

Классы моделей можно размещать в любом месте проекта, но обычно для этого используется папка Models.

Добавление контекста базы данных

Контекст базы данных —это основной класс, который координирует функциональные возможности Entity Framework для модели данных. Этот класс является производным от класса Microsoft.EntityFrameworkCore.DbContext.

  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Назовите класс TodoContext и нажмите Добавить.
  • Введите следующий код:

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models;
    
    public class TodoContext : DbContext
    {
        public TodoContext(DbContextOptions<TodoContext> options)
            : base(options)
        {
        }
    
        public DbSet<TodoItem> TodoItems { get; set; } = null!;
    }
    

Регистрация контекста базы данных

В ASP.NET Core службы (такие как контекст базы данных) должны быть зарегистрированы с помощью контейнера внедрения зависимостей. Контейнер предоставляет службу контроллерам.

Обновите Program.cs следующий выделенный код:

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Предыдущий код:

  • Добавляет using директивы.
  • Добавляет контекст базы данных в контейнер внедрения зависимостей.
  • Указывает, что контекст базы данных будет использовать базу данных в памяти.

Формирование шаблонов контроллера

  • Щелкните папку Controllers правой кнопкой мыши.

  • Выберите Добавить >Создать шаблонный элемент.

  • Выберите Контроллер API с действиями, использующий Entity Framework, а затем выберите Добавить.

  • В диалоговом окне Контроллер API с действиями, использующий Entity Framework сделайте следующее:

    • Выберите TodoItem (TodoApi.Models) в поле Класс модели.
    • Выберите TodoContext (TodoApi.Models) в поле Класс контекста данных.
    • Выберите Добавить.

    Если операция формирования шаблонов завершается неудачно, нажмите кнопку Добавить, чтобы попытаться сформировать шаблон еще раз.

Сформированный код:

  • Пометьте этот класс атрибутом [ApiController]. Этот атрибут указывает, что контроллер отвечает на запросы веб-API. Дополнительные сведения о поведении, которое реализует этот атрибут, см. в статье Создание веб-API с помощью ASP.NET Core.
  • Использует внедрение зависимостей для внедрения контекста базы данных (TodoContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Шаблоны ASP.NET Core для:

  • Контроллеры с представлениями включают [action] в шаблоне маршрута.
  • Контроллеры API не включают [action] в шаблоне маршрута.

[action] Если маркер не входит в шаблон маршрута, имя действия (имя метода) не входит в конечную точку. То есть имя связанного метода действия не используется в соответствующем маршруте.

Обновление метода создания PostTodoItem

Измените инструкцию возврата в PostTodoItem и используйте оператор nameof:

[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //    return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}

Предыдущий код является методом HTTP POST , как указано атрибутом [HttpPost] . Метод получает значение TodoItem из текста HTTP-запроса.

Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

Метод CreatedAtAction:

  • В случае успеха возвращает код состояния HTTP 201. HTTP 201 — это стандартный HTTP POST ответ для метода, создающего новый ресурс на сервере.
  • Добавляет в ответ заголовок Location. Заголовок Location указывает URI новой созданной задачи. Дополнительные сведения см. в статье 10.2.2 201 "Создан ресурс".
  • Указывает действие GetTodoItem для создания URI заголовка Location. Ключевое слово nameof C# используется для предотвращения жесткого программирования имени действия в вызове CreatedAtAction.

Тестирование PostTodoItem

  • Нажмите клавиши CTRL+F5, чтобы запустить приложение.

  • В окне браузера Swagger выберите POST /api/TodoItems, а затем нажмите кнопку "Попробовать".

  • В окне ввода текста запроса обновите JSon. Например,

    {
      "name": "walk dog",
      "isComplete": true
    }
    
  • Нажмите кнопку Выполнить.

    Swagger POST

Тестирование URI заголовка расположения

В предыдущем post пользовательский интерфейс Swagger отображает заголовок расположения в заголовках ответа. Например, location: https://localhost:7260/api/TodoItems/1. Заголовок расположения отображает универсальный код ресурса (URI) для созданного ресурса.

Чтобы проверить заголовок расположения, выполните следующие действия.

  • В окне браузера Swagger выберите GET /api/TodoItems/{id}, а затем нажмите кнопку "Попробовать".

  • Введите 1 в id поле ввода и нажмите кнопку "Выполнить".

    Swagger GET

Знакомство с методами GET

Реализуются две конечные точки GET:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

В предыдущем разделе показан пример /api/todoitems/{id} маршрута.

Следуйте инструкциям POST, чтобы добавить другой элемент todo, а затем протестировать /api/todoitems маршрут с помощью Swagger.

Это приложение использует выполняющуюся в памяти базу данных. Если остановить и вновь запустить его, предшествующий запрос GET не возвратит никаких данных. Если данные не возвращаются, данные для приложения получаются методом POST.

Маршрутизация и пути URL

Атрибут [HttpGet] обозначает метод, который отвечает на HTTP GET запрос. Путь URL для каждого метода формируется следующим образом:

  • Возьмите строку шаблона в атрибуте Route контроллера:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Замените [controller] именем контроллера (по соглашению это имя класса контроллера без суффикса "Controller"). В этом примере класс контроллера имеет имя TodoItems, а сам контроллер, соответственно, — "TodoItems". В ASP.NET Core маршрутизация реализуется без учета регистра символов.

  • Если атрибут [HttpGet] имеет шаблон маршрута (например, [HttpGet("products")]), добавьте его к пути. В этом примере шаблон не используется. Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

В следующем методе GetTodoItem"{id}" — это переменная-заполнитель для уникального идентификатора элемента задачи. При вызове GetTodoItem параметру метода id присваивается значение "{id}" в URL-адресе.

[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Возвращаемые значения

Тип возвращаемого значения для методов GetTodoItems и GetTodoItem — ActionResult<T>. ASP.NET Core автоматически сериализует объект в формат JSON и записывает данные JSON в тело сообщения ответа. Код ответа для этого типа возвращаемого значения равен 200 OK, что свидетельствует об отсутствии необработанных исключений. Необработанные исключения преобразуются в ошибки 5xx.

Типы возвращаемых значений ActionResult могут представлять широкий спектр кодов состояний HTTP. Например, метод GetTodoItem может возвращать два разных значения состояния:

  • Если запрошенному идентификатору не соответствует ни один элемент, метод возвращает код ошибки 404NotFound (Не найдено).
  • В противном случае метод возвращает код 200 с телом ответа в формате JSON. item Возвращает результаты в ответеHTTP 200.

Метод PutTodoItem

Изучите метод PutTodoItem:

[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

PutTodoItem аналогичен PostTodoItem, за исключением того, что он использует HTTP PUT. Ответ — 204 (Нет содержимого). Согласно спецификации HTTP, PUT запрос требует от клиента отправки всей обновленной сущности, а не только изменений. Чтобы обеспечить поддержку частичных обновлений, используйте HTTP PATCH.

Тестирование метода PutTodoItem

В этом примере используется база данных в памяти, которая должна быть инициирована при каждом запуске приложения. При выполнении вызова PUT в базе данных уже должен существовать какой-либо элемент. Для этого перед вызовом PUT выполните вызов GET, чтобы убедиться в наличии такого элемента в базе данных.

Используя пользовательский интерфейс Swagger, нажмите кнопку PUT для обновления TodoItem идентификатора = 1 и задайте для него имя "feed fish". Обратите внимание, что ответ равен HTTP 204 No Content.

Метод DeleteTodoItem

Изучите метод DeleteTodoItem:

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

Тестирование метода DeleteTodoItem

Используйте пользовательский интерфейс Swagger для удаления TodoItem идентификатора = 1. Обратите внимание, что ответ равен HTTP 204 No Content.

Тестирование с помощью других средств

Существует множество других средств, которые можно использовать для тестирования веб-API, например:

Дополнительные сведения см. в разделе:

Предотвращение избыточной публикации

В настоящее время пример приложения предоставляет весь объект TodoItem. Рабочие приложения обычно ограничивают вводимые данные и возвращают их с помощью подмножества модели. Такое поведение реализовано по нескольким причинам, но в основном из соображений безопасности. Подмножество модели обычно называется объектом передачи данных (DTO), моделью ввода или моделью представления. В рамках этого руководства используется DTO.

DTO можно использовать для следующего:

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

Чтобы продемонстрировать подход с применением DTO, обновите класс TodoItem, включив в него поле секрета:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
        public string? Secret { get; set; }
    }
}

Поле секрета должно быть скрыто в этом приложении, однако административное приложение может отобразить его.

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

Создайте модель DTO:

namespace TodoApi.Models;

public class TodoItemDTO
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Обновите TodoItemsController для использования TodoItemDTO:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi.Controllers;

[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
    private readonly TodoContext _context;

    public TodoItemsController(TodoContext context)
    {
        _context = context;
    }

    // GET: api/TodoItems
    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
    {
        return await _context.TodoItems
            .Select(x => ItemToDTO(x))
            .ToListAsync();
    }

    // GET: api/TodoItems/5
    // <snippet_GetByID>
    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        return ItemToDTO(todoItem);
    }
    // </snippet_GetByID>

    // PUT: api/TodoItems/5
    // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
    // <snippet_Update>
    [HttpPut("{id}")]
    public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
    {
        if (id != todoDTO.Id)
        {
            return BadRequest();
        }

        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        todoItem.Name = todoDTO.Name;
        todoItem.IsComplete = todoDTO.IsComplete;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
        {
            return NotFound();
        }

        return NoContent();
    }
    // </snippet_Update>

    // POST: api/TodoItems
    // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
    // <snippet_Create>
    [HttpPost]
    public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
    {
        var todoItem = new TodoItem
        {
            IsComplete = todoDTO.IsComplete,
            Name = todoDTO.Name
        };

        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();

        return CreatedAtAction(
            nameof(GetTodoItem),
            new { id = todoItem.Id },
            ItemToDTO(todoItem));
    }
    // </snippet_Create>

    // DELETE: api/TodoItems/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        _context.TodoItems.Remove(todoItem);
        await _context.SaveChangesAsync();

        return NoContent();
    }

    private bool TodoItemExists(long id)
    {
        return _context.TodoItems.Any(e => e.Id == id);
    }

    private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
       new TodoItemDTO
       {
           Id = todoItem.Id,
           Name = todoItem.Name,
           IsComplete = todoItem.IsComplete
       };
}

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

Вызов веб-API с помощью JavaScript

См . руководство. Вызов веб-API ASP.NET Core с помощью JavaScript.

Серия видео веб-API

См . видео: серия начинающих: веб-API.

Надежные шаблоны веб-приложений

См . статью "Шаблон надежных веб-приложений" for.NETвидео и статьи YouTube по созданию современного, надежного, производительного, тестового, экономичного и масштабируемого приложения ASP.NET Core, будь то с нуля или рефакторинг существующего приложения.

Добавление поддержки аутентификации в веб-API

ASP.NET Core Identity позволяет использовать функцию входа в пользовательском интерфейсе для веб-приложений ASP.NET Core. Чтобы защитить веб-API и одностраничные приложения, используйте один из следующих способов:

Duende Identity Server — это платформа OpenID Connect и OAuth 2.0 для ASP.NET Core. Duende Identity Server включает следующие функции безопасности:

  • Проверка подлинности как услуга (AaaS)
  • Единый вход (SSO) для нескольких типов приложений
  • Контроль доступа для API
  • Шлюз федерации

Внимание

Компания Duende Software может потребовать лицензионный сбор за использование Duende IdentityServer в рабочей среде. Дополнительные сведения см. в статье Миграция с ASP.NET Core 5.0 на 6.0.

Дополнительные сведения см. в документации по Duende Identity Server (на веб-сайте ПО Duende).

Публикация в Azure

Сведения о развертывании в Azure см. в разделе Краткое руководство. Развертывание веб-приложения ASP.NET.

Дополнительные ресурсы

Просмотреть или скачать пример кода для этого учебника. См. раздел Практическое руководство. Скачивание файла.

Дополнительные сведения см. на следующих ресурсах:

В этом руководстве описаны основы создания веб-API на основе контроллера, использующего базу данных. Еще одним подходом к созданию API в ASP.NET Core является создание минимальных API. Сведения о выборе между минимальными API и API на основе контроллера см . в обзоре API. Руководство по созданию минимального API см . в руководстве по созданию минимального API с помощью ASP.NET Core.

Обзор

В этом руководстве создается следующий API-интерфейс:

API Description Текст запроса Текст ответа
GET /api/todoitems Получение всех элементов задач нет Массив элементов задач
GET /api/todoitems/{id} Получение объекта по идентификатору нет Элемент задачи
POST /api/todoitems Добавление нового элемента Элемент задачи Элемент задачи
PUT /api/todoitems/{id} Обновление существующего элемента Элемент задачи нет
DELETE /api/todoitems/{id}     Удаление элемента нет нет

На следующем рисунке показана структура приложения.

The client is represented by a box on the left. It submits a request and receives a response from the application, a box drawn on the right. Within the application box, three boxes represent the controller, the model, and the data access layer. The request comes into the application's controller, and read/write operations occur between the controller and the data access layer. The model is serialized and returned to the client in the response.

Необходимые компоненты

  • Visual Studio 2022 с рабочей нагрузкой ASP.NET и веб-разработка.

    VS22 installer workloads

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

  • В меню Файл выберите пункт Создать>Проект.
  • В поле поиска введите Веб-API.
  • Выберите шаблон Веб-API ASP.NET Core и нажмите кнопку Далее.
  • В диалоговом окне Настроить новый проект присвойте проекту имя TodoApi и нажмите кнопку Далее.
  • В диалоговом окне "Дополнительные сведения":
    • Убедитесь, что платформа.NET 7.0 (или более поздняя версия).
    • Убедитесь, что флажок Use controllers (uncheck to use minimal APIs) (Использовать контроллеры (снимите этот флажок для использования минимальных API)) установлен.
    • Нажмите кнопку создания.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Тестирование проекта

Шаблон проекта создает API WeatherForecast с поддержкой Swagger.

Нажмите клавиши CTRL+F5, чтобы выполнить запуск без отладчика.

Visual Studio отображает следующее диалоговое окно, если проект еще не настроен для использования SSL:

This project is configured to use SSL. To avoid SSL warnings in the browser you can choose to trust the self-signed certificate that IIS Express has generated. Would you like to trust the IIS Express SSL certificate?

Выберите Да, чтобы сделать SSL-сертификат IIS Express доверенным.

Отобразится следующее диалоговое окно.

Security warning dialog

Выберите Да, если согласны доверять сертификату разработки.

Сведения о доверии к браузеру Firefox см. в разделе Ошибка сертификата браузера Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.

Visual Studio запустит браузер по умолчанию и перейдет к https://localhost:<port>/swagger/index.html, где <port> — это номер порта, выбранный случайным образом.

Откроется страница Swagger /swagger/index.html. Выберите Get (Получить)>Try it out (Попробовать)>Execute (Выполнить). На странице отобразятся:

  • команда Curl для тестирования API WeatherForecast;
  • URL-адрес для тестирования API WeatherForecast;
  • код, текст и заголовки ответа;
  • Раскрывающийся список с типами носителей и примером значения и схемы.

Если страница Swagger не отображается, см. эту проблему на сайте GitHub.

Swagger используется для создания полезной документации и страниц справки для веб-API. В этом учебнике рассматривается создание веб-API. Дополнительные сведения о Swagger см. в статье Документация по веб-API ASP.NET Core с использованием Swagger (OpenAPI).

Скопируйте и вставьте URL-адрес запроса в адресную строку браузера: https://localhost:<port>/weatherforecast

Возвращаемые данные JSON будут примерно такими:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

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

Модель — это набор классов, представляющих данные, которыми управляет приложение. Для этого приложения используется класс модели TodoItem.

  • В обозревателе решений щелкните проект правой кнопкой мыши. Выберите Добавить>Новая папка. Назовите папку Models.
  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Присвойте классу имя TodoItem и выберите Добавить.
  • Замените код шаблона следующим кодом:
namespace TodoApi.Models;

public class TodoItem
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Свойство Id выступает в качестве уникального ключа реляционной базы данных.

Классы моделей можно размещать в любом месте проекта, но обычно для этого используется папка Models.

Добавление контекста базы данных

Контекст базы данных —это основной класс, который координирует функциональные возможности Entity Framework для модели данных. Этот класс является производным от класса Microsoft.EntityFrameworkCore.DbContext.

Добавление пакетов NuGet

  • В меню Средства выберите Диспетчер пакетов NuGet > Управление пакетами NuGet для решения.
  • Перейдите на вкладку Обзор и введите Microsoft.EntityFrameworkCore.InMemory в поле поиска.
  • В области слева щелкните Microsoft.EntityFrameworkCore.InMemory.
  • Установите флажок Проект в области справа и выберите Установить.

Добавление контекста базы данных TodoContext

  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Назовите класс TodoContext и нажмите Добавить.
  • Введите следующий код:

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models;
    
    public class TodoContext : DbContext
    {
        public TodoContext(DbContextOptions<TodoContext> options)
            : base(options)
        {
        }
    
        public DbSet<TodoItem> TodoItems { get; set; } = null!;
    }
    

Регистрация контекста базы данных

В ASP.NET Core службы (такие как контекст базы данных) должны быть зарегистрированы с помощью контейнера внедрения зависимостей. Контейнер предоставляет службу контроллерам.

Обновите Program.cs следующий выделенный код:

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Предыдущий код:

  • Добавляет using директивы.
  • Добавляет контекст базы данных в контейнер внедрения зависимостей.
  • Указывает, что контекст базы данных будет использовать базу данных в памяти.

Формирование шаблонов контроллера

  • Щелкните папку Controllers правой кнопкой мыши.

  • Выберите Добавить >Создать шаблонный элемент.

  • Выберите Контроллер API с действиями, использующий Entity Framework, а затем выберите Добавить.

  • В диалоговом окне Контроллер API с действиями, использующий Entity Framework сделайте следующее:

    • Выберите TodoItem (TodoApi.Models) в поле Класс модели.
    • Выберите TodoContext (TodoApi.Models) в поле Класс контекста данных.
    • Выберите Добавить.

    Если операция формирования шаблонов завершается неудачно, нажмите кнопку Добавить, чтобы попытаться сформировать шаблон еще раз.

Сформированный код:

  • Пометьте этот класс атрибутом [ApiController]. Этот атрибут указывает, что контроллер отвечает на запросы веб-API. Дополнительные сведения о поведении, которое реализует этот атрибут, см. в статье Создание веб-API с помощью ASP.NET Core.
  • Использует внедрение зависимостей для внедрения контекста базы данных (TodoContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Шаблоны ASP.NET Core для:

  • Контроллеры с представлениями включают [action] в шаблоне маршрута.
  • Контроллеры API не включают [action] в шаблоне маршрута.

[action] Если маркер не входит в шаблон маршрута, имя действия (имя метода) не входит в конечную точку. То есть имя связанного метода действия не используется в соответствующем маршруте.

Обновление метода создания PostTodoItem

Измените инструкцию возврата в PostTodoItem и используйте оператор nameof:

[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //    return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}

Предыдущий код является методом HTTP POST , как указано атрибутом [HttpPost] . Метод получает значение TodoItem из текста HTTP-запроса.

Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

Метод CreatedAtAction:

  • В случае успеха возвращает код состояния HTTP 201. HTTP 201 — это стандартный HTTP POST ответ для метода, создающего новый ресурс на сервере.
  • Добавляет в ответ заголовок Location. Заголовок Location указывает URI новой созданной задачи. Дополнительные сведения см. в статье 10.2.2 201 "Создан ресурс".
  • Указывает действие GetTodoItem для создания URI заголовка Location. Ключевое слово nameof C# используется для предотвращения жесткого программирования имени действия в вызове CreatedAtAction.

Тестирование PostTodoItem

  • Нажмите клавиши CTRL+F5, чтобы запустить приложение.

  • В окне браузера Swagger выберите POST /api/TodoItems, а затем нажмите кнопку "Попробовать".

  • В окне ввода текста запроса обновите JSon. Например,

    {
      "name": "walk dog",
      "isComplete": true
    }
    
  • Нажмите кнопку Выполнить.

    Swagger POST

Тестирование URI заголовка расположения

В предыдущем post пользовательский интерфейс Swagger отображает заголовок расположения в заголовках ответа. Например, location: https://localhost:7260/api/TodoItems/1. Заголовок расположения отображает универсальный код ресурса (URI) для созданного ресурса.

Чтобы проверить заголовок расположения, выполните следующие действия.

  • В окне браузера Swagger выберите GET /api/TodoItems/{id}, а затем нажмите кнопку "Попробовать".

  • Введите 1 в id поле ввода и нажмите кнопку "Выполнить".

    Swagger GET

Знакомство с методами GET

Реализуются две конечные точки GET:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

В предыдущем разделе показан пример /api/todoitems/{id} маршрута.

Следуйте инструкциям POST, чтобы добавить другой элемент todo, а затем протестировать /api/todoitems маршрут с помощью Swagger.

Это приложение использует выполняющуюся в памяти базу данных. Если остановить и вновь запустить его, предшествующий запрос GET не возвратит никаких данных. Если данные не возвращаются, данные для приложения получаются методом POST.

Маршрутизация и пути URL

Атрибут [HttpGet] обозначает метод, который отвечает на HTTP GET запрос. Путь URL для каждого метода формируется следующим образом:

  • Возьмите строку шаблона в атрибуте Route контроллера:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Замените [controller] именем контроллера (по соглашению это имя класса контроллера без суффикса "Controller"). В этом примере класс контроллера имеет имя TodoItems, а сам контроллер, соответственно, — "TodoItems". В ASP.NET Core маршрутизация реализуется без учета регистра символов.

  • Если атрибут [HttpGet] имеет шаблон маршрута (например, [HttpGet("products")]), добавьте его к пути. В этом примере шаблон не используется. Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

В следующем методе GetTodoItem"{id}" — это переменная-заполнитель для уникального идентификатора элемента задачи. При вызове GetTodoItem параметру метода id присваивается значение "{id}" в URL-адресе.

[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Возвращаемые значения

Тип возвращаемого значения для методов GetTodoItems и GetTodoItem — ActionResult<T>. ASP.NET Core автоматически сериализует объект в формат JSON и записывает данные JSON в тело сообщения ответа. Код ответа для этого типа возвращаемого значения равен 200 OK, что свидетельствует об отсутствии необработанных исключений. Необработанные исключения преобразуются в ошибки 5xx.

Типы возвращаемых значений ActionResult могут представлять широкий спектр кодов состояний HTTP. Например, метод GetTodoItem может возвращать два разных значения состояния:

  • Если запрошенному идентификатору не соответствует ни один элемент, метод возвращает код ошибки 404NotFound (Не найдено).
  • В противном случае метод возвращает код 200 с телом ответа в формате JSON. item Возвращает результаты в ответеHTTP 200.

Метод PutTodoItem

Изучите метод PutTodoItem:

[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

PutTodoItem аналогичен PostTodoItem, за исключением того, что он использует HTTP PUT. Ответ — 204 (Нет содержимого). Согласно спецификации HTTP, PUT запрос требует от клиента отправки всей обновленной сущности, а не только изменений. Чтобы обеспечить поддержку частичных обновлений, используйте HTTP PATCH.

Тестирование метода PutTodoItem

В этом примере используется база данных в памяти, которая должна быть инициирована при каждом запуске приложения. При выполнении вызова PUT в базе данных уже должен существовать какой-либо элемент. Для этого перед вызовом PUT выполните вызов GET, чтобы убедиться в наличии такого элемента в базе данных.

Используя пользовательский интерфейс Swagger, нажмите кнопку PUT для обновления TodoItem идентификатора = 1 и задайте для него имя "feed fish". Обратите внимание, что ответ равен HTTP 204 No Content.

Метод DeleteTodoItem

Изучите метод DeleteTodoItem:

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

Тестирование метода DeleteTodoItem

Используйте пользовательский интерфейс Swagger для удаления TodoItem идентификатора = 1. Обратите внимание, что ответ равен HTTP 204 No Content.

Тестирование с помощью http-repl, Postman или curl

http-repl, Postman и curl часто используются для тестирования API. Swagger использует curl и отображает отправленную curl команду.

Инструкции по этим средствам см. по следующим ссылкам:

Дополнительные сведения см http-repl. в разделе "Тестовые веб-API" с помощью HttpRepl.

Предотвращение избыточной публикации

В настоящее время пример приложения предоставляет весь объект TodoItem. Рабочие приложения обычно ограничивают вводимые данные и возвращают их с помощью подмножества модели. Такое поведение реализовано по нескольким причинам, но в основном из соображений безопасности. Подмножество модели обычно называется объектом передачи данных (DTO), моделью ввода или моделью представления. В рамках этого руководства используется DTO.

DTO можно использовать для следующего:

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

Чтобы продемонстрировать подход с применением DTO, обновите класс TodoItem, включив в него поле секрета:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
        public string? Secret { get; set; }
    }
}

Поле секрета должно быть скрыто в этом приложении, однако административное приложение может отобразить его.

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

Создайте модель DTO:

namespace TodoApi.Models;

public class TodoItemDTO
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Обновите TodoItemsController для использования TodoItemDTO:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi.Controllers;

[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
    private readonly TodoContext _context;

    public TodoItemsController(TodoContext context)
    {
        _context = context;
    }

    // GET: api/TodoItems
    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
    {
        return await _context.TodoItems
            .Select(x => ItemToDTO(x))
            .ToListAsync();
    }

    // GET: api/TodoItems/5
    // <snippet_GetByID>
    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        return ItemToDTO(todoItem);
    }
    // </snippet_GetByID>

    // PUT: api/TodoItems/5
    // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
    // <snippet_Update>
    [HttpPut("{id}")]
    public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
    {
        if (id != todoDTO.Id)
        {
            return BadRequest();
        }

        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        todoItem.Name = todoDTO.Name;
        todoItem.IsComplete = todoDTO.IsComplete;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
        {
            return NotFound();
        }

        return NoContent();
    }
    // </snippet_Update>

    // POST: api/TodoItems
    // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
    // <snippet_Create>
    [HttpPost]
    public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
    {
        var todoItem = new TodoItem
        {
            IsComplete = todoDTO.IsComplete,
            Name = todoDTO.Name
        };

        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();

        return CreatedAtAction(
            nameof(GetTodoItem),
            new { id = todoItem.Id },
            ItemToDTO(todoItem));
    }
    // </snippet_Create>

    // DELETE: api/TodoItems/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        _context.TodoItems.Remove(todoItem);
        await _context.SaveChangesAsync();

        return NoContent();
    }

    private bool TodoItemExists(long id)
    {
        return _context.TodoItems.Any(e => e.Id == id);
    }

    private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
       new TodoItemDTO
       {
           Id = todoItem.Id,
           Name = todoItem.Name,
           IsComplete = todoItem.IsComplete
       };
}

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

Вызов веб-API с помощью JavaScript

См . руководство. Вызов веб-API ASP.NET Core с помощью JavaScript.

Серия видео веб-API

См . видео: серия начинающих: веб-API.

Надежные шаблоны веб-приложений

См . статью "Шаблон надежных веб-приложений" for.NETвидео и статьи YouTube по созданию современного, надежного, производительного, тестового, экономичного и масштабируемого приложения ASP.NET Core, будь то с нуля или рефакторинг существующего приложения.

Добавление поддержки аутентификации в веб-API

ASP.NET Core Identity позволяет использовать функцию входа в пользовательском интерфейсе для веб-приложений ASP.NET Core. Чтобы защитить веб-API и одностраничные приложения, используйте один из следующих способов:

Duende Identity Server — это платформа OpenID Connect и OAuth 2.0 для ASP.NET Core. Duende Identity Server включает следующие функции безопасности:

  • Проверка подлинности как услуга (AaaS)
  • Единый вход (SSO) для нескольких типов приложений
  • Контроль доступа для API
  • Шлюз федерации

Внимание

Компания Duende Software может потребовать лицензионный сбор за использование Duende IdentityServer в рабочей среде. Дополнительные сведения см. в статье Миграция с ASP.NET Core 5.0 на 6.0.

Дополнительные сведения см. в документации по Duende Identity Server (на веб-сайте ПО Duende).

Публикация в Azure

Сведения о развертывании в Azure см. в разделе Краткое руководство. Развертывание веб-приложения ASP.NET.

Дополнительные ресурсы

Просмотреть или скачать пример кода для этого учебника. См. раздел Практическое руководство. Скачивание файла.

Дополнительные сведения см. на следующих ресурсах:

В этом руководстве описаны основы создания веб-API на основе контроллера, использующего базу данных. Еще одним подходом к созданию API в ASP.NET Core является создание минимальных API. Сведения о выборе между минимальными API и API на основе контроллера см . в обзоре API. Руководство по созданию минимального API см . в руководстве по созданию минимального API с помощью ASP.NET Core.

В этом руководстве описано следующее:

  • Создание проекта веб-API.
  • Добавление класса модели и контекста базы данных.
  • Формирование шаблонов контроллера с использованием методов CRUD.
  • Настройка маршрутизации, URL-пути и возвращаемых значений.
  • Вызов веб-API с помощью http-repl.

В итоге вы получите веб-API, позволяющий работать с элементами списка дел, хранимыми в базе данных.

Обзор

В этом руководстве создается следующий API-интерфейс:

API Description Текст запроса Текст ответа
GET /api/todoitems Получение всех элементов задач нет Массив элементов задач
GET /api/todoitems/{id} Получение объекта по идентификатору нет Элемент задачи
POST /api/todoitems Добавление нового элемента Элемент задачи Элемент задачи
PUT /api/todoitems/{id} Обновление существующего элемента Элемент задачи нет
DELETE /api/todoitems/{id}     Удаление элемента нет нет

На следующем рисунке показана структура приложения.

The client is represented by a box on the left. It submits a request and receives a response from the application, a box drawn on the right. Within the application box, three boxes represent the controller, the model, and the data access layer. The request comes into the application's controller, and read/write operations occur between the controller and the data access layer. The model is serialized and returned to the client in the response.

Необходимые компоненты

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

  • В меню Файл выберите пункт Создать>Проект.
  • В поле поиска введите Веб-API.
  • Выберите шаблон Веб-API ASP.NET Core и нажмите кнопку Далее.
  • В диалоговом окне Настроить новый проект присвойте проекту имя TodoApi и нажмите кнопку Далее.
  • В диалоговом окне "Дополнительные сведения":
    • Убедитесь, что для параметра Платформа выбрано значение .NET 6.0 (долгосрочная поддержка).
    • Убедитесь, что флажок Use controllers (uncheck to use minimal APIs) (Использовать контроллеры (снимите этот флажок для использования минимальных API)) установлен.
    • Нажмите кнопку создания.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Тестирование проекта

Шаблон проекта создает API WeatherForecast с поддержкой Swagger.

Нажмите клавиши CTRL+F5, чтобы выполнить запуск без отладчика.

Visual Studio отображает следующее диалоговое окно, если проект еще не настроен для использования SSL:

This project is configured to use SSL. To avoid SSL warnings in the browser you can choose to trust the self-signed certificate that IIS Express has generated. Would you like to trust the IIS Express SSL certificate?

Выберите Да, чтобы сделать SSL-сертификат IIS Express доверенным.

Отобразится следующее диалоговое окно.

Security warning dialog

Выберите Да, если согласны доверять сертификату разработки.

Сведения о доверии к браузеру Firefox см. в разделе Ошибка сертификата браузера Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.

Visual Studio запустит браузер по умолчанию и перейдет к https://localhost:<port>/swagger/index.html, где <port> — это номер порта, выбранный случайным образом.

Откроется страница Swagger /swagger/index.html. Выберите Get (Получить)>Try it out (Попробовать)>Execute (Выполнить). На странице отобразятся:

  • команда Curl для тестирования API WeatherForecast;
  • URL-адрес для тестирования API WeatherForecast;
  • код, текст и заголовки ответа;
  • Раскрывающийся список с типами носителей и примером значения и схемы.

Если страница Swagger не отображается, см. эту проблему на сайте GitHub.

Swagger используется для создания полезной документации и страниц справки для веб-API. В этом учебнике рассматривается создание веб-API. Дополнительные сведения о Swagger см. в статье Документация по веб-API ASP.NET Core с использованием Swagger (OpenAPI).

Скопируйте и вставьте URL-адрес запроса в адресную строку браузера: https://localhost:<port>/weatherforecast

Возвращаемые данные JSON будут примерно такими:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

Обновление launchUrl

В файле Properties/launchSettings.json обновите launchUrl с "swagger" на "api/todoitems".

"launchUrl": "api/todoitems",

Поскольку Swagger будет удален, в предыдущей разметке был изменен URL-адрес, который запускается в методе GET контроллера, добавленного в следующих разделах.

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

Модель — это набор классов, представляющих данные, которыми управляет приложение. Модель этого приложения содержит единственный класс TodoItem.

  • В обозревателе решений щелкните проект правой кнопкой мыши. Выберите Добавить>Новая папка. Назовите папку Models.

  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Присвойте классу имя TodoItem и выберите Добавить.

  • Замените код шаблона следующим кодом:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Свойство Id выступает в качестве уникального ключа реляционной базы данных.

Классы моделей можно размещать в любом месте проекта, но обычно для этого используется папка Models.

Добавление контекста базы данных

Контекст базы данных —это основной класс, который координирует функциональные возможности Entity Framework для модели данных. Этот класс является производным от класса Microsoft.EntityFrameworkCore.DbContext.

Добавление пакетов NuGet

  • В меню Средства выберите Диспетчер пакетов NuGet > Управление пакетами NuGet для решения.
  • Перейдите на вкладку Обзор и введите Microsoft.EntityFrameworkCore.InMemory в поле поиска.
  • В области слева щелкните Microsoft.EntityFrameworkCore.InMemory.
  • Установите флажок Проект в области справа и выберите Установить.

Добавление контекста базы данных TodoContext

  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Назовите класс TodoContext и нажмите Добавить.
  • Введите следующий код:

    using Microsoft.EntityFrameworkCore;
    using System.Diagnostics.CodeAnalysis;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; } = null!;
        }
    }
    

Регистрация контекста базы данных

В ASP.NET Core службы (такие как контекст базы данных) должны быть зарегистрированы с помощью контейнера внедрения зависимостей. Контейнер предоставляет службу контроллерам.

Обновите Program.cs, включив в него следующий код.

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
//builder.Services.AddSwaggerGen(c =>
//{
//    c.SwaggerDoc("v1", new() { Title = "TodoApi", Version = "v1" });
//});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (builder.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    //app.UseSwagger();
    //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Предыдущий код:

  • Удаляет вызовы Swagger.
  • Удаляет неиспользуемые директивы using.
  • Добавляет контекст базы данных в контейнер внедрения зависимостей.
  • Указывает, что контекст базы данных будет использовать базу данных в памяти.

Формирование шаблонов контроллера

  • Щелкните папку Controllers правой кнопкой мыши.

  • Выберите Добавить >Создать шаблонный элемент.

  • Выберите Контроллер API с действиями, использующий Entity Framework, а затем выберите Добавить.

  • В диалоговом окне Контроллер API с действиями, использующий Entity Framework сделайте следующее:

    • Выберите TodoItem (TodoApi.Models) в поле Класс модели.
    • Выберите TodoContext (TodoApi.Models) в поле Класс контекста данных.
    • Выберите Добавить.

    Если операция формирования шаблонов завершается неудачно, нажмите кнопку Добавить, чтобы попытаться сформировать шаблон еще раз.

Сформированный код:

  • Пометьте этот класс атрибутом [ApiController]. Этот атрибут указывает, что контроллер отвечает на запросы веб-API. Дополнительные сведения о поведении, которое реализует этот атрибут, см. в статье Создание веб-API с помощью ASP.NET Core.
  • Использует внедрение зависимостей для внедрения контекста базы данных (TodoContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Шаблоны ASP.NET Core для:

  • Контроллеры с представлениями включают [action] в шаблоне маршрута.
  • Контроллеры API не включают [action] в шаблоне маршрута.

Если токен [action] не находится в шаблоне маршрута, имя действия исключается из маршрута. То есть имя связанного метода действия не используется в соответствующем маршруте.

Обновление метода создания PostTodoItem

Измените инструкцию возврата в PostTodoItem и используйте оператор nameof:

[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}

Предыдущий код является методом HTTP POST, обозначенным атрибутом [HttpPost]. Этот метод получает значение элемента списка дел из текста HTTP-запроса.

Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

Метод CreatedAtAction:

  • В случае успеха возвращает код состояния HTTP 201. HTTP 201 представляет собой стандартный ответ для метода HTTP POST, создающий ресурс на сервере.
  • Добавляет в ответ заголовок Location. Заголовок Location указывает URI новой созданной задачи. Дополнительные сведения см. в статье 10.2.2 201 "Создан ресурс".
  • Указывает действие GetTodoItem для создания URI заголовка Location. Ключевое слово nameof C# используется для предотвращения жесткого программирования имени действия в вызове CreatedAtAction.

Установка http-repl

В рамках этого руководства для проверки веб-API используется средство http-repl.

  • Выполните в командной строке следующую команду:

    dotnet tool install -g Microsoft.dotnet-httprepl
    

    Примечание.

    По умолчанию архитектура двоичных файлов .NET для установки представляет архитектуру операционной системы. Чтобы указать другую архитектуру ОС, см . параметр dotnet tool install, --arch. Дополнительные сведения см. в статье о проблеме GitHub dotnet/AspNetCore.Docs #29262.

  • Если у вас не установлены среда выполнения или пакет SDK для .NET 6.0, установите среду выполнения .NET 6.0.

Тестирование PostTodoItem

  • Нажмите клавиши CTRL+F5, чтобы запустить приложение.

  • Откройте новое окно терминала и выполните приведенные ниже команды. Если приложение использует другой номер порта, укажите его вместо 5001 в команде httprepl.

    httprepl https://localhost:5001/api/todoitems
    post -h Content-Type=application/json -c "{"name":"walk dog","isComplete":true}"
    

    Ниже показан пример результатов выполнения команды :

    HTTP/1.1 201 Created
    Content-Type: application/json; charset=utf-8
    Date: Tue, 07 Sep 2021 20:39:47 GMT
    Location: https://localhost:5001/api/TodoItems/1
    Server: Kestrel
    Transfer-Encoding: chunked
    
    {
      "id": 1,
      "name": "walk dog",
      "isComplete": true
    }
    

Тестирование URI заголовка расположения

Чтобы проверить заголовок location, скопируйте его и вставьте в команду httprepl get.

В следующем примере предполагается, что вы все еще находитесь в сеансе httprepl. Если вы уже завершили сеанс httprepl, измените connect на httprepl в следующих командах:

connect https://localhost:5001/api/todoitems/1
get

Ниже показан пример результатов выполнения команды :

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 07 Sep 2021 20:48:10 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
  "id": 1,
  "name": "walk dog",
  "isComplete": true
}

Знакомство с методами GET

Реализуются две конечные точки GET:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

Это был пример маршрута /api/todoitems/{id}. Проверьте маршрут /api/todoitems:

connect https://localhost:5001/api/todoitems
get

Ниже показан пример результатов выполнения команды :

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 07 Sep 2021 20:59:21 GMT
Server: Kestrel
Transfer-Encoding: chunked

[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]

На этот раз возвращается JSON с массивом из одного элемента.

Это приложение использует выполняющуюся в памяти базу данных. Если остановить и вновь запустить его, предшествующий запрос GET не возвратит никаких данных. Если данные не возвращаются, данные для приложения получаются методом POST.

Маршрутизация и пути URL

Атрибут [HttpGet] обозначает метод, который отвечает на запрос HTTP GET. Путь URL для каждого метода формируется следующим образом:

  • Возьмите строку шаблона в атрибуте Route контроллера:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Замените [controller] именем контроллера (по соглашению это имя класса контроллера без суффикса "Controller"). В этом примере класс контроллера имеет имя TodoItems, а сам контроллер, соответственно, — "TodoItems". В ASP.NET Core маршрутизация реализуется без учета регистра символов.

  • Если атрибут [HttpGet] имеет шаблон маршрута (например, [HttpGet("products")]), добавьте его к пути. В этом примере шаблон не используется. Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

В следующем методе GetTodoItem"{id}" — это переменная-заполнитель для уникального идентификатора элемента задачи. При вызове GetTodoItem параметру метода id присваивается значение "{id}" в URL-адресе.

[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Возвращаемые значения

Тип возвращаемого значения для методов GetTodoItems и GetTodoItem — ActionResult<T>. ASP.NET Core автоматически сериализует объект в формат JSON и записывает данные JSON в тело сообщения ответа. Код ответа для этого типа возвращаемого значения равен 200 OK, что свидетельствует об отсутствии необработанных исключений. Необработанные исключения преобразуются в ошибки 5xx.

Типы возвращаемых значений ActionResult могут представлять широкий спектр кодов состояний HTTP. Например, метод GetTodoItem может возвращать два разных значения состояния:

  • Если запрошенному идентификатору не соответствует ни один элемент, метод возвращает код ошибки 404NotFound (Не найдено).
  • В противном случае метод возвращает код 200 с телом ответа в формате JSON. При возвращении item возвращается ответ HTTP 200.

Метод PutTodoItem

Изучите метод PutTodoItem:

[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

Страница PutTodoItem аналогична странице PostTodoItem, но использует запрос HTTP PUT. Ответ — 204 (Нет содержимого). Согласно спецификации HTTP, запрос PUT требует, чтобы клиент отправлял всю обновленную сущность, а не только изменения. Чтобы обеспечить поддержку частичных обновлений, используйте HTTP PATCH.

Если во время работы со следующим разделом при вызове PutTodoItem произойдет ошибка, вызовите GET, чтобы в базе данных существовал один элемент.

Тестирование метода PutTodoItem

В этом примере используется база данных в памяти, которая должна быть инициирована при каждом запуске приложения. При выполнении вызова PUT в базе данных уже должен существовать какой-либо элемент. Для этого перед вызовом PUT выполните вызов GET, чтобы убедиться в наличии такого элемента в базе данных.

Обновите элемент списка дел с идентификатором 1 и присвойте ему имя "feed fish":

connect https://localhost:5001/api/todoitems/1
put -h Content-Type=application/json -c "{"id":1,"name":"feed fish","isComplete":true}"

Ниже показан пример результатов выполнения команды :

HTTP/1.1 204 No Content
Date: Tue, 07 Sep 2021 21:20:47 GMT
Server: Kestrel

Метод DeleteTodoItem

Изучите метод DeleteTodoItem:

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

Тестирование метода DeleteTodoItem

Удалите элемент списка задач с идентификатором Id = 1:

connect https://localhost:5001/api/todoitems/1
delete

Ниже показан пример результатов выполнения команды :

HTTP/1.1 204 No Content
Date: Tue, 07 Sep 2021 21:43:00 GMT
Server: Kestrel

Предотвращение избыточной публикации

В настоящее время пример приложения предоставляет весь объект TodoItem. Рабочие приложения обычно ограничивают вводимые данные и возвращают их с помощью подмножества модели. Такое поведение реализовано по нескольким причинам, но в основном из соображений безопасности. Подмножество модели обычно называется объектом передачи данных (DTO), моделью ввода или моделью представления. В рамках этого руководства используется DTO.

DTO можно использовать для следующего:

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

Чтобы продемонстрировать подход с применением DTO, обновите класс TodoItem, включив в него поле секрета:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
        public string? Secret { get; set; }
    }
}

Поле секрета должно быть скрыто в этом приложении, однако административное приложение может отобразить его.

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

Создайте модель DTO:

namespace TodoApi.Models
{
    public class TodoItemDTO
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Обновите TodoItemsController для использования TodoItemDTO:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;

        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }

        // GET: api/TodoItems
        [HttpGet]
        public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
        {
            return await _context.TodoItems
                .Select(x => ItemToDTO(x))
                .ToListAsync();
        }

        // GET: api/TodoItems/5
        [HttpGet("{id}")]
        public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
        {
            var todoItem = await _context.TodoItems.FindAsync(id);

            if (todoItem == null)
            {
                return NotFound();
            }

            return ItemToDTO(todoItem);
        }
        // PUT: api/TodoItems/5
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
        {
            if (id != todoItemDTO.Id)
            {
                return BadRequest();
            }

            var todoItem = await _context.TodoItems.FindAsync(id);
            if (todoItem == null)
            {
                return NotFound();
            }

            todoItem.Name = todoItemDTO.Name;
            todoItem.IsComplete = todoItemDTO.IsComplete;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
            {
                return NotFound();
            }

            return NoContent();
        }
        // POST: api/TodoItems
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
        {
            var todoItem = new TodoItem
            {
                IsComplete = todoItemDTO.IsComplete,
                Name = todoItemDTO.Name
            };

            _context.TodoItems.Add(todoItem);
            await _context.SaveChangesAsync();

            return CreatedAtAction(
                nameof(GetTodoItem),
                new { id = todoItem.Id },
                ItemToDTO(todoItem));
        }

        // DELETE: api/TodoItems/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteTodoItem(long id)
        {
            var todoItem = await _context.TodoItems.FindAsync(id);

            if (todoItem == null)
            {
                return NotFound();
            }

            _context.TodoItems.Remove(todoItem);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        private bool TodoItemExists(long id)
        {
            return _context.TodoItems.Any(e => e.Id == id);
        }

        private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
            new TodoItemDTO
            {
                Id = todoItem.Id,
                Name = todoItem.Name,
                IsComplete = todoItem.IsComplete
            };
    }
}

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

Вызов веб-API с помощью JavaScript

См . руководство. Вызов веб-API ASP.NET Core с помощью JavaScript.

Серия видео веб-API

См . видео: серия начинающих: веб-API.

Добавление поддержки аутентификации в веб-API

ASP.NET Core Identity позволяет использовать функцию входа в пользовательском интерфейсе для веб-приложений ASP.NET Core. Чтобы защитить веб-API и одностраничные приложения, используйте один из следующих способов:

Duende Identity Server — это платформа OpenID Connect и OAuth 2.0 для ASP.NET Core. Duende Identity Server включает следующие функции безопасности:

  • Проверка подлинности как услуга (AaaS)
  • Единый вход (SSO) для нескольких типов приложений
  • Контроль доступа для API
  • Шлюз федерации

Внимание

Компания Duende Software может потребовать лицензионный сбор за использование Duende IdentityServer в рабочей среде. Дополнительные сведения см. в статье Миграция с ASP.NET Core 5.0 на 6.0.

Дополнительные сведения см. в документации по Duende Identity Server (на веб-сайте ПО Duende).

Публикация в Azure

Сведения о развертывании в Azure см. в разделе Краткое руководство. Развертывание веб-приложения ASP.NET.

Дополнительные ресурсы

Просмотреть или скачать пример кода для этого учебника. См. раздел Практическое руководство. Скачивание файла.

Дополнительные сведения см. на следующих ресурсах:

В этом руководстве описаны основы создания веб-API на основе контроллера, использующего базу данных. Еще одним подходом к созданию API в ASP.NET Core является создание минимальных API. Сведения о выборе между минимальными API и API на основе контроллера см . в обзоре API. Руководство по созданию минимального API см . в руководстве по созданию минимального API с помощью ASP.NET Core.

В этом руководстве описано следующее:

  • Создание проекта веб-API.
  • Добавление класса модели и контекста базы данных.
  • Формирование шаблонов контроллера с использованием методов CRUD.
  • Настройка маршрутизации, URL-пути и возвращаемых значений.
  • Вызов веб-API с помощью Postman.

В итоге вы получите веб-API, позволяющий работать с элементами списка дел, хранимыми в базе данных.

Обзор

В этом руководстве создается следующий API-интерфейс:

API Description Текст запроса Текст ответа
GET /api/todoitems Получение всех элементов задач нет Массив элементов задач
GET /api/todoitems/{id} Получение объекта по идентификатору нет Элемент задачи
POST /api/todoitems Добавление нового элемента Элемент задачи Элемент задачи
PUT /api/todoitems/{id} Обновление существующего элемента Элемент задачи нет
DELETE /api/todoitems/{id}     Удаление элемента нет нет

На следующем рисунке показана структура приложения.

The client is represented by a box on the left. It submits a request and receives a response from the application, a box drawn on the right. Within the application box, three boxes represent the controller, the model, and the data access layer. The request comes into the application's controller, and read/write operations occur between the controller and the data access layer. The model is serialized and returned to the client in the response.

Необходимые компоненты

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

  • В меню Файл выберите пункт Создать>Проект.
  • Выберите шаблон Веб-API ASP.NET Core и нажмите кнопку Далее.
  • Назовите проект TodoApi и нажмите Создать.
  • Убедитесь, что в диалоговом окне Создание веб-приложения ASP.NET Core выбраны платформы .NET Core и ASP.NET Core 5.0. Выберите шаблон API и нажмите кнопку Создать.

VS new project dialog

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Тестирование проекта

Шаблон проекта создает API WeatherForecast с поддержкой Swagger.

Нажмите клавиши CTRL+F5, чтобы выполнить запуск без отладчика.

Visual Studio отображает следующее диалоговое окно, если проект еще не настроен для использования SSL:

This project is configured to use SSL. To avoid SSL warnings in the browser you can choose to trust the self-signed certificate that IIS Express has generated. Would you like to trust the IIS Express SSL certificate?

Выберите Да, чтобы сделать SSL-сертификат IIS Express доверенным.

Отобразится следующее диалоговое окно.

Security warning dialog

Выберите Да, если согласны доверять сертификату разработки.

Сведения о доверии к браузеру Firefox см. в разделе Ошибка сертификата браузера Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.

Visual Studio запустит:

  • веб-сервер IIS Express;
  • браузер по умолчанию и перейдет к https://localhost:<port>/swagger/index.html, где <port> — это номер порта, выбранный случайным образом.

Откроется страница Swagger /swagger/index.html. Выберите Get (Получить)>Try it out (Попробовать)>Execute (Выполнить). На странице отобразятся:

  • команда Curl для тестирования API WeatherForecast;
  • URL-адрес для тестирования API WeatherForecast;
  • код, текст и заголовки ответа;
  • раскрывающийся список с типами мультимедиа и примером значения и схемы.

Если страница Swagger не отображается, см. эту проблему на сайте GitHub.

Swagger используется для создания полезной документации и страниц справки для веб-API. В этом учебнике рассматривается создание веб-API. Дополнительные сведения о Swagger см. в статье Документация по веб-API ASP.NET Core с использованием Swagger (OpenAPI).

Скопируйте и вставьте URL-адрес запроса в адресную строку браузера: https://localhost:<port>/weatherforecast

Возвращаемые данные JSON будут выглядеть примерно так:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

Обновление launchUrl

В файле Properties/launchSettings.json обновите launchUrl с "swagger" на "api/todoitems".

"launchUrl": "api/todoitems",

Поскольку Swagger будет удален, в предыдущей разметке был изменен URL-адрес, который запускается в методе GET контроллера, добавленного в следующих разделах.

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

Модель — это набор классов, представляющих данные, которыми управляет приложение. Модель этого приложения содержит единственный класс TodoItem.

  • В обозревателе решений щелкните проект правой кнопкой мыши. Выберите Добавить>Новая папка. Назовите папку Models.

  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Присвойте классу имя TodoItem и выберите Добавить.

  • Замените код шаблона следующим кодом:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Свойство Id выступает в качестве уникального ключа реляционной базы данных.

Классы моделей можно размещать в любом месте проекта, но обычно для этого используется папка Models.

Добавление контекста базы данных

Контекст базы данных —это основной класс, который координирует функциональные возможности Entity Framework для модели данных. Этот класс является производным от класса Microsoft.EntityFrameworkCore.DbContext.

Добавление пакетов NuGet

  • В меню Средства выберите Диспетчер пакетов NuGet > Управление пакетами NuGet для решения.
  • Перейдите на вкладку Обзор и введите Microsoft.EntityFrameworkCore.InMemory в поле поиска.
  • В области слева щелкните Microsoft.EntityFrameworkCore.InMemory.
  • Установите флажок Проект в области справа и выберите Установить.

NuGet Package Manager

Добавление контекста базы данных TodoContext

  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Назовите класс TodoContext и нажмите Добавить.
  • Введите следующий код:

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; }
        }
    }
    

Регистрация контекста базы данных

В ASP.NET Core службы (такие как контекст базы данных) должны быть зарегистрированы с помощью контейнера внедрения зависимостей. Контейнер предоставляет службу контроллерам.

Обновите Startup.cs, включив в него следующий код.

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddDbContext<TodoContext>(opt =>
                                               opt.UseInMemoryDatabase("TodoList"));
            //services.AddSwaggerGen(c =>
            //{
            //    c.SwaggerDoc("v1", new OpenApiInfo { Title = "TodoApi", Version = "v1" });
            //});
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                //app.UseSwagger();
                //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
            }

            app.UseHttpsRedirection();
            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Предыдущий код:

  • Удаляет вызовы Swagger.
  • Удалите неиспользуемые объявления using.
  • Добавляет контекст базы данных в контейнер внедрения зависимостей.
  • Указывает, что контекст базы данных будет использовать базу данных в памяти.

Формирование шаблонов контроллера

  • Щелкните папку Controllers правой кнопкой мыши.

  • Выберите Добавить >Создать шаблонный элемент.

  • Выберите Контроллер API с действиями, использующий Entity Framework, а затем выберите Добавить.

  • В диалоговом окне Контроллер API с действиями, использующий Entity Framework сделайте следующее:

    • Выберите TodoItem (TodoApi.Models) в поле Класс модели.
    • Выберите TodoContext (TodoApi.Models) в поле Класс контекста данных.
    • Выберите Добавить.

Сформированный код:

  • Пометьте этот класс атрибутом [ApiController]. Этот атрибут указывает, что контроллер отвечает на запросы веб-API. Дополнительные сведения о поведении, которое реализует этот атрибут, см. в статье Создание веб-API с помощью ASP.NET Core.
  • Использует внедрение зависимостей для внедрения контекста базы данных (TodoContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Шаблоны ASP.NET Core для:

  • Контроллеры с представлениями включают [action] в шаблоне маршрута.
  • Контроллеры API не включают [action] в шаблоне маршрута.

Если токен [action] не находится в шаблоне маршрута, имя действия исключается из маршрута. То есть имя связанного метода действия не используется в соответствующем маршруте.

Обновление метода создания PostTodoItem

Измените инструкцию возврата в PostTodoItem и используйте оператор nameof:

// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}

Предыдущий код является методом HTTP POST, обозначенным атрибутом [HttpPost]. Этот метод получает значение элемента списка дел из текста HTTP-запроса.

Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

Метод CreatedAtAction:

  • В случае успеха возвращает код состояния HTTP 201. HTTP 201 представляет собой стандартный ответ для метода HTTP POST, создающий ресурс на сервере.
  • Добавляет в ответ заголовок Location. Заголовок Location указывает URI новой созданной задачи. Дополнительные сведения см. в статье 201.
  • Указывает действие GetTodoItem для создания URI заголовка Location. Ключевое слово nameof C# используется для предотвращения жесткого программирования имени действия в вызове CreatedAtAction.

Установка Postman

В этом учебнике для тестирования веб-API используется Postman.

  • Установка Postman
  • Запустите веб-приложение.
  • Запустите Postman.
  • Отключите проверку SSL-сертификата:
    • Postman для Windows: выберите Файл>Параметры (вкладка Общие) и отключите параметр Проверка SSL-сертификата.
    • Postman для macOS: выберите Postman>Settings (Параметры) (вкладка General (Общие)) и отключите параметр SSL certificate verification (Проверка SSL-сертификата).

      Предупреждение

      После тестирования контроллера снова включите проверку SSL-сертификата.

Тестирование PostTodoItem с использованием Postman

  • Создайте новый запрос.

  • Установите HTTP-метод POST.

  • Задайте для URI значение https://localhost:<port>/api/todoitems. Например, https://localhost:5001/api/todoitems.

  • Выберите вкладку Body (Текст).

  • Установите переключатель без обработки.

  • Задайте тип JSON (приложение/json).

  • В теле запроса введите код JSON для элемента списка дел:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • Выберите Отправить.

    Postman with create request

Тестирование URI заголовка расположения

URI заголовка расположения можно протестировать в браузере. Скопируйте и вставьте URI заголовка расположения в строку браузера.

Чтобы протестировать в Postman, выполните следующие действия.

  • Перейдите на вкладку Заголовки в области Ответ.

  • Скопируйте значение заголовка Расположение:

    Headers tab of the Postman console

  • Установите HTTP-метод GET.

  • Задайте для URI значение https://localhost:<port>/api/todoitems/1. Например, https://localhost:5001/api/todoitems/1.

  • Выберите Отправить.

Знакомство с методами GET

Реализуются две конечные точки GET:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

Протестируйте приложение, вызвав эти две конечные точки в браузере или в Postman. Например:

  • https://localhost:5001/api/todoitems
  • https://localhost:5001/api/todoitems/1

При вызове GetTodoItems возвращается примерно такой ответ:

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

Тестирование Get с использованием Postman

  • Создайте новый запрос.
  • Укажите метод HTTP GET.
  • Задайте для URI запроса значение https://localhost:<port>/api/todoitems. Например, https://localhost:5001/api/todoitems.
  • Выберите режим Представление с двумя областями в Postman.
  • Выберите Отправить.

Это приложение использует выполняющуюся в памяти базу данных. Если остановить и вновь запустить его, предшествующий запрос GET не возвратит никаких данных. Если данные не возвращаются, данные для приложения получаются методом POST.

Маршрутизация и пути URL

Атрибут [HttpGet] обозначает метод, который отвечает на запрос HTTP GET. Путь URL для каждого метода формируется следующим образом:

  • Возьмите строку шаблона в атрибуте Route контроллера:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • Замените [controller] именем контроллера (по соглашению это имя класса контроллера без суффикса "Controller"). В этом примере класс контроллера имеет имя TodoItems, а сам контроллер, соответственно, — "TodoItems". В ASP.NET Core маршрутизация реализуется без учета регистра символов.

  • Если атрибут [HttpGet] имеет шаблон маршрута (например, [HttpGet("products")]), добавьте его к пути. В этом примере шаблон не используется. Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

В следующем методе GetTodoItem"{id}" — это переменная-заполнитель для уникального идентификатора элемента задачи. При вызове GetTodoItem параметру метода id присваивается значение "{id}" в URL-адресе.

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Возвращаемые значения

Тип возвращаемого значения для методов GetTodoItems и GetTodoItem — ActionResult<T>. ASP.NET Core автоматически сериализует объект в формат JSON и записывает данные JSON в тело сообщения ответа. Код ответа для этого типа возвращаемого значения равен 200 OK, что свидетельствует об отсутствии необработанных исключений. Необработанные исключения преобразуются в ошибки 5xx.

Типы возвращаемых значений ActionResult могут представлять широкий спектр кодов состояний HTTP. Например, метод GetTodoItem может возвращать два разных значения состояния:

  • Если запрошенному идентификатору не соответствует ни один элемент, метод возвращает код ошибки 404NotFound (Не найдено).
  • В противном случае метод возвращает код 200 с телом ответа в формате JSON. При возвращении item возвращается ответ HTTP 200.

Метод PutTodoItem

Изучите метод PutTodoItem:

// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

Страница PutTodoItem аналогична странице PostTodoItem, но использует запрос HTTP PUT. Ответ — 204 (Нет содержимого). Согласно спецификации HTTP, запрос PUT требует, чтобы клиент отправлял всю обновленную сущность, а не только изменения. Чтобы обеспечить поддержку частичных обновлений, используйте HTTP PATCH.

Если возникнет ошибка вызова PutTodoItem, вызовите GET, чтобы в базе данных был один элемент.

Тестирование метода PutTodoItem

В этом примере используется база данных в памяти, которая должна быть инициирована при каждом запуске приложения. При выполнении вызова PUT в базе данных уже должен существовать какой-либо элемент. Для этого перед вызовом PUT выполните вызов GET, чтобы убедиться в наличии такого элемента в базе данных.

Обновите элемент списка дел с идентификатором 1 и присвойте ему имя "feed fish":

  {
    "Id":1,
    "name":"feed fish",
    "isComplete":true
  }

На следующем рисунке показан процесс обновления Postman:

Postman console showing 204 (No Content) response

Метод DeleteTodoItem

Изучите метод DeleteTodoItem:

// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

Тестирование метода DeleteTodoItem

Удалите элемент списка дел с помощью Postman:

  • Укажите метод DELETE.
  • Укажите URI удаляемого объекта (например, https://localhost:5001/api/todoitems/1).
  • Выберите Отправить.

Предотвращение избыточной публикации

В настоящее время пример приложения предоставляет весь объект TodoItem. Рабочие приложения обычно ограничивают вводимые данные и возвращают их с помощью подмножества модели. Это связано с несколькими причинами, и безопасность является основной. Подмножество модели обычно называется объектом передачи данных (DTO), моделью ввода или моделью представления. В этой статье используется DTO.

DTO можно использовать для следующего:

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

Чтобы продемонстрировать подход с применением DTO, обновите класс TodoItem, включив в него поле секрета:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
        public string Secret { get; set; }
    }
}

Поле секрета должно быть скрыто в этом приложении, однако административное приложение может отобразить его.

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

Создайте модель DTO:

public class TodoItemDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

Обновите TodoItemsController для использования TodoItemDTO:

// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
    return await _context.TodoItems
        .Select(x => ItemToDTO(x))
        .ToListAsync();
}

[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return ItemToDTO(todoItem);
}

[HttpPut("{id}")]
public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
{
    if (id != todoItemDTO.Id)
    {
        return BadRequest();
    }

    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    todoItem.Name = todoItemDTO.Name;
    todoItem.IsComplete = todoItemDTO.IsComplete;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
    {
        return NotFound();
    }

    return NoContent();
}

[HttpPost]
public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
{
    var todoItem = new TodoItem
    {
        IsComplete = todoItemDTO.IsComplete,
        Name = todoItemDTO.Name
    };

    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    return CreatedAtAction(
        nameof(GetTodoItem),
        new { id = todoItem.Id },
        ItemToDTO(todoItem));
}

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

private bool TodoItemExists(long id) =>
     _context.TodoItems.Any(e => e.Id == id);

private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
    new TodoItemDTO
    {
        Id = todoItem.Id,
        Name = todoItem.Name,
        IsComplete = todoItem.IsComplete
    };

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

Вызов веб-API с помощью JavaScript

См . руководство. Вызов веб-API ASP.NET Core с помощью JavaScript.

Добавление поддержки аутентификации в веб-API

ASP.NET Core Identity позволяет использовать функцию входа в пользовательском интерфейсе для веб-приложений ASP.NET Core. Чтобы защитить веб-API и одностраничные приложения, используйте один из следующих способов:

Duende Identity Server — это платформа OpenID Connect и OAuth 2.0 для ASP.NET Core. Duende Identity Server включает следующие функции безопасности:

  • Проверка подлинности как услуга (AaaS)
  • Единый вход (SSO) для нескольких типов приложений
  • Контроль доступа для API
  • Шлюз федерации

Внимание

Компания Duende Software может потребовать лицензионный сбор за использование Duende IdentityServer в рабочей среде. Дополнительные сведения см. в статье Миграция с ASP.NET Core 5.0 на 6.0.

Дополнительные сведения см. в документации по Duende Identity Server (на веб-сайте ПО Duende).

Публикация в Azure

Сведения о развертывании в Azure см. в разделе Краткое руководство. Развертывание веб-приложения ASP.NET.

Дополнительные ресурсы

Просмотреть или скачать пример кода для этого учебника. См. раздел Практическое руководство. Скачивание файла.

Дополнительные сведения см. на следующих ресурсах:

В этом руководстве описаны основы создания веб-API на основе контроллера, использующего базу данных. Еще одним подходом к созданию API в ASP.NET Core является создание минимальных API. Сведения о выборе между минимальными API и API на основе контроллера см . в обзоре API. Руководство по созданию минимального API см . в руководстве по созданию минимального API с помощью ASP.NET Core.

В этом руководстве описано следующее:

  • Создание проекта веб-API.
  • Добавление класса модели и контекста базы данных.
  • Формирование шаблонов контроллера с использованием методов CRUD.
  • Настройка маршрутизации, URL-пути и возвращаемых значений.
  • Вызов веб-API с помощью Postman.

В итоге вы получите веб-API, позволяющий работать с элементами списка дел, хранимыми в базе данных.

Обзор

В этом руководстве создается следующий API-интерфейс:

API Description Текст запроса Текст ответа
GET /api/todoitems Получение всех элементов задач нет Массив элементов задач
GET /api/todoitems/{id} Получение объекта по идентификатору нет Элемент задачи
POST /api/todoitems Добавление нового элемента Элемент задачи Элемент задачи
PUT /api/todoitems/{id} Обновление существующего элемента Элемент задачи нет
DELETE /api/todoitems/{id}     Удаление элемента нет нет

На следующем рисунке показана структура приложения.

The client is represented by a box on the left. It submits a request and receives a response from the application, a box drawn on the right. Within the application box, three boxes represent the controller, the model, and the data access layer. The request comes into the application's controller, and read/write operations occur between the controller and the data access layer. The model is serialized and returned to the client in the response.

Необходимые компоненты

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

  • В меню Файл выберите пункт Создать>Проект.
  • Выберите шаблон Веб-приложение ASP.NET Core и нажмите Далее.
  • Назовите проект TodoApi и нажмите Создать.
  • В диалоговом окне Создание веб-приложения ASP.NET Core убедитесь в том, что выбраны платформы .NET Core и ASP.NET Core 3.1. Выберите шаблон API и нажмите кнопку Создать.

VS new project dialog

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Проверка API

Шаблон проекта создает API WeatherForecast. Вызовите метод Get из браузера для тестирования приложения.

Нажмите клавиши CTRL+F5, чтобы запустить приложение. Visual Studio запустит браузер и перейдет к https://localhost:<port>/weatherforecast, где <port> — это номер порта, выбранный случайным образом.

Если появится диалоговое окно с запросом о необходимости доверять сертификату IIS Express, выберите Да. В появляющемся следом диалоговом окне Предупреждение системы безопасности выберите Да.

Возвращаемые данные JSON будут выглядеть примерно так:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

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

Модель — это набор классов, представляющих данные, которыми управляет приложение. Модель этого приложения содержит единственный класс TodoItem.

  • В обозревателе решений щелкните проект правой кнопкой мыши. Выберите Добавить>Новая папка. Назовите папку Models.

  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Присвойте классу имя TodoItem и выберите Добавить.

  • Замените код шаблона следующим кодом:

public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

Свойство Id выступает в качестве уникального ключа реляционной базы данных.

Классы моделей можно размещать в любом месте проекта, но обычно для этого используется папка Models.

Добавление контекста базы данных

Контекст базы данных —это основной класс, который координирует функциональные возможности Entity Framework для модели данных. Этот класс является производным от класса Microsoft.EntityFrameworkCore.DbContext.

Добавление пакетов NuGet

  • В меню Средства выберите Диспетчер пакетов NuGet > Управление пакетами NuGet для решения.
  • Перейдите на вкладку Обзор и введите Microsoft.EntityFrameworkCore.InMemory в поле поиска.
  • На панели слева выберите Microsoft.EntityFrameworkCore.InMemory.
  • Установите флажок Проект в области справа и выберите Установить.

NuGet Package Manager

Добавление контекста базы данных TodoContext

  • Щелкните папку Models правой кнопкой мыши и выберите пункты Добавить>Класс. Назовите класс TodoContext и нажмите Добавить.
  • Введите следующий код:

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; }
        }
    }
    

Регистрация контекста базы данных

В ASP.NET Core службы (такие как контекст базы данных) должны быть зарегистрированы с помощью контейнера внедрения зависимостей. Контейнер предоставляет службу контроллерам.

Обновите Startup.cs следующий выделенный код:

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TodoContext>(opt =>
               opt.UseInMemoryDatabase("TodoList"));
            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Предыдущий код:

  • Удалите неиспользуемые объявления using.
  • Добавляет контекст базы данных в контейнер внедрения зависимостей.
  • Указывает, что контекст базы данных будет использовать базу данных в памяти.

Формирование шаблонов контроллера

  • Щелкните папку Controllers правой кнопкой мыши.

  • Выберите Добавить >Создать шаблонный элемент.

  • Выберите Контроллер API с действиями, использующий Entity Framework, а затем выберите Добавить.

  • В диалоговом окне Контроллер API с действиями, использующий Entity Framework сделайте следующее:

    • Выберите TodoItem (TodoApi.Models) в поле Класс модели.
    • Выберите TodoContext (TodoApi.Models) в поле Класс контекста данных.
    • Выберите Добавить.

Сформированный код:

  • Пометьте этот класс атрибутом [ApiController]. Этот атрибут указывает, что контроллер отвечает на запросы веб-API. Дополнительные сведения о поведении, которое реализует этот атрибут, см. в статье Создание веб-API с помощью ASP.NET Core.
  • Использует внедрение зависимостей для внедрения контекста базы данных (TodoContext) в контроллер. Контекст базы данных используется в каждом методе создания, чтения, обновления и удаления в контроллере.

Шаблоны ASP.NET Core для:

  • Контроллеры с представлениями включают [action] в шаблоне маршрута.
  • Контроллеры API не включают [action] в шаблоне маршрута.

Если токен [action] не находится в шаблоне маршрута, имя действия исключается из маршрута. То есть имя связанного метода действия не используется в соответствующем маршруте.

Знакомство с методом создания PostTodoItem

Измените инструкцию возврата в PostTodoItem и используйте оператор nameof:

// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}

Предыдущий код является методом HTTP POST, обозначенным атрибутом [HttpPost]. Этот метод получает значение элемента списка дел из текста HTTP-запроса.

Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

Метод CreatedAtAction:

  • В случае успеха возвращает код состояния HTTP 201. HTTP 201 представляет собой стандартный ответ для метода HTTP POST, создающий ресурс на сервере.
  • Добавляет в ответ заголовок Location. Заголовок Location указывает URI новой созданной задачи. Дополнительные сведения см. в статье 201.
  • Указывает действие GetTodoItem для создания URI заголовка Location. Ключевое слово nameof C# используется для предотвращения жесткого программирования имени действия в вызове CreatedAtAction.

Установка Postman

В этом учебнике для тестирования веб-API используется Postman.

  • Установка Postman
  • Запустите веб-приложение.
  • Запустите Postman.
  • Отключите проверку SSL-сертификата:
    • Postman для Windows: выберите Файл>Параметры (вкладка Общие) и отключите параметр Проверка SSL-сертификата.
    • Postman для macOS: выберите Postman>Settings (Параметры) (вкладка General (Общие)) и отключите параметр SSL certificate verification (Проверка SSL-сертификата).

      Предупреждение

      После тестирования контроллера снова включите проверку SSL-сертификата.

Тестирование PostTodoItem с использованием Postman

  • Создайте новый запрос.

  • Установите HTTP-метод POST.

  • Задайте для URI значение https://localhost:<port>/api/todoitems. Например, https://localhost:5001/api/todoitems.

  • Выберите вкладку Body (Текст).

  • Установите переключатель без обработки.

  • Задайте тип JSON (приложение/json).

  • В теле запроса введите код JSON для элемента списка дел:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • Выберите Отправить.

    Postman with create request

Тестирование URI заголовка расположения с помощью Postman

  • Перейдите на вкладку Заголовки в области Ответ.

  • Скопируйте значение заголовка Расположение:

    Headers tab of the Postman console

  • Установите HTTP-метод GET.

  • Задайте для URI значение https://localhost:<port>/api/todoitems/1. Например, https://localhost:5001/api/todoitems/1.

  • Выберите Отправить.

Знакомство с методами GET

Эти методы реализуют две конечные точки GET:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

Протестируйте приложение, вызвав эти две конечные точки в браузере или в Postman. Например:

  • https://localhost:5001/api/todoitems
  • https://localhost:5001/api/todoitems/1

При вызове GetTodoItems возвращается примерно такой ответ:

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

Тестирование Get с использованием Postman

  • Создайте новый запрос.
  • Укажите метод HTTP GET.
  • Задайте для URI запроса значение https://localhost:<port>/api/todoitems. Например, https://localhost:5001/api/todoitems.
  • Выберите режим Представление с двумя областями в Postman.
  • Выберите Отправить.

Это приложение использует выполняющуюся в памяти базу данных. Если остановить и вновь запустить его, предшествующий запрос GET не возвратит никаких данных. Если данные не возвращаются, данные для приложения получаются методом POST.

Маршрутизация и пути URL

Атрибут [HttpGet] обозначает метод, который отвечает на запрос HTTP GET. Путь URL для каждого метода формируется следующим образом:

  • Возьмите строку шаблона в атрибуте Route контроллера:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • Замените [controller] именем контроллера (по соглашению это имя класса контроллера без суффикса "Controller"). В этом примере класс контроллера имеет имя TodoItems, а сам контроллер, соответственно, — "TodoItems". В ASP.NET Core маршрутизация реализуется без учета регистра символов.

  • Если атрибут [HttpGet] имеет шаблон маршрута (например, [HttpGet("products")]), добавьте его к пути. В этом примере шаблон не используется. Дополнительные сведения см. в разделе Маршрутизация атрибутов с помощью атрибутов Http[Verb].

В следующем методе GetTodoItem"{id}" — это переменная-заполнитель для уникального идентификатора элемента задачи. При вызове GetTodoItem параметру метода id присваивается значение "{id}" в URL-адресе.

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Возвращаемые значения

Тип возвращаемого значения для методов GetTodoItems и GetTodoItem — ActionResult<T>. ASP.NET Core автоматически сериализует объект в формат JSON и записывает данные JSON в тело сообщения ответа. Код ответа для этого типа возвращаемого значения равен 200, что свидетельствует об отсутствии необработанных исключений. Необработанные исключения преобразуются в ошибки 5xx.

Типы возвращаемых значений ActionResult могут представлять широкий спектр кодов состояний HTTP. Например, метод GetTodoItem может возвращать два разных значения состояния:

  • Если элемент не совпадает с запрошенным идентификатором, метод возвращает код ошибки 404 NotFound .
  • В противном случае метод возвращает код 200 с телом ответа в формате JSON. При возвращении item возвращается ответ HTTP 200.

Метод PutTodoItem

Изучите метод PutTodoItem:

// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

Страница PutTodoItem аналогична странице PostTodoItem, но использует запрос HTTP PUT. Ответ — 204 (Нет содержимого). Согласно спецификации HTTP, запрос PUT требует, чтобы клиент отправлял всю обновленную сущность, а не только изменения. Чтобы обеспечить поддержку частичных обновлений, используйте HTTP PATCH.

Если возникнет ошибка вызова PutTodoItem, вызовите GET, чтобы в базе данных был один элемент.

Тестирование метода PutTodoItem

В этом примере используется база данных в памяти, которая должна быть инициирована при каждом запуске приложения. При выполнении вызова PUT в базе данных уже должен существовать какой-либо элемент. Для этого перед вызовом PUT выполните вызов GET, чтобы убедиться в наличии такого элемента в базе данных.

Обновите элемент списка дел с идентификатором 1 и присвойте ему имя "feed fish":

  {
    "id":1,
    "name":"feed fish",
    "isComplete":true
  }

На следующем рисунке показан процесс обновления Postman:

Postman console showing 204 (No Content) response

Метод DeleteTodoItem

Изучите метод DeleteTodoItem:

// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<ActionResult<TodoItem>> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return todoItem;
}

Тестирование метода DeleteTodoItem

Удалите элемент списка дел с помощью Postman:

  • Укажите метод DELETE.
  • Укажите URI удаляемого объекта (например, https://localhost:5001/api/todoitems/1).
  • Выберите Отправить.

Предотвращение избыточной публикации

В настоящее время пример приложения предоставляет весь объект TodoItem. Рабочие приложения обычно ограничивают вводимые данные и возвращают их с помощью подмножества модели. Это связано с несколькими причинами, и безопасность является основной. Подмножество модели обычно называется объектом передачи данных (DTO), моделью ввода или моделью представления. В этой статье используется DTO.

DTO можно использовать для следующего:

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

Чтобы продемонстрировать подход с применением DTO, обновите класс TodoItem, включив в него поле секрета:

public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
    public string Secret { get; set; }
}

Поле секрета должно быть скрыто в этом приложении, однако административное приложение может отобразить его.

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

Создайте модель DTO:

public class TodoItemDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

Обновите TodoItemsController для использования TodoItemDTO:

    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
    {
        return await _context.TodoItems
            .Select(x => ItemToDTO(x))
            .ToListAsync();
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        return ItemToDTO(todoItem);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
    {
        if (id != todoItemDTO.Id)
        {
            return BadRequest();
        }

        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        todoItem.Name = todoItemDTO.Name;
        todoItem.IsComplete = todoItemDTO.IsComplete;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
        {
            return NotFound();
        }

        return NoContent();
    }

    [HttpPost]
    public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
    {
        var todoItem = new TodoItem
        {
            IsComplete = todoItemDTO.IsComplete,
            Name = todoItemDTO.Name
        };

        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();

        return CreatedAtAction(
            nameof(GetTodoItem),
            new { id = todoItem.Id },
            ItemToDTO(todoItem));
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        _context.TodoItems.Remove(todoItem);
        await _context.SaveChangesAsync();

        return NoContent();
    }

    private bool TodoItemExists(long id) =>
         _context.TodoItems.Any(e => e.Id == id);

    private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
        new TodoItemDTO
        {
            Id = todoItem.Id,
            Name = todoItem.Name,
            IsComplete = todoItem.IsComplete
        };       
}

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

Вызов веб-API с помощью JavaScript

См . руководство. Вызов веб-API ASP.NET Core с помощью JavaScript.

Добавление поддержки аутентификации в веб-API

ASP.NET Core Identity позволяет использовать функцию входа в пользовательском интерфейсе для веб-приложений ASP.NET Core. Чтобы защитить веб-API и одностраничные приложения, используйте один из следующих способов:

Duende Identity Server — это платформа OpenID Connect и OAuth 2.0 для ASP.NET Core. Duende Identity Server включает следующие функции безопасности:

  • Проверка подлинности как услуга (AaaS)
  • Единый вход (SSO) для нескольких типов приложений
  • Контроль доступа для API
  • Шлюз федерации

Внимание

Компания Duende Software может потребовать лицензионный сбор за использование Duende IdentityServer в рабочей среде. Дополнительные сведения см. в статье Миграция с ASP.NET Core 5.0 на 6.0.

Дополнительные сведения см. в документации по Duende Identity Server (на веб-сайте ПО Duende).

Публикация в Azure

Сведения о развертывании в Azure см. в разделе Краткое руководство. Развертывание веб-приложения ASP.NET.

Дополнительные ресурсы

Просмотреть или скачать пример кода для этого учебника. См. раздел Практическое руководство. Скачивание файла.

Дополнительные сведения см. на следующих ресурсах: