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


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

Note

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

Warning

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

Important

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

В текущей версии смотрите статью о версии .NET 9.

By Tim Deschryver and Rick Anderson

В этом руководстве описаны основы создания веб-API на основе контроллера, использующего базу данных. Another approach to creating APIs in ASP.NET Core is to create minimal APIs. For help with choosing between minimal APIs and controller-based APIs, see APIs overview. См. Руководство: Создание минимального API с помощью ASP.NET Core для получения инструкции по созданию минимального API.

Overview

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

API Description Request body Response body
GET /api/todoitems Получение всех элементов задач None Массив задач
GET /api/todoitems/{id} Получение элемента по идентификатору None To-do item
POST /api/todoitems Добавление нового элемента To-do item To-do item
PUT /api/todoitems/{id} Обновление существующего элемента To-do item None
DELETE /api/todoitems/{id}     Удаление элемента None None

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

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

Prerequisites

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

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

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

  • From the File menu, select New>Project.
  • Enter Web API in the search box.
  • Выберите шаблон Веб-API ASP.NET Core и нажмите кнопку Далее.
  • В диалоговом окне Настроить новый проект присвойте проекту имя TodoApi и нажмите кнопку Далее.
  • In the Additional information dialog:
    • Confirm the Framework is .NET 9.0 (Standard Term Support).
    • Убедитесь, что установлен флажок для включения поддержки OpenAPI.
    • Убедитесь, что установлен флажок для Использовать контроллеры (снимите флажок, чтобы использовать минимальные API).
    • Select Create.

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

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

  • From the Tools menu, select NuGet Package Manager > Manage NuGet Packages for Solution.
  • Select the Browse tab.
  • Enter Microsoft.EntityFrameworkCore.InMemory in the search box, and then select Microsoft.EntityFrameworkCore.InMemory.
  • Select the Project checkbox in the right pane and then select Install.

Note

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

Запуск проекта

The project template creates a WeatherForecast API with support for OpenAPI.

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

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

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

Select Yes if you trust the IIS Express SSL certificate.

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

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

Select Yes if you agree to trust the development certificate.

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

Visual Studio запускает окно терминала и отображает URL-адрес работающего приложения. API размещается в https://localhost:<port>, где <port> является случайным образом выбранным номером порта при создании проекта.

...
info: Microsoft.Hosting.Lifetime[14]
   Now listening on: https://localhost:7260
info: Microsoft.Hosting.Lifetime[14]
   Now listening on: http://localhost:7261
info: Microsoft.Hosting.Lifetime[0]
   Application started. Press Ctrl+C to shut down.
...

Ctrl+click the HTTPS URL in the output to test the web app in a browser. Конечная точка https://localhost:<port>отсутствует, поэтому браузер возвращает HTTP 404 Not Found.

Добавьте /weatherforecast в URL-адрес для тестирования API WeatherForecast. В браузере отображается JSON, аналогичный следующему примеру:

