Partilhar via


Tutorial: Criar uma API da Web baseada em controlador com o ASP.NET Core

Note

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Warning

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.

Por Tim Deschryver e Rick Anderson

Este tutorial ensina os conceitos básicos da criação de uma API da Web baseada em controlador que usa um banco de dados. Outra abordagem para criar APIs no ASP.NET Core é criar APIs mínimas. Para obter ajuda com a escolha entre APIs mínimas e APIs baseadas em controlador, consulte Visão geral de APIs. Para obter um tutorial sobre como criar uma API mínima, consulte Tutorial: Criar uma API mínima com o ASP.NET Core.

Overview

Este tutorial cria a seguinte API:

API Description Corpo de solicitação Corpo da resposta
GET /api/todoitems Consiga todos os itens to-do None Lista de tarefas a fazer
GET /api/todoitems/{id} Obter um item pelo ID None Item de tarefa
POST /api/todoitems Adicionar um novo item Item de tarefa Item de tarefa
PUT /api/todoitems/{id} Atualizar um item existente Item de tarefa None
DELETE /api/todoitems/{id}     Excluir um item None None

O diagrama a seguir mostra o design do aplicativo.

O cliente é representado por uma caixa à esquerda. Apresenta um pedido e recebe uma resposta do pedido, uma caixa desenhada à direita. Dentro da caixa de aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação entra no controlador do aplicativo e as operações de leitura/gravação ocorrem entre o controlador e a camada de acesso a dados. O modelo é serializado e retornado ao cliente na resposta.

Prerequisites

Criar um projeto de API Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Digite Web API na caixa de pesquisa.
  • Selecione o modelo ASP.NET Core Web API e selecione Next.
  • Na caixa de diálogo Configurar seu novo projeto, nomeie o projeto como TodoApi e selecione Avançar.
  • No caixa de diálogo de Informações adicionais :
    • Confirme se o Framework é .NET 9.0 (Standard Term Support).
    • Confirme que a caixa de seleção Ativar suporte a OpenAPI está marcada.
    • Confirme se a caixa de seleção Usar controladores (desmarque para usar APIs mínimas) está marcada.
    • Selecione Criar.

Adicionar um pacote NuGet

Um pacote NuGet deve ser adicionado para dar suporte ao banco de dados usado neste tutorial.

  • No menu Ferramentas, selecione Gerenciador de Pacotes NuGet > Gerenciar Pacotes NuGet para Solução.
  • Selecione a guia Procurar.
  • Digite Microsoft.EntityFrameworkCore.InMemory na caixa de pesquisa e selecione Microsoft.EntityFrameworkCore.InMemory.
  • Marque a caixa de seleção Projeto no painel direito e selecione Instalar.

Note

Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos na seção Instalar e gerenciar pacotes em Workflow de utilização de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.

Executar o projeto

O modelo de projeto cria uma WeatherForecast API com suporte para OpenAPI.

Pressione Ctrl+F5 para executar sem o depurador.

Visual Studio exibe a seguinte caixa de diálogo quando um projeto ainda não está configurado para usar SSL:

Este projeto está configurado para usar SSL. Para evitar avisos SSL no navegador, você pode optar por confiar no certificado autoassinado que o IIS Express gerou. Gostaria de confiar no certificado SSL do IIS Express?

Selecione Sim se confiar no certificado SSL do IIS Express.

A seguinte caixa de diálogo é exibida:

Caixa de diálogo de aviso de segurança

Selecione Sim se concordar em confiar no certificado de desenvolvimento.

Para obter informações sobre como confiar no navegador Firefox, consulte o erro de certificado SEC_ERROR_INADEQUATE_KEY_USAGE do Firefox na secção .

O Visual Studio inicia uma janela de terminal e exibe a URL do aplicativo em execução. A API é hospedada em https://localhost:<port>, onde <port> é um número de porta escolhido aleatoriamente definido na criação do projeto.

...
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+clique URL HTTPS na saída para testar o aplicativo Web em um navegador. Não há nenhum endpoint no https://localhost:<port>, então o navegador retorna HTTP 404 Não Encontrado.

Anexe /weatherforecast ao URL para testar a API WeatherForecast. O navegador exibe JSON semelhante ao exemplo a seguir:

[
    {
        "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"
    }
]

Testar o projeto

Este tutorial usa Endpoints Explorer e arquivos .http para testar a API.

Adicionar uma classe de modelo

Um modelo é um conjunto de classes que representam os dados que o aplicativo gerencia. O modelo para este aplicativo é a TodoItem classe.

  • No Gerenciador de Soluções , clique com o botão direito do mouse no projeto. Selecione Adicionar>Nova Pasta. Dê um nome à pasta Models.
  • Clique com o botão direito do mouse na Models pasta e selecione Adicionar>classe. Nomeie a classe TodoItem e selecione Adicionar.
  • Substitua o código do modelo pelo seguinte:
namespace TodoApi.Models;

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

A Id propriedade funciona como a chave exclusiva em um banco de dados relacional.

As classes de modelo podem ir para qualquer lugar no projeto, mas a Models pasta é usada por convenção.

Adicionar um contexto de banco de dados

O contexto do banco de dados é a classe principal que coordena a funcionalidade do Entity Framework para um modelo de dados. Essa classe é criada derivando da classe Microsoft.EntityFrameworkCore.DbContext.

  • Clique com o botão direito do mouse na Models pasta e selecione Adicionar>classe. Nomeie a classe TodoContext e clique em Adicionar.

  • Insira o seguinte código:

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

Registrar o contexto do banco de dados

No ASP.NET Core, serviços como o contexto de banco de dados devem ser registrados com o contêiner de injeção de dependência (DI ). O contêiner fornece o serviço aos controladores.

Atualize Program.cs com o seguinte código destacado:

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();

O código anterior:

  • Acrescenta using diretivas.
  • Adiciona o contexto do banco de dados ao contêiner DI.
  • Especifica que o contexto do banco de dados usará um banco de dados na memória.

Andaime um controlador

  • Clique com o botão direito do rato na pasta Controllers.

  • Selecione Adicionar>New Scaffolded Item.

  • Selecione Controlador de API com ações, usando o Entity Framework e, em seguida, selecione Adicionar.

  • Na caixa de diálogo Adicionar controlador de API com ações, usando o Entity Framework :

    • Selecione TodoItem (TodoApi.Models) na classe Model.
    • Selecione TodoContext (TodoApi.Models) na classe de contexto Data.
    • Selecione Adicionar.

    Se a operação de andaime falhar, selecione Adicionar para tentar andaimes uma segunda vez.

