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


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

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.

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

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.

Внимание

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

В текущем выпуске см . версию .NET 8 этой статьи.

Авторы: Рик Андерсон (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}     Удаление элемента нет нет

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

Клиент представлен квадратом слева. Он отправляет запрос и получает ответ от приложения (квадрата, нарисованного справа). Внутри квадрата приложения три квадрата обозначают контроллер, модель и уровень доступа к данным. Запрос поступает в контроллер приложения, а операции чтения/записи происходят между контроллером и уровнем доступа к данным. Модель сериализуется и возвращается клиенту в ответе.

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

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

    Рабочие нагрузки установщика VS22

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

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

Этот проект настроен для использования SSL. Вы можете сделать самозаверяющий сертификат, созданный IIS Express, доверенным, чтобы не получать предупреждения SSL в браузере. Сделать SSL-сертификат IIS Express доверенным?

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

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

Диалоговое окно

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

Сведения о доверии к браузеру 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.

  • Выберите Добавить>New Scaffolded Item.

  • Выберите Контроллер 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 может возвращать два разных значения состояния:

  • Если запрошенному идентификатору не соответствует ни один элемент, метод возвращает код ошибки 404 NotFound.
  • В противном случае метод возвращает код 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.

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

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

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