[
    {
        "date": "2025-07-16",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2025-07-17",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2025-07-18",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2025-07-19",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2025-07-20",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

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

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

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

A model is a set of classes that represent the data that the app manages. Для этого приложения используется класс модели TodoItem.

  • In Solution Explorer, right-click the project. Select Add>New Folder. Назовите папку Models.
  • Right-click the Models folder and select Add>Class. Name the class TodoItem and select Add.
  • Замените код шаблона следующим кодом:
namespace TodoApi.Models;

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

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

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

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

The database context is the main class that coordinates Entity Framework functionality for a data model. Этот класс является производным от класса Microsoft.EntityFrameworkCore.DbContext.

  • Right-click the Models folder and select Add>Class. Name the class TodoContext and click Add.

  • Введите следующий код:

    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.AddOpenApi();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

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

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

Создание каркаса контроллера

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

  • Select Add>New Scaffolded Item.

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

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

    • Select TodoItem (TodoApi.Models) in the Model class.
    • Select TodoContext (TodoApi.Models) in the Data context class.
    • Select Add.

    If the scaffolding operation fails, select Add to try scaffolding a second time.

На этом шаге в проект добавляются Microsoft.VisualStudio.Web.CodeGeneration.Design и Microsoft.EntityFrameworkCore.Tools пакеты NuGet. Эти пакеты необходимы для создания каркаса.

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

  • Пометьте этот класс атрибутом [ApiController]. Этот атрибут указывает, что контроллер отвечает на запросы веб-API. Дополнительные сведения о поведении, которое реализует этот атрибут, см. в статье Создание веб-API с помощью ASP.NET Core.
  • Использует DI для инъекции контекста базы данных (TodoContext) в контроллер. The database context is used in each of the CRUD methods in the controller.

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

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

When the [action] token isn't in the route template, the action name (method name) isn't included in the endpoint. То есть имя связанного метода действия не используется в соответствующем маршруте.

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

Update the return statement in the PostTodoItem to use the nameof operator:

[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 ответ для метода, создающего новый ресурс на сервере.
  • Adds a Location header to the response. The Location header specifies the URI of the newly created to-do item. Дополнительные сведения см. в разделе 10.2.2 201 «Создан ресурс».
  • Указывает действие GetTodoItem для создания URI заголовка Location. Ключевое слово nameof C# используется для предотвращения жесткого программирования имени действия в вызове CreatedAtAction.

Test PostTodoItem

  • Select View>Other Windows>Endpoints Explorer.

  • Right-click the POST endpoint and select Generate request.

    Контекстное меню обозревателя конечных точек, в котором выделен пункт меню

    Новый файл создается в папке TodoApi.httpпроекта с содержимым, аналогичным следующему примеру:

    @TodoApi_HostAddress = https://localhost:49738
    
    POST {{TodoApi_HostAddress}}/api/todoitems
    Content-Type: application/json
    
    {
      //TodoItem
    }
    
    ###
    
    • Первая строка создает переменную, используемую для всех конечных точек.
    • Следующая строка определяет запрос POST.
    • Строки после строки запроса POST определяют заголовки и заполнитель для текста запроса.
    • Тройной хэштег (###) — это разделитель запросов: то, что следует после него, предназначено для другого запроса.
  • Запрос POST ожидает TodoItem. Чтобы определить задачу, замените комментарий //TodoItem следующим JSON:

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

    Теперь файл TodoApi.http должен выглядеть следующим образом, но с номером порта:

    @TodoApi_HostAddress = https://localhost:7260
    
    Post {{TodoApi_HostAddress}}/api/todoitems
    Content-Type: application/json
    
    {
      "name": "walk dog",
      "isComplete": true
    }
    
    ###
    
  • Запустите приложение.

  • Select the Send request link that is above the POST request line.

    Окно файла .http с выделенной ссылкой для выполнения.

    The POST request is sent to the app and the response is displayed in the Response pane.

    Окно HTTP-файла с ответом из запроса POST.

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

Test the app by calling the GET endpoints from a browser or by using Endpoints Explorer. The following steps are for Endpoints Explorer.

  • In Endpoints Explorer, right-click the first GET endpoint, and select Generate request.

    В файл добавляется следующее содержимое TodoApi.http :

    GET {{TodoApi_HostAddress}}/api/todoitems
    
    ###
    
  • Select the Send request link that is above the new GET request line.

    The GET request is sent to the app and the response is displayed in the Response pane.

  • Текст ответа аналогичен следующему json:

    [
      {
        "id": 1,
        "name": "walk dog",
        "isComplete": true
      }
    ]
    
  • In Endpoints Explorer, right-click the /api/todoitems/{id}GET endpoint and select Generate request. В файл добавляется следующее содержимое TodoApi.http :

    @id=0
    GET {{TodoApi_HostAddress}}/api/todoitems/{{id}}
    
    ###
    
  • Назначьте {@id} на 1 (вместо 0).

  • Select the Send request link that is above the new GET request line.

    The GET request is sent to the app and the response is displayed in the Response pane.

  • Текст ответа аналогичен следующему json:

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

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

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

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

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

Follow the POST instructions to add another todo item, and then test the /api/todoitems route using Swagger.

Это приложение использует базу данных в оперативной памяти. Если приложение остановлено и запущено, предыдущий запрос GET не возвращает никаких данных. If no data is returned, POST data to the app.

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

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

  • Начните с строки шаблона в атрибуте Route контроллера.

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Замените [controller] именем контроллера (по соглашению это имя класса контроллера без суффикса "Controller"). For this sample, the controller class name is TodoItemsController, so the controller name is "TodoItems". ASP.NET Core routing is case insensitive.

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

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

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

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

    return todoItem;
}

Return values

Тип возвращаемого значения для методов GetTodoItems и GetTodoItem — ActionResult<T>. ASP.NET Core automatically serializes the object to JSON and writes the JSON into the body of the response message. The response code for this return type is 200 OK, assuming there are no unhandled exceptions. Необработанные исключения преобразуются в ошибки 5xx.

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

  • If no item matches the requested ID, the method returns a 404 statusNotFound error code.
  • В противном случае метод возвращает код 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 запрос требует от клиента отправки всей обновленной сущности, а не только изменений. To support partial updates, use HTTP PATCH.

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

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

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

  • In Endpoints Explorer, right-click the PUT endpoint, and select Generate request.

    В файл добавляется следующее содержимое TodoApi.http :

    PUT {{TodoApi_HostAddress}}/api/todoitems/{{id}}
    Content-Type: application/json
    
    {
      //TodoItem
    }
    
    ###
    
  • В строке запроса PUT замените {{id}} на 1.

  • Замените заполнитель //TodoItem следующими строками:

    PUT {{TodoApi_HostAddress}}/api/todoitems/1
    Content-Type: application/json
    
    {
      "id": 1,
      "name": "feed fish",
      "isComplete": false
    }
    
  • Select the Send request link that is above the new PUT request line.

    The PUT request is sent to the app and the response is displayed in the Response pane. Текст ответа пуст, а код состояния — 204.

Метод 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

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

  • In Endpoints Explorer, right-click the DELETE endpoint and select Generate request.

    Запрос DELETE добавляется в TodoApi.http.

  • Замените {{id}} строку запроса DELETE на 1. Запрос DELETE должен выглядеть следующим образом:

    DELETE {{TodoApi_HostAddress}}/api/todoitems/{{id}}
    
    ###
    
  • Select the Send request link for the DELETE request.

    The DELETE request is sent to the app and the response is displayed in the Response pane. Текст ответа пуст, а код состояния — 204.

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

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

  • http-repl
  • curl. Swagger использует curl и показывает команды curl, которые он отправляет.
  • Fiddler

Prevent over-posting

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

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

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

Чтобы продемонстрировать подход с применением 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; }
}

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

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

Create a DTO model in a Models/TodoItemsDTO.cs file:

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

Смотрите Видео: Серия для начинающих: Web API.

Шаблоны корпоративных веб-приложений

Рекомендации по созданию надежного, безопасного, производительного, тестового и масштабируемого приложения ASP.NET Core см. в шаблонах веб-приложений Enterprise. Полный пример веб-приложения с высоким качеством рабочей среды, реализующий шаблоны, доступен.

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

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

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

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

Important

Duende Software might require you to pay a license fee for production use of Duende Identity Server. Дополнительные сведения см. в статье "Миграция из ASP.NET Core в .NET 5 в .NET 6".

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

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

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

Additional resources

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

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

В этом руководстве описаны основы создания веб-API на основе контроллера, использующего базу данных. Another approach to creating APIs in ASP.NET Core is to create minimal APIs. For help with choosing between minimal APIs and controller-based APIs, see APIs overview. См. Руководство: Создание минимального API с помощью ASP.NET Core для получения инструкции по созданию минимального API.

Overview

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

API Description Request body Response body
GET /api/todoitems Получение всех элементов задач None Массив задач
GET /api/todoitems/{id} Получение элемента по идентификатору None To-do item
POST /api/todoitems Добавление нового элемента To-do item To-do item
PUT /api/todoitems/{id} Обновление существующего элемента To-do item None
DELETE /api/todoitems/{id}     Удаление элемента None None

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

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

Prerequisites

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

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

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

  • From the File menu, select New>Project.
  • Enter Web API in the search box.
  • Выберите шаблон Веб-API ASP.NET Core и нажмите кнопку Далее.
  • В диалоговом окне Настроить новый проект присвойте проекту имя TodoApi и нажмите кнопку Далее.
  • In the Additional information dialog:
    • Confirm the Framework is .NET 8.0 (Long Term Support).
    • Убедитесь, что флажок Use controllers (uncheck to use minimal APIs) (Использовать контроллеры (снимите этот флажок для использования минимальных API)) установлен.
    • Убедитесь, что установлен флажок для включения поддержки OpenAPI.
    • Select Create.

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

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

  • From the Tools menu, select NuGet Package Manager > Manage NuGet Packages for Solution.
  • Select the Browse tab.
  • Enter Microsoft.EntityFrameworkCore.InMemory in the search box, and then select Microsoft.EntityFrameworkCore.InMemory.
  • Select the Project checkbox in the right pane and then select Install.

Note

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

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

The project template creates a WeatherForecast API with support for Swagger.

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

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

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

Select Yes if you trust the IIS Express SSL certificate.

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

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

Select Yes if you agree to trust the development certificate.

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

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

Откроется страница Swagger /swagger/index.html. Select GET>Try it out>Execute. На странице отобразятся:

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

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

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

Copy and paste the Request URL in the browser: 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"
    }
]

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