Esta etapa adiciona os Microsoft.VisualStudio.Web.CodeGeneration.Design pacotes e Microsoft.EntityFrameworkCore.Tools NuGet ao projeto. Estas embalagens são necessárias para andaimes.

O código gerado:

  • Marca a classe com o [ApiController] atributo. Esse atributo indica que o controlador responde a solicitações de API da Web. Para obter informações sobre comportamentos específicos habilitados pelo atributo, consulte Criar APIs da Web com ASP.NET Core.
  • Usa DI para injetar o contexto da base de dados (TodoContext) no controlador. O contexto do banco de dados é usado em cada um dos métodos CRUD no controlador.

Os modelos ASP.NET Core para:

  • Os controladores com views incluem [action] no modelo de rota.
  • Os controladores de API não incluem [action] no modelo de rota.

Quando o [action] token não está no modelo de rota, o nome da ação (nome do método) não é incluído no ponto de extremidade. Ou seja, o nome do método associado à ação não é usado na rota correspondente.

Atualizar o método de criação PostTodoItem

Atualize a instrução de retorno no PostTodoItem para usar o operador 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);
}

O código anterior é um HTTP POST método, conforme indicado pelo [HttpPost] atributo. O método obtém o valor do TodoItem do corpo da solicitação HTTP.

Para obter mais informações, consulte Roteamento de atributos com atributos Http[Verb].

O método CreatedAtAction:

  • Retorna um código de status HTTP 201 se for bem-sucedido. HTTP 201 é a resposta padrão para um HTTP POST método que cria um novo recurso no servidor.
  • Adiciona um cabeçalho Location à resposta. O Location cabeçalho especifica o URI do item to-do recém-criado. Para obter mais informações, consulte 10.2.2 201 Criado.
  • Faz referência à ação GetTodoItem para criar o URI do cabeçalho Location. A palavra-chave C# nameof é usada para evitar a codificação direta do nome da ação na chamada CreatedAtAction.

Teste PostTodoItem

  • Selecione Ver>Outras Janelas>Explorador de Endpoints.

  • Clique com o botão direito em POST endpoint e selecione Gerar solicitação.

    menu de contexto do Explorador de Endpoints realçando o item de menu 'Gerar Solicitação'.

    Um novo arquivo é criado na pasta do projeto chamada TodoApi.http, com conteúdo semelhante ao exemplo a seguir:

    @TodoApi_HostAddress = https://localhost:49738
    
    POST {{TodoApi_HostAddress}}/api/todoitems
    Content-Type: application/json
    
    {
      //TodoItem
    }
    
    ###
    
    • A primeira linha cria uma variável que é usada para todos os pontos de extremidade.
    • A próxima linha define uma solicitação POST.
    • As linhas após a linha de solicitação POST definem os cabeçalhos e um espaço reservado para o corpo da solicitação.
    • A linha de três hashtags (###) é um delimitador de solicitação: o que vem depois é para uma solicitação diferente.
  • O pedido POST espera um TodoItem. Para definir o todo, substitua o //TodoItem comentário pelo seguinte JSON:

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

    O ficheiro TodoApi.http agora deve assemelhar-se ao exemplo abaixo, mas com o seu número de porta:

    @TodoApi_HostAddress = https://localhost:7260
    
    Post {{TodoApi_HostAddress}}/api/todoitems
    Content-Type: application/json
    
    {
      "name": "walk dog",
      "isComplete": true
    }
    
    ###
    
  • Execute o aplicativo.

  • Selecione o link Enviar solicitação que está acima da linha de solicitação POST.

    janela do arquivo .http com o link de execução realçado.

    A solicitação POST é enviada para a aplicação e a resposta é exibida no painel de Resposta .

    janela de arquivo .http com resposta da solicitação POST.

Testar o URI do cabeçalho de localização

Teste a aplicação chamando os endpoints GET de um navegador ou usando o Explorador de Endpoints. As etapas a seguir são para Endpoints Explorer.

  • No Explorador de Pontos de Extremidade, clique com o botão direito do rato no primeiro ponto de extremidade GET e selecione Gerar pedido.

    O seguinte conteúdo é adicionado ao arquivo TodoApi.http:

    GET {{TodoApi_HostAddress}}/api/todoitems
    
    ###
    
  • Selecione o link Enviar solicitação que está acima da nova GET linha de solicitação.

    A solicitação GET é enviada para o aplicativo e a resposta é exibida no painel Resposta .

  • O corpo da resposta é semelhante ao seguinte JSON:

    [
      {
        "id": 1,
        "name": "walk dog",
        "isComplete": true
      }
    ]
    
  • No Explorador de Endpoints, clique com o botão direito do rato no /api/todoitems/{id} endpoint GET e selecionar Gerar pedido. O seguinte conteúdo é adicionado ao arquivo TodoApi.http:

    @id=0
    GET {{TodoApi_HostAddress}}/api/todoitems/{{id}}
    
    ###
    
  • Atribuir {@id} a 1 (em vez de 0).

  • Selecione o link Enviar solicitação que está acima da nova linha de solicitação GET.

    A solicitação GET é enviada para o aplicativo e a resposta é exibida no painel Resposta .

  • O corpo da resposta é semelhante ao seguinte JSON:

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

Examine os métodos GET

Estão implementados dois endpoints GET.

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

A seção anterior mostrou um exemplo da /api/todoitems/{id} rota.

Siga as instruções do POST para adicionar outro item todo e teste a rota usando o /api/todoitems Swagger.

Este aplicativo usa um banco de dados na memória. Se o aplicativo for interrompido e iniciado, a solicitação GET anterior não retornará nenhum dado. Se nenhum dado for retornado, envie dados POST para a aplicação.

Roteamento e caminhos de URL

O [HttpGet] atributo denota um método que responde a uma HTTP GET solicitação. O caminho da URL para cada método é construído da seguinte maneira:

  • Comece com a cadeia de caracteres do modelo no atributo do controlador Route.

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Substitua [controller] pelo nome do controlador, que por convenção é o nome da classe do controlador menos o sufixo "Controller". Para este exemplo, o nome da classe do controlador é TodoItemsController, portanto, o nome do controlador é "TodoItems". ASP.NET O roteamento principal não diferencia maiúsculas de minúsculas.

  • Se o [HttpGet] atributo tiver um modelo de rota (por exemplo, [HttpGet("products")]), acrescente-o ao caminho. Este exemplo não usa um modelo. Para obter mais informações, consulte Roteamento de atributos com atributos Http[Verb].

No método a seguir GetTodoItem , "{id}" é uma variável de espaço reservado para o identificador exclusivo do item to-do. Quando GetTodoItem é invocado, o valor de "{id}" na URL é fornecido para o método em seu id parâmetro.

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

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

    return todoItem;
}

Valores de retorno

O tipo de retorno dos métodos GetTodoItems e GetTodoItem é o tipo ActionResult<T>. ASP.NET Core serializa automaticamente o objeto para JSON e grava o JSON no corpo da mensagem de resposta. O código de resposta para esse tipo de retorno é 200 OK, supondo que não haja exceções não tratadas. As exceções não tratadas são traduzidas em erros 5xx.

ActionResult os tipos de retorno podem representar uma ampla gama de códigos de status HTTP. Por exemplo, GetTodoItem pode retornar dois valores de status diferentes:

  • Se nenhum item corresponder à ID solicitada, o método retornará um 404 statusNotFound código de erro.
  • Caso contrário, o método retorna 200 com um corpo de resposta JSON. O retorno item resulta em uma HTTP 200 resposta.

O método PutTodoItem

Examine o PutTodoItem método:

[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 é semelhante ao PostTodoItem, exceto que usa HTTP PUT. A resposta é 204 (Sem Conteúdo). De acordo com a especificação HTTP, uma PUT solicitação requer que o cliente envie toda a entidade atualizada, não apenas as alterações. Para suportar atualizações parciais, use HTTP PATCH.

Testar o método PutTodoItem

Este exemplo usa um banco de dados na memória que deve ser inicializado sempre que o aplicativo é iniciado. Deve haver um item no banco de dados antes de fazer uma chamada PUT. Chame GET para garantir que há um item no banco de dados antes de fazer uma chamada PUT.

Use o PUT método para atualizar o TodoItem que tem Id = 1 e defina seu nome como "feed fish". Observe que a resposta é HTTP 204 No Content.

  • No Explorador de Pontos de Extremidade, clique com o botão direito do rato no ponto de extremidade PUT e selecione Gerar pedido.

    O seguinte conteúdo é adicionado ao arquivo TodoApi.http:

    PUT {{TodoApi_HostAddress}}/api/todoitems/{{id}}
    Content-Type: application/json
    
    {
      //TodoItem
    }
    
    ###
    
  • Na linha de solicitação PUT, substitua {{id}} por 1.

  • Substitua o marcador de posição //TodoItem pelas seguintes linhas:

    PUT {{TodoApi_HostAddress}}/api/todoitems/1
    Content-Type: application/json
    
    {
      "id": 1,
      "name": "feed fish",
      "isComplete": false
    }
    
  • Selecione o link Enviar solicitação que está acima da nova linha de solicitação PUT.

    A solicitação PUT é enviada para o aplicativo e a resposta é exibida no painel Resposta . O corpo da resposta está vazio e o código de status é 204.

O método DeleteTodoItem

Examine o DeleteTodoItem método:

[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();
}

Testar o método DeleteTodoItem

Use o DELETE método para excluir o TodoItem que tem Id = 1. Observe que a resposta é HTTP 204 No Content.

  • No Endpoints Explorer, clique com o botão direito no endpoint DELETE e selecione Gerar solicitação.

    Uma solicitação DELETE é adicionada ao TodoApi.http.

  • Substitua {{id}} na linha de solicitação DELETE por 1. A solicitação DELETE deve se parecer com o exemplo a seguir:

    DELETE {{TodoApi_HostAddress}}/api/todoitems/{{id}}
    
    ###
    
  • Selecione o link Enviar solicitação para a solicitação DELETE.

    A solicitação DELETE é enviada para o aplicativo e a resposta é exibida no painel Resposta . O corpo da resposta está vazio e o código de status é 204.

Teste com outras ferramentas

Existem muitas outras ferramentas que podem ser usadas para testar APIs da Web, por exemplo:

Evitar a sobrepostagem

Atualmente, o aplicativo de exemplo expõe todo o objeto TodoItem. Os aplicativos de produção normalmente limitam os dados que são inseridos e retornados usando um subconjunto do modelo. Há várias razões por trás disso, e a segurança é uma das principais. O subconjunto de um modelo é geralmente referido como um objeto de transferência de dados (DTO), modelo de entrada ou modelo de exibição. DTO é usado neste tutorial.

Um DTO pode ser utilizado para:

  • Evite a sobrepublicação.
  • Oculte propriedades que os clientes não devem visualizar.
  • Omita algumas propriedades para reduzir o tamanho da carga útil.
  • Nivelar gráficos de objetos que contêm objetos aninhados. Gráficos de objetos achatados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem DTO, atualize a classe TodoItem para incluir um campo secreto:

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; }
}

O campo secreto precisa ser oculto deste aplicativo, mas um aplicativo administrativo pode optar por expô-lo.

Verifique se você pode postar e obter o campo secreto.

Crie um modelo DTO em um arquivo Models/TodoItemsDTO.cs :

namespace TodoApi.Models;

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

Atualize o TodoItemsController para usar 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
       };
}

Verifique se você não pode postar ou obter o campo secreto.

Chamar a API da Web com JavaScript

Consulte Tutorial: Chamar uma API da Web ASP.NET Core com JavaScript.

Série de vídeos da API Web

Veja Vídeo: Série para Iniciantes em APIs da Web.

Padrões de aplicativos Web corporativos

Para obter orientação sobre como criar um aplicativo ASP.NET Core confiável, seguro, com desempenho, testável e escalável, consulte Padrões de aplicativos Web corporativos. Está disponível um aplicativo Web de exemplo completo com qualidade de produção que implementa os padrões.

Adicionar suporte de autenticação a uma API da Web

O ASP.NET Core Identity adiciona a funcionalidade de login da interface do usuário (UI) aos aplicativos Web ASP.NET Core. Para proteger APIs da Web e SPAs, use uma das seguintes opções:

O Duende Identity Server é uma framework para OpenID Connect e OAuth 2.0 no ASP.NET Core. O Duende Server habilita os seguintes recursos de Identity segurança:

  • Autenticação como serviço (AaaS)
  • Logon único/desligamento (SSO) em vários tipos de aplicativos
  • Controle de acesso para APIs
  • Gateway de Federação

Important

Duende Software pode exigir que você pague uma taxa de licença para uso de produção do Duende Identity Server. Para obter mais informações, consulte Migrar do ASP.NET Core no .NET 5 para o .NET 6.