A model is a set of classes that represent the data that the app manages. Для этого приложения используется класс модели TodoItem.

  • In Solution Explorer, right-click the project. Select Add>New Folder. Назовите папку Models.
  • Right-click the Models folder and select Add>Class. Name the class TodoItem and select Add.
  • Замените код шаблона следующим кодом:
namespace TodoApi.Models;

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

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

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

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

The database context is the main class that coordinates Entity Framework functionality for a data model. Этот класс является производным от класса Microsoft.EntityFrameworkCore.DbContext.

  • Right-click the Models folder and select Add>Class. Name the class TodoContext and click Add.
  • Введите следующий код:

    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.

  • Select Add>New Scaffolded Item.

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

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

    • Select TodoItem (TodoApi.Models) in the Model class.
    • Select TodoContext (TodoApi.Models) in the Data context class.
    • Select Add.

    If the scaffolding operation fails, select Add to try scaffolding a second time.

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

  • Пометьте этот класс атрибутом [ApiController]. Этот атрибут указывает, что контроллер отвечает на запросы веб-API. Дополнительные сведения о поведении, которое реализует этот атрибут, см. в статье Создание веб-API с помощью ASP.NET Core.
  • Использует DI для инъекции контекста базы данных (TodoContext) в контроллер. The database context is used in each of the CRUD methods in the controller.

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

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