Para obter mais informações, consulte a documentação do Duende Identity Server (site da Duende Software).

Publicar no Azure

Para obter informações sobre como implantar no Azure, consulte Guia de início rápido: implantar um aplicativo Web ASP.NET.

Recursos adicionais

Veja ou descarregue o código de exemplo para este tutorial. Veja como fazer o download.

Para obter mais informações, consulte os seguintes recursos:

Este tutorial ensina os conceitos básicos da criação de uma API da Web baseada em controlador que usa um banco de dados. Outra abordagem para criar APIs no ASP.NET Core é criar APIs mínimas. Para obter ajuda com a escolha entre APIs mínimas e APIs baseadas em controlador, consulte Visão geral de APIs. Para obter um tutorial sobre como criar uma API mínima, consulte Tutorial: Criar uma API mínima com o ASP.NET Core.

Overview

Este tutorial cria a seguinte API:

API Description Corpo de solicitação Corpo da resposta
GET /api/todoitems Consiga todos os itens to-do None Lista de tarefas a fazer
GET /api/todoitems/{id} Obter um item pelo ID None Item de tarefa
POST /api/todoitems Adicionar um novo item Item de tarefa Item de tarefa
PUT /api/todoitems/{id} Atualizar um item existente Item de tarefa None
DELETE /api/todoitems/{id}     Excluir um item None None

O diagrama a seguir mostra o design do aplicativo.

O cliente é representado por uma caixa à esquerda. Apresenta um pedido e recebe uma resposta do pedido, uma caixa desenhada à direita. Dentro da caixa de aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação entra no controlador do aplicativo e as operações de leitura/gravação ocorrem entre o controlador e a camada de acesso a dados. O modelo é serializado e retornado ao cliente na resposta.

Prerequisites

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Digite Web API na caixa de pesquisa.
  • Selecione o modelo ASP.NET Core Web API e selecione Next.
  • Na caixa de diálogo Configurar seu novo projeto, nomeie o projeto como TodoApi e selecione Avançar.
  • No caixa de diálogo de Informações adicionais :
    • Confirme se o Framework é .NET 8.0 (Suporte de Longo Prazo).
    • Confirme que a caixa de seleção Usar controladores (desmarque para usar APIs mínimas) está marcada.
    • Confirme que a caixa de seleção Ativar suporte a OpenAPI está marcada.
    • Selecione Criar.

Adicionar um pacote NuGet

Um pacote NuGet deve ser adicionado para dar suporte ao banco de dados usado neste tutorial.

  • No menu Ferramentas, selecione Gerenciador de Pacotes NuGet > Gerenciar Pacotes NuGet para Solução.
  • Selecione a guia Procurar.
  • Digite Microsoft.EntityFrameworkCore.InMemory na caixa de pesquisa e selecione Microsoft.EntityFrameworkCore.InMemory.
  • Marque a caixa de seleção Projeto no painel direito e selecione Instalar.

Note

Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos na seção Instalar e gerenciar pacotes em Workflow de utilização de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.

Testar o projeto

O modelo de projeto cria uma WeatherForecast API com suporte para Swagger.

Pressione Ctrl+F5 para executar sem o depurador.

Visual Studio exibe a seguinte caixa de diálogo quando um projeto ainda não está configurado para usar SSL:

Este projeto está configurado para usar SSL. Para evitar avisos SSL no navegador, você pode optar por confiar no certificado autoassinado que o IIS Express gerou. Gostaria de confiar no certificado SSL do IIS Express?

Selecione Sim se confiar no certificado SSL do IIS Express.

A seguinte caixa de diálogo é exibida:

Caixa de diálogo de aviso de segurança

Selecione Sim se concordar em confiar no certificado de desenvolvimento.

Para obter informações sobre como confiar no navegador Firefox, consulte o erro de certificado SEC_ERROR_INADEQUATE_KEY_USAGE do Firefox na secção .

O Visual Studio inicia o navegador padrão e navega até https://localhost:<port>/swagger/index.html, onde <port> é um número de porta escolhido aleatoriamente definido na criação do projeto.

A página Swagger /swagger/index.html é exibida. Selecione GET>Try it out>Execute. A página exibe:

  • O comando Curl para testar a API WeatherForecast.
  • A URL para testar a API WeatherForecast.
  • O código de resposta, o corpo e os cabeçalhos.
  • Uma caixa de listagem suspensa com tipos de media, valor de exemplo e esquema.

Se a página Swagger não aparecer, consulte este problema do GitHub.

Swagger é usado para gerar documentação útil e páginas de ajuda para APIs da web. Este tutorial usa o Swagger para testar o aplicativo. Para obter mais informações sobre Swagger, consulte documentação da API Web do ASP.NET Core com Swagger / OpenAPI.

Copie e cole o URL de solicitação no navegador: https://localhost:<port>/weatherforecast

JSON semelhante ao exemplo a seguir é retornado:

[
    {
        "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"
    }
]

Adicionar uma classe de modelo

Um modelo é um conjunto de classes que representam os dados que o aplicativo gerencia. O modelo para este aplicativo é a TodoItem classe.

  • No Gerenciador de Soluções , clique com o botão direito do mouse no projeto. Selecione Adicionar>Nova Pasta. Dê um nome à pasta Models.
  • Clique com o botão direito do mouse na Models pasta e selecione Adicionar>classe. Nomeie a classe TodoItem e selecione Adicionar.
  • Substitua o código do modelo pelo seguinte:
namespace TodoApi.Models;

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

A Id propriedade funciona como a chave exclusiva em um banco de dados relacional.

As classes de modelo podem ir para qualquer lugar no projeto, mas a Models pasta é usada por convenção.

Adicionar um contexto de banco de dados

O contexto do banco de dados é a classe principal que coordena a funcionalidade do Entity Framework para um modelo de dados. Essa classe é criada derivando da classe Microsoft.EntityFrameworkCore.DbContext.

  • Clique com o botão direito do mouse na Models pasta e selecione Adicionar>classe. Nomeie a classe TodoContext e clique em Adicionar.
  • Insira o seguinte código:

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

Registrar o contexto do banco de dados

No ASP.NET Core, serviços como o contexto de banco de dados devem ser registrados com o contêiner de injeção de dependência (DI ). O contêiner fornece o serviço aos controladores.

Atualize Program.cs com o seguinte código destacado:

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();

O código anterior:

  • Acrescenta using diretivas.
  • Adiciona o contexto do banco de dados ao contêiner DI.
  • Especifica que o contexto do banco de dados usará um banco de dados na memória.

Andaime um controlador

  • Clique com o botão direito do rato na pasta Controllers.

  • Selecione Adicionar>New Scaffolded Item.

  • Selecione Controlador de API com ações, usando o Entity Framework e, em seguida, selecione Adicionar.

  • Na caixa de diálogo Adicionar controlador de API com ações, usando o Entity Framework :

    • Selecione TodoItem (TodoApi.Models) na classe Model.
    • Selecione TodoContext (TodoApi.Models) na classe de contexto Data.
    • Selecione Adicionar.

    Se a operação de andaime falhar, selecione Adicionar para tentar andaimes uma segunda vez.

O código gerado:

  • Marca a classe com o [ApiController] atributo. Esse atributo indica que o controlador responde a solicitações de API da Web. Para obter informações sobre comportamentos específicos habilitados pelo atributo, consulte Criar APIs da Web com ASP.NET Core.
  • Usa DI para injetar o contexto da base de dados (TodoContext) no controlador. O contexto do banco de dados é usado em cada um dos métodos CRUD no controlador.

Os modelos ASP.NET Core para:

  • Os controladores com views incluem [action] no modelo de rota.
  • Os controladores de API não incluem [action] no modelo de rota.

Quando o [action] token não está no modelo de rota, o nome da ação (nome do método) não é incluído no ponto de extremidade. Ou seja, o nome do método associado à ação não é usado na rota correspondente.

Atualizar o método de criação PostTodoItem

Atualize a instrução de retorno no PostTodoItem para usar o operador 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);
}

O código anterior é um HTTP POST método, conforme indicado pelo [HttpPost] atributo. O método obtém o valor do TodoItem do corpo da solicitação HTTP.

Para obter mais informações, consulte Roteamento de atributos com atributos Http[Verb].

O método CreatedAtAction:

  • Retorna um código de status HTTP 201 se for bem-sucedido. HTTP 201 é a resposta padrão para um HTTP POST método que cria um novo recurso no servidor.
  • Adiciona um cabeçalho Location à resposta. O Location cabeçalho especifica o URI do item to-do recém-criado. Para obter mais informações, consulte 10.2.2 201 Criado.
  • Faz referência à ação GetTodoItem para criar o URI do cabeçalho Location. A palavra-chave C# nameof é usada para evitar a codificação direta do nome da ação na chamada CreatedAtAction.

Teste PostTodoItem

  • Pressione Ctrl+F5 para executar o aplicativo.

  • Na janela do navegador Swagger, selecione POST /api/TodoItems e, em seguida, selecione Experimentar.

  • Na janela Solicitar corpo de entrada, atualize o JSON. Por exemplo

    {
      "name": "walk dog",
      "isComplete": true
    }
    
  • Selecione Executar

    Swagger POST

Testar o URI do cabeçalho de localização

No POST anterior, a interface Swagger mostra o cabeçalho de localização em Cabeçalhos de resposta. Por exemplo, location: https://localhost:7260/api/TodoItems/1. O cabeçalho de localização mostra o endereço URI para o recurso criado.

Para testar o cabeçalho de localização:

  • Na janela do navegador Swagger, selecione GET /api/TodoItems/{id} e, em seguida, selecione Experimentar.

  • Entre 1 na id caixa de entrada e selecione Executar.

    Swagger GET

Examine os métodos GET

Estão implementados dois endpoints GET.

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

A seção anterior mostrou um exemplo da /api/todoitems/{id} rota.

Siga as instruções do POST para adicionar outro item todo e teste a rota usando o /api/todoitems Swagger.

Este aplicativo usa um banco de dados na memória. Se o aplicativo for interrompido e iniciado, a solicitação GET anterior não retornará nenhum dado. Se nenhum dado for retornado, envie dados POST para a aplicação.

Roteamento e caminhos de URL

O [HttpGet] atributo denota um método que responde a uma HTTP GET solicitação. O caminho da URL para cada método é construído da seguinte maneira:

  • Comece com a cadeia de caracteres do modelo no atributo do controlador Route.

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Substitua [controller] pelo nome do controlador, que por convenção é o nome da classe do controlador menos o sufixo "Controller". Para este exemplo, o nome da classe do controlador é TodoItemsController, portanto, o nome do controlador é "TodoItems". ASP.NET O roteamento principal não diferencia maiúsculas de minúsculas.

  • Se o [HttpGet] atributo tiver um modelo de rota (por exemplo, [HttpGet("products")]), acrescente-o ao caminho. Este exemplo não usa um modelo. Para obter mais informações, consulte Roteamento de atributos com atributos Http[Verb].

No método a seguir GetTodoItem , "{id}" é uma variável de espaço reservado para o identificador exclusivo do item to-do. Quando GetTodoItem é invocado, o valor de "{id}" na URL é fornecido para o método em seu id parâmetro.

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

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

    return todoItem;
}

Valores de retorno

O tipo de retorno dos métodos GetTodoItems e GetTodoItem é o tipo ActionResult<T>. ASP.NET Core serializa automaticamente o objeto para JSON e grava o JSON no corpo da mensagem de resposta. O código de resposta para esse tipo de retorno é 200 OK, supondo que não haja exceções não tratadas. As exceções não tratadas são traduzidas em erros 5xx.

ActionResult os tipos de retorno podem representar uma ampla gama de códigos de status HTTP. Por exemplo, GetTodoItem pode retornar dois valores de status diferentes:

  • Se nenhum item corresponder à ID solicitada, o método retornará um 404 statusNotFound código de erro.
  • Caso contrário, o método retorna 200 com um corpo de resposta JSON. O retorno item resulta em uma HTTP 200 resposta.

O método PutTodoItem

Examine o PutTodoItem método:

[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 é semelhante ao PostTodoItem, exceto que usa HTTP PUT. A resposta é 204 (Sem Conteúdo). De acordo com a especificação HTTP, uma PUT solicitação requer que o cliente envie toda a entidade atualizada, não apenas as alterações. Para suportar atualizações parciais, use HTTP PATCH.

Testar o método PutTodoItem

Este exemplo usa um banco de dados na memória que deve ser inicializado sempre que o aplicativo é iniciado. Deve haver um item no banco de dados antes de fazer uma chamada PUT. Chame GET para garantir que há um item no banco de dados antes de fazer uma chamada PUT.

Usando a interface do usuário do Swagger, use o botão PUT para atualizar o TodoItem que tem Id = 1 e defina seu nome como "feed fish". Observe que a resposta é HTTP 204 No Content.

O método DeleteTodoItem

Examine o DeleteTodoItem método:

[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();
}

Testar o método DeleteTodoItem

Utilize a interface Swagger para apagar o TodoItem que tem o Id = 1. Observe que a resposta é HTTP 204 No Content.

Teste com outras ferramentas

Existem muitas outras ferramentas que podem ser usadas para testar APIs da Web, por exemplo:

Para obter mais informações, consulte:

Evitar a sobrepostagem

Atualmente, o aplicativo de exemplo expõe todo o objeto TodoItem. Os aplicativos de produção normalmente limitam os dados que são inseridos e retornados usando um subconjunto do modelo. Há várias razões por trás disso, e a segurança é uma das principais. O subconjunto de um modelo é geralmente referido como um objeto de transferência de dados (DTO), modelo de entrada ou modelo de exibição. DTO é usado neste tutorial.

Um DTO pode ser utilizado para:

  • Evite a sobrepublicação.
  • Oculte propriedades que os clientes não devem visualizar.
  • Omita algumas propriedades para reduzir o tamanho da carga útil.
  • Nivelar gráficos de objetos que contêm objetos aninhados. Gráficos de objetos achatados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem DTO, atualize a classe TodoItem para incluir um campo secreto:

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; }
    }
}

O campo secreto precisa ser oculto deste aplicativo, mas um aplicativo administrativo pode optar por expô-lo.

Verifique se você pode postar e obter o campo secreto.

Crie um modelo DTO:

namespace TodoApi.Models;

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

Atualize o TodoItemsController para usar 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
       };
}

Verifique se você não pode postar ou obter o campo secreto.

Chamar a API da Web com JavaScript

Consulte Tutorial: Chamar uma API da Web ASP.NET Core com JavaScript.

Série de vídeos da API Web

Veja Vídeo: Série para Iniciantes em APIs da Web.

Padrões de aplicativos Web corporativos

Para obter orientação sobre como criar um aplicativo ASP.NET Core confiável, seguro, com desempenho, testável e escalável, consulte Padrões de aplicativos Web corporativos. Está disponível um aplicativo Web de exemplo completo com qualidade de produção que implementa os padrões.

Adicionar suporte de autenticação a uma API da Web

O ASP.NET Core Identity adiciona a funcionalidade de login da interface do usuário (UI) aos aplicativos Web ASP.NET Core. Para proteger APIs da Web e SPAs, use uma das seguintes opções:

O Duende Identity Server é uma framework para OpenID Connect e OAuth 2.0 no ASP.NET Core. O Duende Server habilita os seguintes recursos de Identity segurança:

  • Autenticação como serviço (AaaS)
  • Logon único/desligamento (SSO) em vários tipos de aplicativos
  • Controle de acesso para APIs
  • Gateway de Federação

Important

Duende Software pode exigir que você pague uma taxa de licença para uso de produção do Duende Identity Server. Para obter mais informações, consulte Migrar do ASP.NET Core no .NET 5 para o .NET 6.

Para obter mais informações, consulte a documentação do Duende Identity Server (site da Duende Software).

Publicar no Azure

Para obter informações sobre como implantar no Azure, consulte Guia de início rápido: implantar um aplicativo Web ASP.NET.

Recursos adicionais

Veja ou descarregue o código de exemplo para este tutorial. Veja como fazer o download.

Para obter mais informações, consulte os seguintes recursos:

Este tutorial ensina os conceitos básicos da criação de uma API da Web baseada em controlador que usa um banco de dados. Outra abordagem para criar APIs no ASP.NET Core é criar APIs mínimas. Para obter ajuda com a escolha entre APIs mínimas e APIs baseadas em controlador, consulte Visão geral de APIs. Para obter um tutorial sobre como criar uma API mínima, consulte Tutorial: Criar uma API mínima com o ASP.NET Core.

Overview

Este tutorial cria a seguinte API:

API Description Corpo de solicitação Corpo da resposta
GET /api/todoitems Consiga todos os itens to-do None Lista de tarefas a fazer
GET /api/todoitems/{id} Obter um item pelo ID None Item de tarefa
POST /api/todoitems Adicionar um novo item Item de tarefa Item de tarefa
PUT /api/todoitems/{id} Atualizar um item existente Item de tarefa None
DELETE /api/todoitems/{id}     Excluir um item None None

O diagrama a seguir mostra o design do aplicativo.

O cliente é representado por uma caixa à esquerda. Apresenta um pedido e recebe uma resposta do pedido, uma caixa desenhada à direita. Dentro da caixa de aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação entra no controlador do aplicativo e as operações de leitura/gravação ocorrem entre o controlador e a camada de acesso a dados. O modelo é serializado e retornado ao cliente na resposta.

Prerequisites

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Digite Web API na caixa de pesquisa.
  • Selecione o modelo ASP.NET Core Web API e selecione Next.
  • Na caixa de diálogo Configurar seu novo projeto, nomeie o projeto como TodoApi e selecione Avançar.
  • No caixa de diálogo de Informações adicionais :
    • Confirme se o Framework é .NET 8.0 (Suporte de Longo Prazo).
    • Confirme que a caixa de seleção Usar controladores (desmarque para usar APIs mínimas) está marcada.
    • Confirme que a caixa de seleção Ativar suporte a OpenAPI está marcada.
    • Selecione Criar.

Adicionar um pacote NuGet

Um pacote NuGet deve ser adicionado para dar suporte ao banco de dados usado neste tutorial.

  • No menu Ferramentas, selecione Gerenciador de Pacotes NuGet > Gerenciar Pacotes NuGet para Solução.
  • Selecione a guia Procurar.
  • Digite Microsoft.EntityFrameworkCore.InMemory na caixa de pesquisa e selecione Microsoft.EntityFrameworkCore.InMemory.
  • Marque a caixa de seleção Projeto no painel direito e selecione Instalar.

Note

Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos na seção Instalar e gerenciar pacotes em Workflow de utilização de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.

Testar o projeto

O modelo de projeto cria uma WeatherForecast API com suporte para Swagger.

Pressione Ctrl+F5 para executar sem o depurador.

Visual Studio exibe a seguinte caixa de diálogo quando um projeto ainda não está configurado para usar SSL:

Este projeto está configurado para usar SSL. Para evitar avisos SSL no navegador, você pode optar por confiar no certificado autoassinado que o IIS Express gerou. Gostaria de confiar no certificado SSL do IIS Express?