When the [action] token isn't in the route template, the action name (method name) isn't included in the endpoint. То есть имя связанного метода действия не используется в соответствующем маршруте.

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

Update the return statement in the PostTodoItem to use the nameof operator:

[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 ответ для метода, создающего новый ресурс на сервере.
  • Adds a Location header to the response. The Location header specifies the URI of the newly created to-do item. Дополнительные сведения см. в разделе 10.2.2 201 «Создан ресурс».
  • Указывает действие GetTodoItem для создания URI заголовка Location. Ключевое слово nameof C# используется для предотвращения жесткого программирования имени действия в вызове CreatedAtAction.

Test PostTodoItem

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

  • In the Swagger browser window, select POST /api/TodoItems, and then select Try it out.

  • In the Request body input window, update the JSON. For example,

    {
      "name": "walk dog",
      "isComplete": true
    }
    
  • Select Execute

    Swagger POST

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

In the preceding POST, the Swagger UI shows the location header under Response headers. Например, location: https://localhost:7260/api/TodoItems/1. Заголовок расположения отображает универсальный код ресурса (URI) для созданного ресурса.

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

  • In the Swagger browser window, select GET /api/TodoItems/{id}, and then select Try it out.

  • Enter 1 in the id input box, and then select Execute.

    Swagger GET

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

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

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

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

Follow the POST instructions to add another todo item, and then test the /api/todoitems route using Swagger.

Это приложение использует базу данных в оперативной памяти. Если приложение остановлено и запущено, предыдущий запрос GET не возвращает никаких данных. If no data is returned, POST data to the app.

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

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

  • Начните с строки шаблона в атрибуте Route контроллера.

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Замените [controller] именем контроллера (по соглашению это имя класса контроллера без суффикса "Controller"). For this sample, the controller class name is TodoItemsController, so the controller name is "TodoItems". ASP.NET Core routing is case insensitive.

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

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

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

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

    return todoItem;
}

Return values

Тип возвращаемого значения для методов GetTodoItems и GetTodoItem — ActionResult<T>. ASP.NET Core automatically serializes the object to JSON and writes the JSON into the body of the response message. The response code for this return type is 200 OK, assuming there are no unhandled exceptions. Необработанные исключения преобразуются в ошибки 5xx.

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

  • If no item matches the requested ID, the method returns a 404 statusNotFound error code.
  • В противном случае метод возвращает код 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 запрос требует от клиента отправки всей обновленной сущности, а не только изменений. To support partial updates, use HTTP PATCH.

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

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

Используя Swagger UI, нажмите кнопку 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, например:

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

Prevent over-posting

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

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

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

Чтобы продемонстрировать подход с применением 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

Смотрите Видео: Серия для начинающих: Web API.

Шаблоны корпоративных веб-приложений

Рекомендации по созданию надежного, безопасного, производительного, тестового и масштабируемого приложения ASP.NET Core см. в шаблонах веб-приложений Enterprise. Полный пример веб-приложения с высоким качеством рабочей среды, реализующий шаблоны, доступен.

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

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

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

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

Important

Duende Software might require you to pay a license fee for production use of Duende Identity Server. Дополнительные сведения см. в статье "Миграция из ASP.NET Core в .NET 5 в .NET 6".

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

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

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

Additional resources

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

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

В этом руководстве описаны основы создания веб-API на основе контроллера, использующего базу данных. Another approach to creating APIs in ASP.NET Core is to create minimal APIs. For help with choosing between minimal APIs and controller-based APIs, see APIs overview. См. Руководство: Создание минимального API с помощью ASP.NET Core для получения инструкции по созданию минимального API.

Overview

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

API Description Request body Response body
GET /api/todoitems Получение всех элементов задач None Массив задач
GET /api/todoitems/{id} Получение элемента по идентификатору None To-do item
POST /api/todoitems Добавление нового элемента To-do item To-do item
PUT /api/todoitems/{id} Обновление существующего элемента To-do item None
DELETE /api/todoitems/{id}     Удаление элемента None None

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

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

Prerequisites

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

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

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

  • From the File menu, select New>Project.
  • Enter Web API in the search box.
  • Выберите шаблон Веб-API ASP.NET Core и нажмите кнопку Далее.
  • В диалоговом окне Настроить новый проект присвойте проекту имя TodoApi и нажмите кнопку Далее.
  • In the Additional information dialog:
    • Confirm the Framework is .NET 8.0 (Long Term Support).
    • Убедитесь, что флажок Use controllers (uncheck to use minimal APIs) (Использовать контроллеры (снимите этот флажок для использования минимальных API)) установлен.
    • Убедитесь, что установлен флажок для включения поддержки OpenAPI.
    • Select Create.

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

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

  • From the Tools menu, select NuGet Package Manager > Manage NuGet Packages for Solution.
  • Select the Browse tab.
  • Enter Microsoft.EntityFrameworkCore.InMemory in the search box, and then select Microsoft.EntityFrameworkCore.InMemory.
  • Select the Project checkbox in the right pane and then select Install.

Note

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

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

The project template creates a WeatherForecast API with support for Swagger.

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

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

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

Select Yes if you trust the IIS Express SSL certificate.

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

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

Select Yes if you agree to trust the development certificate.

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

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

Откроется страница Swagger /swagger/index.html. Select GET>Try it out>Execute. На странице отобразятся:

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

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

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

Copy and paste the Request URL in the browser: 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"
    }
]

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

A model is a set of classes that represent the data that the app manages. Для этого приложения используется класс модели TodoItem.

  • In Solution Explorer, right-click the project. Select Add>New Folder. Назовите папку Models.
  • Right-click the Models folder and select Add>Class. Name the class TodoItem and select Add.
  • Замените код шаблона следующим кодом:
namespace TodoApi.Models;

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

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

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

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

The database context is the main class that coordinates Entity Framework functionality for a data model. Этот класс является производным от класса Microsoft.EntityFrameworkCore.DbContext.

  • Right-click the Models folder and select Add>Class. Name the class TodoContext and click Add.
  • Введите следующий код:

    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.

  • Select Add>New Scaffolded Item.

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

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

    • Select TodoItem (TodoApi.Models) in the Model class.
    • Select TodoContext (TodoApi.Models) in the Data context class.
    • Select Add.

    If the scaffolding operation fails, select Add to try scaffolding a second time.

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

  • Пометьте этот класс атрибутом [ApiController]. Этот атрибут указывает, что контроллер отвечает на запросы веб-API. Дополнительные сведения о поведении, которое реализует этот атрибут, см. в статье Создание веб-API с помощью ASP.NET Core.
  • Использует DI для инъекции контекста базы данных (TodoContext) в контроллер. The database context is used in each of the CRUD methods in the controller.

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

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

When the [action] token isn't in the route template, the action name (method name) isn't included in the endpoint. То есть имя связанного метода действия не используется в соответствующем маршруте.

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

Update the return statement in the PostTodoItem to use the nameof operator:

[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 ответ для метода, создающего новый ресурс на сервере.
  • Adds a Location header to the response. The Location header specifies the URI of the newly created to-do item. Дополнительные сведения см. в разделе 10.2.2 201 «Создан ресурс».
  • Указывает действие GetTodoItem для создания URI заголовка Location. Ключевое слово nameof C# используется для предотвращения жесткого программирования имени действия в вызове CreatedAtAction.

Test PostTodoItem

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

  • In the Swagger browser window, select POST /api/TodoItems, and then select Try it out.

  • In the Request body input window, update the JSON. For example,

    {
      "name": "walk dog",
      "isComplete": true
    }
    
  • Select Execute

    Swagger POST

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

In the preceding POST, the Swagger UI shows the location header under Response headers. Например, location: https://localhost:7260/api/TodoItems/1. Заголовок расположения отображает универсальный код ресурса (URI) для созданного ресурса.

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

  • In the Swagger browser window, select GET /api/TodoItems/{id}, and then select Try it out.

  • Enter 1 in the id input box, and then select Execute.

    Swagger GET

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

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

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

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

Follow the POST instructions to add another todo item, and then test the /api/todoitems route using Swagger.

Это приложение использует базу данных в оперативной памяти. Если приложение остановлено и запущено, предыдущий запрос GET не возвращает никаких данных. If no data is returned, POST data to the app.

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

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

  • Начните с строки шаблона в атрибуте Route контроллера.

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Замените [controller] именем контроллера (по соглашению это имя класса контроллера без суффикса "Controller"). For this sample, the controller class name is TodoItemsController, so the controller name is "TodoItems". ASP.NET Core routing is case insensitive.

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

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

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

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

    return todoItem;
}

Return values

Тип возвращаемого значения для методов GetTodoItems и GetTodoItem — ActionResult<T>. ASP.NET Core automatically serializes the object to JSON and writes the JSON into the body of the response message. The response code for this return type is 200 OK, assuming there are no unhandled exceptions. Необработанные исключения преобразуются в ошибки 5xx.

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

  • If no item matches the requested ID, the method returns a 404 statusNotFound error code.
  • В противном случае метод возвращает код 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 запрос требует от клиента отправки всей обновленной сущности, а не только изменений. To support partial updates, use HTTP PATCH.

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

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

Используя Swagger UI, нажмите кнопку 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, например:

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

Prevent over-posting

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

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

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

Чтобы продемонстрировать подход с применением 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

Смотрите Видео: Серия для начинающих: Web API.

Шаблоны корпоративных веб-приложений

Рекомендации по созданию надежного, безопасного, производительного, тестового и масштабируемого приложения ASP.NET Core см. в шаблонах веб-приложений Enterprise. Полный пример веб-приложения с высоким качеством рабочей среды, реализующий шаблоны, доступен.

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

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

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

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

Important

Duende Software might require you to pay a license fee for production use of Duende Identity Server. Дополнительные сведения см. в статье "Миграция из ASP.NET Core в .NET 5 в .NET 6".

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

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

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

Additional resources

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

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