Selecione Sim se confiar no certificado SSL do IIS Express.

A seguinte caixa de diálogo é exibida:

Caixa de diálogo de aviso de segurança

Selecione Sim se concordar em confiar no certificado de desenvolvimento.

Para obter informações sobre como confiar no navegador Firefox, consulte o erro de certificado SEC_ERROR_INADEQUATE_KEY_USAGE do Firefox na secção .

O Visual Studio inicia o navegador padrão e navega até https://localhost:<port>/swagger/index.html, onde <port> é um número de porta escolhido aleatoriamente definido na criação do projeto.

A página Swagger /swagger/index.html é exibida. Selecione GET>Try it out>Execute. A página exibe:

  • O comando Curl para testar a API WeatherForecast.
  • A URL para testar a API WeatherForecast.
  • O código de resposta, o corpo e os cabeçalhos.
  • Uma caixa de listagem suspensa com tipos de media, valor de exemplo e esquema.

Se a página Swagger não aparecer, consulte este problema do GitHub.

Swagger é usado para gerar documentação útil e páginas de ajuda para APIs da web. Este tutorial usa o Swagger para testar o aplicativo. Para obter mais informações sobre Swagger, consulte documentação da API Web do ASP.NET Core com Swagger / OpenAPI.

Copie e cole o URL de solicitação no navegador: https://localhost:<port>/weatherforecast

JSON semelhante ao exemplo a seguir é retornado:

[
    {
        "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"
    }
]

Adicionar uma classe de modelo

Um modelo é um conjunto de classes que representam os dados que o aplicativo gerencia. O modelo para este aplicativo é a TodoItem classe.

  • No Gerenciador de Soluções , clique com o botão direito do mouse no projeto. Selecione Adicionar>Nova Pasta. Dê um nome à pasta Models.
  • Clique com o botão direito do mouse na Models pasta e selecione Adicionar>classe. Nomeie a classe TodoItem e selecione Adicionar.
  • Substitua o código do modelo pelo seguinte:
namespace TodoApi.Models;

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

A Id propriedade funciona como a chave exclusiva em um banco de dados relacional.

As classes de modelo podem ir para qualquer lugar no projeto, mas a Models pasta é usada por convenção.

Adicionar um contexto de banco de dados

O contexto do banco de dados é a classe principal que coordena a funcionalidade do Entity Framework para um modelo de dados. Essa classe é criada derivando da classe Microsoft.EntityFrameworkCore.DbContext.

  • Clique com o botão direito do mouse na Models pasta e selecione Adicionar>classe. Nomeie a classe TodoContext e clique em Adicionar.
  • Insira o seguinte código:

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

Registrar o contexto do banco de dados

No ASP.NET Core, serviços como o contexto de banco de dados devem ser registrados com o contêiner de injeção de dependência (DI ). O contêiner fornece o serviço aos controladores.

Atualize Program.cs com o seguinte código destacado:

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();

O código anterior:

  • Acrescenta using diretivas.
  • Adiciona o contexto do banco de dados ao contêiner DI.
  • Especifica que o contexto do banco de dados usará um banco de dados na memória.

Andaime um controlador

  • Clique com o botão direito do rato na pasta Controllers.

  • Selecione Adicionar>New Scaffolded Item.

  • Selecione Controlador de API com ações, usando o Entity Framework e, em seguida, selecione Adicionar.

  • Na caixa de diálogo Adicionar controlador de API com ações, usando o Entity Framework :

    • Selecione TodoItem (TodoApi.Models) na classe Model.
    • Selecione TodoContext (TodoApi.Models) na classe de contexto Data.
    • Selecione Adicionar.

    Se a operação de andaime falhar, selecione Adicionar para tentar andaimes uma segunda vez.

O código gerado:

  • Marca a classe com o [ApiController] atributo. Esse atributo indica que o controlador responde a solicitações de API da Web. Para obter informações sobre comportamentos específicos habilitados pelo atributo, consulte Criar APIs da Web com ASP.NET Core.
  • Usa DI para injetar o contexto da base de dados (TodoContext) no controlador. O contexto do banco de dados é usado em cada um dos métodos CRUD no controlador.

Os modelos ASP.NET Core para:

  • Os controladores com views incluem [action] no modelo de rota.
  • Os controladores de API não incluem [action] no modelo de rota.

Quando o [action] token não está no modelo de rota, o nome da ação (nome do método) não é incluído no ponto de extremidade. Ou seja, o nome do método associado à ação não é usado na rota correspondente.

Atualizar o método de criação PostTodoItem

Atualize a instrução de retorno no PostTodoItem para usar o operador 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);
}

O código anterior é um HTTP POST método, conforme indicado pelo [HttpPost] atributo. O método obtém o valor do TodoItem do corpo da solicitação HTTP.

Para obter mais informações, consulte Roteamento de atributos com atributos Http[Verb].

O método CreatedAtAction:

  • Retorna um código de status HTTP 201 se for bem-sucedido. HTTP 201 é a resposta padrão para um HTTP POST método que cria um novo recurso no servidor.
  • Adiciona um cabeçalho Location à resposta. O Location cabeçalho especifica o URI do item to-do recém-criado. Para obter mais informações, consulte 10.2.2 201 Criado.
  • Faz referência à ação GetTodoItem para criar o URI do cabeçalho Location. A palavra-chave C# nameof é usada para evitar a codificação direta do nome da ação na chamada CreatedAtAction.

Teste PostTodoItem

  • Pressione Ctrl+F5 para executar o aplicativo.

  • Na janela do navegador Swagger, selecione POST /api/TodoItems e, em seguida, selecione Experimentar.

  • Na janela Solicitar corpo de entrada, atualize o JSON. Por exemplo

    {
      "name": "walk dog",
      "isComplete": true
    }
    
  • Selecione Executar

    Swagger POST

Testar o URI do cabeçalho de localização

No POST anterior, a interface Swagger mostra o cabeçalho de localização em Cabeçalhos de resposta. Por exemplo, location: https://localhost:7260/api/TodoItems/1. O cabeçalho de localização mostra o endereço URI para o recurso criado.

Para testar o cabeçalho de localização:

  • Na janela do navegador Swagger, selecione GET /api/TodoItems/{id} e, em seguida, selecione Experimentar.

  • Entre 1 na id caixa de entrada e selecione Executar.

    Swagger GET

Examine os métodos GET

Estão implementados dois endpoints GET.

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

A seção anterior mostrou um exemplo da /api/todoitems/{id} rota.

Siga as instruções do POST para adicionar outro item todo e teste a rota usando o /api/todoitems Swagger.

Este aplicativo usa um banco de dados na memória. Se o aplicativo for interrompido e iniciado, a solicitação GET anterior não retornará nenhum dado. Se nenhum dado for retornado, envie dados POST para a aplicação.

Roteamento e caminhos de URL

O [HttpGet] atributo denota um método que responde a uma HTTP GET solicitação. O caminho da URL para cada método é construído da seguinte maneira:

  • Comece com a cadeia de caracteres do modelo no atributo do controlador Route.

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Substitua [controller] pelo nome do controlador, que por convenção é o nome da classe do controlador menos o sufixo "Controller". Para este exemplo, o nome da classe do controlador é TodoItemsController, portanto, o nome do controlador é "TodoItems". ASP.NET O roteamento principal não diferencia maiúsculas de minúsculas.

  • Se o [HttpGet] atributo tiver um modelo de rota (por exemplo, [HttpGet("products")]), acrescente-o ao caminho. Este exemplo não usa um modelo. Para obter mais informações, consulte Roteamento de atributos com atributos Http[Verb].

No método a seguir GetTodoItem , "{id}" é uma variável de espaço reservado para o identificador exclusivo do item to-do. Quando GetTodoItem é invocado, o valor de "{id}" na URL é fornecido para o método em seu id parâmetro.

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

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

    return todoItem;
}

Valores de retorno

O tipo de retorno dos métodos GetTodoItems e GetTodoItem é o tipo ActionResult<T>. ASP.NET Core serializa automaticamente o objeto para JSON e grava o JSON no corpo da mensagem de resposta. O código de resposta para esse tipo de retorno é 200 OK, supondo que não haja exceções não tratadas. As exceções não tratadas são traduzidas em erros 5xx.

ActionResult os tipos de retorno podem representar uma ampla gama de códigos de status HTTP. Por exemplo, GetTodoItem pode retornar dois valores de status diferentes:

  • Se nenhum item corresponder à ID solicitada, o método retornará um 404 statusNotFound código de erro.
  • Caso contrário, o método retorna 200 com um corpo de resposta JSON. O retorno item resulta em uma HTTP 200 resposta.

O método PutTodoItem

Examine o PutTodoItem método:

[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 é semelhante ao PostTodoItem, exceto que usa HTTP PUT. A resposta é 204 (Sem Conteúdo). De acordo com a especificação HTTP, uma PUT solicitação requer que o cliente envie toda a entidade atualizada, não apenas as alterações. Para suportar atualizações parciais, use HTTP PATCH.

Testar o método PutTodoItem

Este exemplo usa um banco de dados na memória que deve ser inicializado sempre que o aplicativo é iniciado. Deve haver um item no banco de dados antes de fazer uma chamada PUT. Chame GET para garantir que há um item no banco de dados antes de fazer uma chamada PUT.

Usando a interface do usuário do Swagger, use o botão PUT para atualizar o TodoItem que tem Id = 1 e defina seu nome como "feed fish". Observe que a resposta é HTTP 204 No Content.

O método DeleteTodoItem

Examine o DeleteTodoItem método:

[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();
}

Testar o método DeleteTodoItem

Utilize a interface Swagger para apagar o TodoItem que tem o Id = 1. Observe que a resposta é HTTP 204 No Content.

Teste com outras ferramentas

Existem muitas outras ferramentas que podem ser usadas para testar APIs da Web, por exemplo:

Para obter mais informações, consulte:

Evitar a sobrepostagem

Atualmente, o aplicativo de exemplo expõe todo o objeto TodoItem. Os aplicativos de produção normalmente limitam os dados que são inseridos e retornados usando um subconjunto do modelo. Há várias razões por trás disso, e a segurança é uma das principais. O subconjunto de um modelo é geralmente referido como um objeto de transferência de dados (DTO), modelo de entrada ou modelo de exibição. DTO é usado neste tutorial.

Um DTO pode ser utilizado para:

  • Evite a sobrepublicação.
  • Oculte propriedades que os clientes não devem visualizar.
  • Omita algumas propriedades para reduzir o tamanho da carga útil.
  • Nivelar gráficos de objetos que contêm objetos aninhados. Gráficos de objetos achatados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem DTO, atualize a classe TodoItem para incluir um campo secreto:

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; }
    }
}

O campo secreto precisa ser oculto deste aplicativo, mas um aplicativo administrativo pode optar por expô-lo.

Verifique se você pode postar e obter o campo secreto.

Crie um modelo DTO:

namespace TodoApi.Models;

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

Atualize o TodoItemsController para usar 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
       };
}

Verifique se você não pode postar ou obter o campo secreto.

Chamar a API da Web com JavaScript

Consulte Tutorial: Chamar uma API da Web ASP.NET Core com JavaScript.

Série de vídeos da API Web

Veja Vídeo: Série para Iniciantes em APIs da Web.

Padrões de aplicativos Web corporativos

Para obter orientação sobre como criar um aplicativo ASP.NET Core confiável, seguro, com desempenho, testável e escalável, consulte Padrões de aplicativos Web corporativos. Está disponível um aplicativo Web de exemplo completo com qualidade de produção que implementa os padrões.

Adicionar suporte de autenticação a uma API da Web

O ASP.NET Core Identity adiciona a funcionalidade de login da interface do usuário (UI) aos aplicativos Web ASP.NET Core. Para proteger APIs da Web e SPAs, use uma das seguintes opções:

O Duende Identity Server é uma framework para OpenID Connect e OAuth 2.0 no ASP.NET Core. O Duende Server habilita os seguintes recursos de Identity segurança:

  • Autenticação como serviço (AaaS)
  • Logon único/desligamento (SSO) em vários tipos de aplicativos
  • Controle de acesso para APIs
  • Gateway de Federação

Important

Duende Software pode exigir que você pague uma taxa de licença para uso de produção do Duende Identity Server. Para obter mais informações, consulte Migrar do ASP.NET Core no .NET 5 para o .NET 6.

Para obter mais informações, consulte a documentação do Duende Identity Server (site da Duende Software).

Publicar no Azure

Para obter informações sobre como implantar no Azure, consulte Guia de início rápido: implantar um aplicativo Web ASP.NET.

Recursos adicionais

Veja ou descarregue o código de exemplo para este tutorial. Veja como fazer o download.

Para obter mais informações, consulte os seguintes recursos: