Tutorial: criar uma API Web com o ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Por Rick Anderson e Kirk Larkin

Este tutorial ensina os conceitos básicos da criação de uma API 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 na escolha entre APIs mínimas e APIs baseadas em controlador, confira a visão geral das APIs. Para obter um tutorial sobre como criar uma API mínima, confira Tutorial: Criar uma API mínima com ASP.NET Core.

Visão geral

Este tutorial cria a seguinte API:

API Descrição Corpo da solicitação Corpo da resposta
GET /api/todoitems Obter todos os itens de tarefas pendentes Nenhum Matriz de itens de tarefas pendentes
GET /api/todoitems/{id} Obter um item por ID Nenhum Item de tarefas pendentes
POST /api/todoitems Adicionar um novo item Item de tarefas pendentes Item de tarefas pendentes
PUT /api/todoitems/{id} Atualizar um item existente Item de tarefas pendentes Nenhum
DELETE /api/todoitems/{id}     Excluir um item Nenhum Nenhum

O diagrama a seguir mostra o design do aplicativo.

O cliente é representado por uma caixa à esquerda. Ele envia uma solicitação e recebe uma resposta do aplicativo, uma caixa desenhada à direita. Dentro da caixa do aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação é recebida 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 para o cliente na resposta.

Pré-requisitos

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Insira API Web na caixa de pesquisa.
  • Selecione o modelo API Web do ASP.NET Core e Avançar.
  • Na caixa de diálogo Configurar o novo projeto, nomeie o projeto TodoApi e selecione Avançar.
  • Na caixa de diálogo Informações adicionais:
    • Confirme se o Framework é .NET 8.0 (suporte a longo prazo).
    • Confirme se a caixa de seleção Usar controladores (desmarque para utilizar APIs mínimas) está marcada.
    • Confirme se a caixa de seleção Habilitar suporte a OpenAPI está marcada.
    • Selecione Criar.

Adicionar um pacote NuGet

Um pacote NuGet deve ser adicionado para suportar o banco de dados utilizado neste tutorial.

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

Observação

Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

Testar o projeto

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

Pressione Ctrl + F5 para execução sem o depurador.

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

Este projeto está configurado para usar SSL. para evitar avisos de 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 você confia 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 você concordar com confiar no certificado de desenvolvimento.

Para obter informações sobre como confiar no navegador Firefox, confira Erro de certificado Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.

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 na criação do projeto.

A página /swagger/index.html do Swagger é 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, o corpo e os cabeçalhos de resposta.
  • Uma caixa de listagem suspensa com tipos de mídia e o valor e o esquema de exemplo.

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

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

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

É retornado JSON semelhante ao exemplo abaixo:

[
    {
        "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 gerenciados pelo aplicativo. O modelo para este aplicativo é a classe TodoItem.

  • No Gerenciador de Soluções, clique com o botão direito do mouse no nome do projeto. Selecione Adicionar>Nova Pasta. Nomeie a pasta Models.
  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Dê à classe o nome 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 propriedade Id funciona como a chave exclusiva em um banco de dados relacional.

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

Adicionar um contexto de banco de dados

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

  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Nomeie a classe como 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 de banco de dados

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

Atualize Program.cs com o seguinte código realçado:

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:

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

Faça scaffold de um controlador

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

  • Selecione Adicionar>Novo item com scaffold.

  • 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 Modelo.
    • Selecione TodoContext (TodoApi.Models) na classe Contexto de Dados.
    • Selecione Adicionar.

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

O código gerado:

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

Os modelos do ASP.NET Core para:

  • Os controladores com exibições incluem [action] no modelo de rota.
  • Os controladores de API não incluem [action] no modelo de rota.

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

Atualize o método PostTodoItem create

Atualize a instrução return 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("PostTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(PostTodoItem), new { id = todoItem.Id }, todoItem);
}

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

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

O método CreatedAtAction:

  • Retorna um código de status HTTP 201 em caso de êxito. HTTP 201 é a resposta padrão para um método HTTP POST que cria um recurso no servidor.
  • Adiciona um cabeçalho de Local à resposta. O cabeçalho Location especifica o URI do item de tarefas pendentes recém-criado. Para obter mais informações, confira 10.2.2 201 Criado.
  • Faz referência à ação PostTodoItem para criar o URI de Location do cabeçalho. A palavra-chave nameof do C# é usada para evitar o hard-coding do nome da ação, na chamada CreatedAtAction.

Testar PostTodoItem

  • Pressione CTRL+F5 para executar o aplicativo.

  • Na janela do navegador Swagger, selecione POST /api/TodoItems e selecione Experimente.

  • Na janela de entrada Corpo da solicitação, atualize o JSON. Por exemplo,

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

    Swagger POST

Testar o URI do cabeçalho de local

No POST anterior, a interface do usuário do Swagger mostra o cabeçalho de local em cabeçalhos de resposta. Por exemplo, location: https://localhost:7260/api/TodoItems/1. O cabeçalho de local mostra o URI para o recurso criado.

Para testar o cabeçalho de local:

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

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

    Swagger GET

Examine os métodos GET

Dois pontos de extremidade GET são implementados:

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

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

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

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

Roteamento e caminhos de URL

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

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

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

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

No método GetTodoItem a seguir, "{id}" é uma variável de espaço reservado para o identificador exclusivo do item pendente. Quando GetTodoItem é invocado, o valor de "{id}" na URL é fornecido para o método no parâmetro 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;
}

Valores de retorno

O tipo de retorno dos métodos GetTodoItems e GetTodoItem é o tipo <ActionResult>T. O ASP.NET Core serializa automaticamente o objeto em 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 nenhuma exceção sem tratamento. As exceções sem tratamento são convertidas em erros 5xx.

Os tipos de retorno ActionResult podem representar uma ampla variedade 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 código de erro Status 404NotFound.
  • Caso contrário, o método retornará 200 com um corpo de resposta JSON. O retorno de item resulta em uma resposta HTTP 200.

O método PutTodoItem

Examine o método 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 é semelhante a PostTodoItem, exceto pelo uso de HTTP PUT. A resposta é 204 (Sem conteúdo). De acordo com a especificação de HTTP, uma solicitação PUT exige que o cliente envie a entidade inteira atualizada, não apenas as alterações. Para dar suporte a atualizações parciais, use HTTP PATCH.

Testar o método PutTodoItem

Este exemplo usa um banco de dados em memória que precisará ser iniciado sempre que o aplicativo for iniciado. Deverá haver um item no banco de dados antes de você fazer uma chamada PUT. Chame GET para garantir a existência de 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 definir seu nome como "feed fish". Observe que a resposta é HTTP 204 No Content.

O método DeleteTodoItem

Examine o método 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();
}

Testar o método DeleteTodoItem

Use a interface do usuário do Swagger para excluir o TodoItem que tem ID = 1. Observe que a resposta é HTTP 204 No Content.

Teste com outras ferramentas

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

Para obter mais informações, consulte:

Impedir o excesso de postagem

Atualmente, o aplicativo de exemplo expõe todo o objeto TodoItem. Os aplicativos de produção normalmente limitam os dados entrada retornados pelo uso de 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 é chamado de DTO (Objeto de Transferência de Dados), modelo de entrada ou modelo de exibição. O DTO é usado neste tutorial.

Um DTO pode ser usado para:

  • Impedir o excesso de postagem.
  • Ocultar as propriedades que os clientes não devem exibir.
  • Omitir algumas propriedades para reduzir o tamanho da carga.
  • Nivelar gráficos de objetos que contenham objetos aninhados. Os grafos de objeto nivelados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem de 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 ocultado neste aplicativo, mas um aplicativo administrativo poderia optar por mostrá-lo.

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

Crie um modelo de 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 Web com o JavaScript

Confira Tutorial: Chamar uma API Web do ASP.NET Core com o JavaScript.

Série de vídeos da API Web

Confira Vídeo: Séries iniciantes: APIs Web.

Padrões de aplicativos Web confiáveis

Veja O padrão de aplicativo da Web confiável para.NET vídeos do YouTube e artigo para obter orientação sobre como criar um aplicativo ASP.NET Core moderno, confiável, de alto desempenho, testável, econômico e escalonável, seja do zero ou refatorando um existente aplicativo.

Adicionar suporte de autenticação a uma API Web

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

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

  • AaaS (autenticação como serviço)
  • SSO (logon único) em vários tipos de aplicativo
  • Controle de acesso para APIs
  • Federation Gateway

Importante

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

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

Publicar no Azure

Para obter informações sobre como implantar no Azure, consulte Início Rápido: Implantar um aplicativo Web ASP.NET.

Recursos adicionais

Exibir ou baixar o código de exemplo para este tutorial. Consulte como baixar.

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

Este tutorial ensina os conceitos básicos da criação de uma API 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 na escolha entre APIs mínimas e APIs baseadas em controlador, confira a visão geral das APIs. Para obter um tutorial sobre como criar uma API mínima, confira Tutorial: Criar uma API mínima com ASP.NET Core.

Visão geral

Este tutorial cria a seguinte API:

API Descrição Corpo da solicitação Corpo da resposta
GET /api/todoitems Obter todos os itens de tarefas pendentes Nenhum Matriz de itens de tarefas pendentes
GET /api/todoitems/{id} Obter um item por ID Nenhum Item de tarefas pendentes
POST /api/todoitems Adicionar um novo item Item de tarefas pendentes Item de tarefas pendentes
PUT /api/todoitems/{id} Atualizar um item existente Item de tarefas pendentes Nenhum
DELETE /api/todoitems/{id}     Excluir um item Nenhum Nenhum

O diagrama a seguir mostra o design do aplicativo.

O cliente é representado por uma caixa à esquerda. Ele envia uma solicitação e recebe uma resposta do aplicativo, uma caixa desenhada à direita. Dentro da caixa do aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação é recebida 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 para o cliente na resposta.

Pré-requisitos

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Insira API Web na caixa de pesquisa.
  • Selecione o modelo API Web do ASP.NET Core e Avançar.
  • Na caixa de diálogo Configurar o novo projeto, nomeie o projeto TodoApi e selecione Avançar.
  • Na caixa de diálogo Informações adicionais:
    • Confirme se o Framework é .NET 7.0 (ou posterior).
    • Confirme se a caixa de seleção Usar controladores (desmarque para utilizar APIs mínimas) está marcada.
    • Selecione Criar.

Observação

Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

Testar o projeto

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

Pressione Ctrl + F5 para execução sem o depurador.

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

Este projeto está configurado para usar SSL. para evitar avisos de 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 você confia 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 você concordar com confiar no certificado de desenvolvimento.

Para obter informações sobre como confiar no navegador Firefox, confira Erro de certificado Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.

O Visual Studio inicia o navegador padrão e navega para https://localhost:<port>/swagger/index.html, em que <port> é um número de porta escolhido aleatoriamente.

A página /swagger/index.html do Swagger é 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, o corpo e os cabeçalhos de resposta.
  • Uma caixa de listagem suspensa com tipos de mídia e o valor e o esquema de exemplo.

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

O Swagger é usado para gerar documentação útil e páginas de ajuda para APIs Web. Este tutorial se concentra na criação de uma API Web. Para obter mais informações sobre o Swagger, confira a documentação da API Web do ASP.NET Core com Swagger/OpenAPI.

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

É retornado JSON semelhante ao exemplo abaixo:

[
    {
        "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 gerenciados pelo aplicativo. O modelo para este aplicativo é a classe TodoItem.

  • No Gerenciador de Soluções, clique com o botão direito do mouse no nome do projeto. Selecione Adicionar>Nova Pasta. Nomeie a pasta Models.
  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Dê à classe o nome 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 propriedade Id funciona como a chave exclusiva em um banco de dados relacional.

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

Adicionar um contexto de banco de dados

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

Adicionar pacotes do NuGet

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

Adicione o contexto de banco de dados TodoContext

  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Nomeie a classe como 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 de banco de dados

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

Atualize Program.cs com o seguinte código realçado:

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:

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

Faça scaffold de um controlador

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

  • Selecione Adicionar>Novo item com scaffold.

  • 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 Modelo.
    • Selecione TodoContext (TodoApi.Models) na classe Contexto de Dados.
    • Selecione Adicionar.

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

O código gerado:

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

Os modelos do ASP.NET Core para:

  • Os controladores com exibições incluem [action] no modelo de rota.
  • Os controladores de API não incluem [action] no modelo de rota.

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

Atualize o método PostTodoItem create

Atualize a instrução return 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("PostTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(PostTodoItem), new { id = todoItem.Id }, todoItem);
}

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

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

O método CreatedAtAction:

  • Retorna um código de status HTTP 201 em caso de êxito. HTTP 201 é a resposta padrão para um método HTTP POST que cria um recurso no servidor.
  • Adiciona um cabeçalho de Local à resposta. O cabeçalho Location especifica o URI do item de tarefas pendentes recém-criado. Para obter mais informações, confira 10.2.2 201 Criado.
  • Faz referência à ação PostTodoItem para criar o URI de Location do cabeçalho. A palavra-chave nameof do C# é usada para evitar o hard-coding do nome da ação, na chamada CreatedAtAction.

Testar PostTodoItem

  • Pressione CTRL+F5 para executar o aplicativo.

  • Na janela do navegador Swagger, selecione POST /api/TodoItems e selecione Experimente.

  • Na janela de entrada Corpo da solicitação, atualize o JSON. Por exemplo,

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

    Swagger POST

Testar o URI do cabeçalho de local

No POST anterior, a interface do usuário do Swagger mostra o cabeçalho de local em cabeçalhos de resposta. Por exemplo, location: https://localhost:7260/api/TodoItems/1. O cabeçalho de local mostra o URI para o recurso criado.

Para testar o cabeçalho de local:

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

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

    Swagger GET

Examine os métodos GET

Dois pontos de extremidade GET são implementados:

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

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

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

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

Roteamento e caminhos de URL

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

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

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

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

No método GetTodoItem a seguir, "{id}" é uma variável de espaço reservado para o identificador exclusivo do item pendente. Quando GetTodoItem é invocado, o valor de "{id}" na URL é fornecido para o método no parâmetro 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;
}

Valores de retorno

O tipo de retorno dos métodos GetTodoItems e GetTodoItem é o tipo <ActionResult>T. O ASP.NET Core serializa automaticamente o objeto em 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 nenhuma exceção sem tratamento. As exceções sem tratamento são convertidas em erros 5xx.

Os tipos de retorno ActionResult podem representar uma ampla variedade 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 código de erro Status 404NotFound.
  • Caso contrário, o método retornará 200 com um corpo de resposta JSON. O retorno de item resulta em uma resposta HTTP 200.

O método PutTodoItem

Examine o método 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 é semelhante a PostTodoItem, exceto pelo uso de HTTP PUT. A resposta é 204 (Sem conteúdo). De acordo com a especificação de HTTP, uma solicitação PUT exige que o cliente envie a entidade inteira atualizada, não apenas as alterações. Para dar suporte a atualizações parciais, use HTTP PATCH.

Testar o método PutTodoItem

Este exemplo usa um banco de dados em memória que precisará ser iniciado sempre que o aplicativo for iniciado. Deverá haver um item no banco de dados antes de você fazer uma chamada PUT. Chame GET para garantir a existência de 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 definir seu nome como "feed fish". Observe que a resposta é HTTP 204 No Content.

O método DeleteTodoItem

Examine o método 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();
}

Testar o método DeleteTodoItem

Use a interface do usuário do Swagger para excluir o TodoItem que tem ID = 1. Observe que a resposta é HTTP 204 No Content.

Testar com http-repl, Postman ou curl

http-repl, Postman e curl geralmente são usados para testar as API. O Swagger usa curl e mostra o comando curl enviado.

Para obter instruções sobre essas ferramentas, confira os seguintes links:

Para obter mais informações sobre http-repl, confira Testar APIs Web com o HttpRepl.

Impedir o excesso de postagem

Atualmente, o aplicativo de exemplo expõe todo o objeto TodoItem. Os aplicativos de produção normalmente limitam os dados entrada retornados pelo uso de 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 é chamado de DTO (Objeto de Transferência de Dados), modelo de entrada ou modelo de exibição. O DTO é usado neste tutorial.

Um DTO pode ser usado para:

  • Impedir o excesso de postagem.
  • Ocultar as propriedades que os clientes não devem exibir.
  • Omitir algumas propriedades para reduzir o tamanho da carga.
  • Nivelar gráficos de objetos que contenham objetos aninhados. Os grafos de objeto nivelados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem de 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 ocultado neste aplicativo, mas um aplicativo administrativo poderia optar por mostrá-lo.

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

Crie um modelo de 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 Web com o JavaScript

Confira Tutorial: Chamar uma API Web do ASP.NET Core com o JavaScript.

Série de vídeos da API Web

Confira Vídeo: Séries iniciantes: APIs Web.

Padrões de aplicativos Web confiáveis

Veja O padrão de aplicativo da Web confiável para.NET vídeos do YouTube e artigo para obter orientação sobre como criar um aplicativo ASP.NET Core moderno, confiável, de alto desempenho, testável, econômico e escalonável, seja do zero ou refatorando um existente aplicativo.

Adicionar suporte de autenticação a uma API Web

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

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

  • AaaS (autenticação como serviço)
  • SSO (logon único) em vários tipos de aplicativo
  • Controle de acesso para APIs
  • Federation Gateway

Importante

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

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

Publicar no Azure

Para obter informações sobre como implantar no Azure, consulte Início Rápido: Implantar um aplicativo Web ASP.NET.

Recursos adicionais

Exibir ou baixar o código de exemplo para este tutorial. Consulte como baixar.

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

Este tutorial ensina os conceitos básicos da criação de uma API 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 na escolha entre APIs mínimas e APIs baseadas em controlador, confira a visão geral das APIs. Para obter um tutorial sobre como criar uma API mínima, confira Tutorial: Criar uma API mínima com ASP.NET Core.

Neste tutorial, você aprenderá como:

  • Criar um projeto de API Web.
  • Adicione uma classe de modelo e um contexto de banco de dados.
  • Faça scaffold de um controlador com métodos CRUD.
  • Configure o roteamento, os caminhos de URL e os valores retornados.
  • Chamar a API Web com http-repl.

No final, você terá uma API Web que pode gerenciar itens de "tarefas pendentes" armazenados em um banco de dados.

Visão geral

Este tutorial cria a seguinte API:

API Descrição Corpo da solicitação Corpo da resposta
GET /api/todoitems Obter todos os itens de tarefas pendentes Nenhum Matriz de itens de tarefas pendentes
GET /api/todoitems/{id} Obter um item por ID Nenhum Item de tarefas pendentes
POST /api/todoitems Adicionar um novo item Item de tarefas pendentes Item de tarefas pendentes
PUT /api/todoitems/{id} Atualizar um item existente Item de tarefas pendentes Nenhum
DELETE /api/todoitems/{id}     Excluir um item Nenhum Nenhum

O diagrama a seguir mostra o design do aplicativo.

O cliente é representado por uma caixa à esquerda. Ele envia uma solicitação e recebe uma resposta do aplicativo, uma caixa desenhada à direita. Dentro da caixa do aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação é recebida 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 para o cliente na resposta.

Pré-requisitos

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Insira API Web na caixa de pesquisa.
  • Selecione o modelo API Web do ASP.NET Core e Avançar.
  • Na caixa de diálogo Configurar o novo projeto, nomeie o projeto TodoApi e selecione Avançar.
  • Na caixa de diálogo Informações adicionais:
    • Confirme se o Framework é o .NET 6.0 (suporte de longo prazo).
    • Confirme se a caixa de seleção Usar controladores (desmarque para utilizar APIs mínimas) está marcada.
    • Selecione Criar.

Observação

Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

Testar o projeto

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

Pressione Ctrl + F5 para execução sem o depurador.

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

Este projeto está configurado para usar SSL. para evitar avisos de 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 você confia 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 você concordar com confiar no certificado de desenvolvimento.

Para obter informações sobre como confiar no navegador Firefox, confira Erro de certificado Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.

O Visual Studio inicia o navegador padrão e navega para https://localhost:<port>/swagger/index.html, em que <port> é um número de porta escolhido aleatoriamente.

A página /swagger/index.html do Swagger é 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, o corpo e os cabeçalhos de resposta.
  • Uma caixa de listagem suspensa com tipos de mídia e o valor e o esquema de exemplo.

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

O Swagger é usado para gerar documentação útil e páginas de ajuda para APIs Web. Este tutorial se concentra na criação de uma API Web. Para obter mais informações sobre o Swagger, confira a documentação da API Web do ASP.NET Core com Swagger/OpenAPI.

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

É retornado JSON semelhante ao exemplo abaixo:

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

Atualizar o launchUrl

Em Properties\launchSettings.json, atualize launchUrl de "swagger" para "api/todoitems":

"launchUrl": "api/todoitems",

Como o Swagger será removido, a marcação anterior alterará a URL que é iniciada para o método GET do controlador adicionado nas seções a seguir.

Adicionar uma classe de modelo

Um modelo é um conjunto de classes que representam os dados gerenciados pelo aplicativo. O modelo para esse aplicativo é uma única classe TodoItem.

  • No Gerenciador de Soluções, clique com o botão direito do mouse no nome do projeto. Selecione Adicionar>Nova Pasta. Nomeie a pasta Models.

  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Dê à classe o nome 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 propriedade Id funciona como a chave exclusiva em um banco de dados relacional.

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

Adicionar um contexto de banco de dados

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

Adicionar pacotes do NuGet

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

Adicione o contexto de banco de dados TodoContext

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

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

Registrar o contexto de banco de dados

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

Atualize Program.cs com o código a seguir:

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

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

var app = builder.Build();

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

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

O código anterior:

  • Remove as chamadas do Swagger.
  • Remove diretivas using não utilizadas.
  • Adiciona o contexto de banco de dados ao contêiner de DI.
  • Especifica que o contexto de banco de dados usará um banco de dados em memória.

Faça scaffold de um controlador

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

  • Selecione Adicionar>Novo item com scaffold.

  • 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 Modelo.
    • Selecione TodoContext (TodoApi.Models) na classe Contexto de Dados.
    • Selecione Adicionar.

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

O código gerado:

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

Os modelos do ASP.NET Core para:

  • Os controladores com exibições incluem [action] no modelo de rota.
  • Os controladores de API não incluem [action] no modelo de rota.

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

Atualize o método PostTodoItem create

Atualize a instrução return 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("PostTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(PostTodoItem), new { id = todoItem.Id }, todoItem);
}

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

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

O método CreatedAtAction:

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

Instalar http-repl

Este tutorial usa http-repl para testar a API Web.

  • Execute o comando a seguir em um prompt de comando:

    dotnet tool install -g Microsoft.dotnet-httprepl
    

    Observação

    Por padrão, a arquitetura dos binários do .NET a serem instalados representa a arquitetura do SO sendo executado no momento. Para especificar uma arquitetura de SO diferente, consulte instalação da ferramenta dotnet, opção --arch. Para obter mais informações, confira o problema dotnet/AspNetCore.Docs #29262 do GitHub.

  • Se você não tiver o SDK do .NET 6.0 ou o runtime instalado, instale o runtime do .NET 6.0.

Testar PostTodoItem

  • Pressione CTRL+F5 para executar o aplicativo.

  • Abra uma nova janela de terminal e execute os comandos a seguir. Se o aplicativo usar um número de porta diferente, substitua 5001 no comando httprepl pelo número da porta.

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

    Aqui está um exemplo da saída do comando:

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

Testar o URI do cabeçalho de local

Para testar o cabeçalho de local, copie e cole-o em um comando httprepl get.

O exemplo a seguir pressupõe que você ainda esteja em uma sessão httprepl. Se você terminou a sessão httprepl anterior, substitua connect por httprepl nos seguintes comandos:

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

Aqui está um exemplo da saída do comando:

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

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

Examine os métodos GET

Dois pontos de extremidade GET são implementados:

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

Você acabou de ver um exemplo da rota /api/todoitems/{id}. Teste a rota /api/todoitems:

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

Aqui está um exemplo da saída do comando:

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

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

Desta vez, o JSON retornado é uma matriz de um item.

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

Roteamento e caminhos de URL

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

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

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

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

No método GetTodoItem a seguir, "{id}" é uma variável de espaço reservado para o identificador exclusivo do item pendente. Quando GetTodoItem é invocado, o valor de "{id}" na URL é fornecido para o método no parâmetro 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;
}

Valores de retorno

O tipo de retorno dos métodos GetTodoItems e GetTodoItem é o tipo <ActionResult>T. O ASP.NET Core serializa automaticamente o objeto em 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 nenhuma exceção sem tratamento. As exceções sem tratamento são convertidas em erros 5xx.

Os tipos de retorno ActionResult podem representar uma ampla variedade 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 código de erro Status 404NotFound.
  • Caso contrário, o método retornará 200 com um corpo de resposta JSON. Retornar item resulta em uma resposta HTTP 200.

O método PutTodoItem

Examine o método 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 é semelhante a PostTodoItem, exceto pelo uso de HTTP PUT. A resposta é 204 (Sem conteúdo). De acordo com a especificação de HTTP, uma solicitação PUT exige que o cliente envie a entidade inteira atualizada, não apenas as alterações. Para dar suporte a atualizações parciais, use HTTP PATCH.

Se você receber um erro chamando PutTodoItem na seção a seguir, chame GET para garantir que haja um item no banco de dados.

Testar o método PutTodoItem

Este exemplo usa um banco de dados em memória que precisará ser iniciado sempre que o aplicativo for iniciado. Deverá haver um item no banco de dados antes de você fazer uma chamada PUT. Chame GET para garantir a existência de um item no banco de dados antes de fazer uma chamada PUT.

Atualize o item pendente que tem a ID = 1 e defina seu nome como "feed fish":

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

Aqui está um exemplo da saída do comando:

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

O método DeleteTodoItem

Examine o método 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();
}

Testar o método DeleteTodoItem

Exclua o item de tarefas pendentes que tem ID = 1:

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

Aqui está um exemplo da saída do comando:

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

Impedir o excesso de postagem

Atualmente, o aplicativo de exemplo expõe todo o objeto TodoItem. Os aplicativos de produção normalmente limitam os dados entrada retornados pelo uso de 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 é chamado de DTO (Objeto de Transferência de Dados), modelo de entrada ou modelo de exibição. O DTO é usado neste tutorial.

Um DTO pode ser usado para:

  • Impedir o excesso de postagem.
  • Ocultar as propriedades que os clientes não devem exibir.
  • Omitir algumas propriedades para reduzir o tamanho da carga.
  • Nivelar gráficos de objetos que contenham objetos aninhados. Os grafos de objeto nivelados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem de 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 ocultado neste aplicativo, mas um aplicativo administrativo poderia optar por mostrá-lo.

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

Crie um modelo de 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
        [HttpGet("{id}")]
        public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
        {
            var todoItem = await _context.TodoItems.FindAsync(id);

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

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

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

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

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

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

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

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

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

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

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

            return NoContent();
        }

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

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

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

Chamar a API Web com o JavaScript

Confira Tutorial: Chamar uma API Web do ASP.NET Core com o JavaScript.

Série de vídeos da API Web

Confira Vídeo: Séries iniciantes: APIs Web.

Adicionar suporte de autenticação a uma API Web

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

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

  • AaaS (autenticação como serviço)
  • SSO (logon único) em vários tipos de aplicativo
  • Controle de acesso para APIs
  • Federation Gateway

Importante

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

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

Publicar no Azure

Para obter informações sobre como implantar no Azure, consulte Início Rápido: Implantar um aplicativo Web ASP.NET.

Recursos adicionais

Exibir ou baixar o código de exemplo para este tutorial. Consulte como baixar.

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

Este tutorial ensina os conceitos básicos da criação de uma API 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 na escolha entre APIs mínimas e APIs baseadas em controlador, confira a visão geral das APIs. Para obter um tutorial sobre como criar uma API mínima, confira Tutorial: Criar uma API mínima com ASP.NET Core.

Neste tutorial, você aprenderá como:

  • Criar um projeto de API Web.
  • Adicione uma classe de modelo e um contexto de banco de dados.
  • Faça scaffold de um controlador com métodos CRUD.
  • Configure o roteamento, os caminhos de URL e os valores retornados.
  • Chamar a API Web com o Postman.

No final, você terá uma API Web que pode gerenciar itens de "tarefas pendentes" armazenados em um banco de dados.

Visão geral

Este tutorial cria a seguinte API:

API Descrição Corpo da solicitação Corpo da resposta
GET /api/todoitems Obter todos os itens de tarefas pendentes Nenhum Matriz de itens de tarefas pendentes
GET /api/todoitems/{id} Obter um item por ID Nenhum Item de tarefas pendentes
POST /api/todoitems Adicionar um novo item Item de tarefas pendentes Item de tarefas pendentes
PUT /api/todoitems/{id} Atualizar um item existente Item de tarefas pendentes Nenhum
DELETE /api/todoitems/{id}     Excluir um item Nenhum Nenhum

O diagrama a seguir mostra o design do aplicativo.

O cliente é representado por uma caixa à esquerda. Ele envia uma solicitação e recebe uma resposta do aplicativo, uma caixa desenhada à direita. Dentro da caixa do aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação é recebida 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 para o cliente na resposta.

Pré-requisitos

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Selecione o modelo da API Web do ASP.NET Core e clique em Avançar.
  • Nomeie o projeto como TodoApi e clique em Criar.
  • Na caixa de diálogo Criar um aplicativo Web ASP.NET Core, confirme se .NET Core e ASP.NET Core 5.0 estão selecionados. Selecione o modelo API e clique em Criar.

Caixa de diálogo Novo projeto do VS

Observação

Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

Testar o projeto

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

Pressione Ctrl + F5 para execução sem o depurador.

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

Este projeto está configurado para usar SSL. para evitar avisos de 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 você confia 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 você concordar com confiar no certificado de desenvolvimento.

Para obter informações sobre como confiar no navegador Firefox, confira Erro de certificado Firefox SEC_ERROR_INADEQUATE_KEY_USAGE.

O Visual Studio inicia:

  • O servidor Web do IIS Express.
  • O navegador padrão e navega até https://localhost:<port>/swagger/index.html, em que <port> é um número de porta escolhido aleatoriamente.

A página /swagger/index.html do Swagger é 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, o corpo e os cabeçalhos de resposta.
  • Uma caixa de listagem suspensa com tipos de mídia e o valor e o esquema de exemplo.

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

O Swagger é usado para gerar documentação útil e páginas de ajuda para APIs Web. Este tutorial se concentra na criação de uma API Web. Para obter mais informações sobre o Swagger, confira a documentação da API Web do ASP.NET Core com Swagger/OpenAPI.

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

É retornado um JSON semelhante ao seguinte:

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

Atualizar o launchUrl

Em Properties\launchSettings.json, atualize launchUrl de "swagger" para "api/todoitems":

"launchUrl": "api/todoitems",

Como o Swagger será removido, a marcação anterior alterará a URL que é iniciada para o método GET do controlador adicionado nas seções a seguir.

Adicionar uma classe de modelo

Um modelo é um conjunto de classes que representam os dados gerenciados pelo aplicativo. O modelo para esse aplicativo é uma única classe TodoItem.

  • No Gerenciador de Soluções, clique com o botão direito do mouse no nome do projeto. Selecione Adicionar>Nova Pasta. Nomeie a pasta Models.

  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Dê à classe o nome 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 propriedade Id funciona como a chave exclusiva em um banco de dados relacional.

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

Adicionar um contexto de banco de dados

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

Adicionar pacotes do NuGet

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

Gerenciador de Pacotes NuGet

Adicione o contexto de banco de dados TodoContext

  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Nomeie a classe como 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; }
        }
    }
    

Registrar o contexto de banco de dados

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

Atualize Startup.cs com o código a seguir:

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

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

        public IConfiguration Configuration { get; }

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

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

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

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

            app.UseAuthorization();

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

O código anterior:

  • Remove as chamadas do Swagger.
  • Remove as declarações using não utilizadas.
  • Adiciona o contexto de banco de dados ao contêiner de DI.
  • Especifica que o contexto de banco de dados usará um banco de dados em memória.

Faça scaffold de um controlador

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

  • Selecione Adicionar>Novo item com scaffold.

  • 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 Modelo.
    • Selecione TodoContext (TodoApi.Models) na classe Contexto de Dados.
    • Selecione Adicionar.

O código gerado:

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

Os modelos do ASP.NET Core para:

  • Os controladores com exibições incluem [action] no modelo de rota.
  • Os controladores de API não incluem [action] no modelo de rota.

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

Atualize o método PostTodoItem create

Atualize a instrução return no PostTodoItem para usar o operador nameof:

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

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

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

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

O método CreatedAtAction:

  • Retorna um código de status HTTP 201 em caso de êxito. HTTP 201 é a resposta padrão para um método HTTP POST que cria um novo recurso no servidor.
  • Adiciona um cabeçalho de Local à resposta. O cabeçalho Location especifica o URI do item de tarefas pendentes recém-criado. Para obter mais informações, confira 201 Criado.
  • Faz referência à ação GetTodoItem para criar o URI de Location do cabeçalho. A palavra-chave nameof do C# é usada para evitar o hard-coding do nome da ação, na chamada CreatedAtAction.

Instale o Postman

Este tutorial usa o Postman para testar a API Web.

  • Instale o Postman
  • Inicie o aplicativo Web.
  • Inicie o Postman.
  • Desabilite a Verificação do certificado SSL:
    • Postman para Windows: selecione Arquivo>Configurações (guia Geral), desabilite a verificação do certificado SSL.
    • Postman para macOS: selecione Postman>Configurações (guia Geral), desabilite a verificação do certificado SSL.

      Aviso

      Habilite novamente a verificação do certificado SSL depois de testar o controlador.

Teste o PostTodoItem com o Postman

  • Crie uma solicitação.

  • Defina o método HTTP como POST.

  • Defina o URI como https://localhost:<port>/api/todoitems. Por exemplo, https://localhost:5001/api/todoitems.

  • Selecione a guia Corpo.

  • Selecione o botão de opção bruto.

  • Defina o tipo como JSON (application/json).

  • No corpo da solicitação, insira JSON para um item de tarefa pendente:

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

    Postman com a solicitação Create

Testar o URI do cabeçalho de local

O URI do cabeçalho de local pode ser testado no navegador. Copie e cole o URI do cabeçalho de localização no navegador.

Para testar no Postman:

  • Selecione a guia Cabeçalhos no painel Resposta.

  • Copie o valor do cabeçalho Local:

    Guia Cabeçalhos do console do Postman

  • Defina o método HTTP como GET.

  • Defina o URI como https://localhost:<port>/api/todoitems/1. Por exemplo, https://localhost:5001/api/todoitems/1.

  • Selecione Enviar.

Examine os métodos GET

Dois pontos de extremidade GET são implementados:

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

Teste o aplicativo chamando os dois pontos de extremidade de um navegador ou do Postman. Por exemplo:

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

Uma resposta semelhante à seguinte é produzida pela chamada a GetTodoItems:

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

Teste o GET com o Postman

  • Crie uma solicitação.
  • Defina o método HTTP como GET.
  • Defina o URI da solicitação como https://localhost:<port>/api/todoitems. Por exemplo, https://localhost:5001/api/todoitems.
  • Defina Exibição de dois painéis no Postman.
  • Selecione Enviar.

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

Roteamento e caminhos de URL

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

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

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • Substitua [controller] pelo nome do controlador, que é o nome de classe do controlador menos o sufixo "Controlador" por convenção. Para esta amostra, o nome da classe do controlador é TodoItemsController. Portanto, o nome do controlador é "TodoItems". O roteamento do ASP.NET Core não diferencia maiúsculas de minúsculas.

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

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

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

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

    return todoItem;
}

Valores de retorno

O tipo de retorno dos métodos GetTodoItems e GetTodoItem é o tipo <ActionResult>T. O ASP.NET Core serializa automaticamente o objeto em 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 nenhuma exceção sem tratamento. As exceções sem tratamento são convertidas em erros 5xx.

Os tipos de retorno ActionResult podem representar uma ampla variedade 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 código de erro Status 404NotFound.
  • Caso contrário, o método retornará 200 com um corpo de resposta JSON. Retornar item resulta em uma resposta HTTP 200.

O método PutTodoItem

Examine o método PutTodoItem:

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

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

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

    return NoContent();
}

PutTodoItem é semelhante a PostTodoItem, exceto pelo uso de HTTP PUT. A resposta é 204 (Sem conteúdo). De acordo com a especificação de HTTP, uma solicitação PUT exige que o cliente envie a entidade inteira atualizada, não apenas as alterações. Para dar suporte a atualizações parciais, use HTTP PATCH.

Se você vir um erro ao chamar PutTodoItem, chame GET para garantir que existe um item no banco de dados.

Testar o método PutTodoItem

Este exemplo usa um banco de dados em memória que precisará ser iniciado sempre que o aplicativo for iniciado. Deverá haver um item no banco de dados antes de você fazer uma chamada PUT. Chame GET para garantir a existência de um item no banco de dados antes de fazer uma chamada PUT.

Atualize o item pendente que tem a ID = 1 e defina seu nome como "feed fish":

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

A seguinte imagem mostra a atualização do Postman:

Console do Postman mostrando a resposta 204 (sem conteúdo)

O método DeleteTodoItem

Examine o método DeleteTodoItem:

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

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

    return NoContent();
}

Testar o método DeleteTodoItem

Use o Postman para excluir um item pendente:

  • Defina o método como DELETE.
  • Defina o URI do objeto a ser excluído (por exemplo, https://localhost:5001/api/todoitems/1).
  • Selecione Enviar.

Impedir o excesso de postagem

Atualmente, o aplicativo de exemplo expõe todo o objeto TodoItem. Os aplicativos de produção normalmente limitam os dados alimentados e retornados pelo uso de 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 é chamado de DTO (Objeto de Transferência de Dados), modelo de entrada ou modelo de exibição. O DTO é usado neste artigo.

Um DTO pode ser usado para:

  • Impedir o excesso de postagem.
  • Ocultar as propriedades que os clientes não devem exibir.
  • Omitir algumas propriedades para reduzir o tamanho da carga.
  • Nivelar gráficos de objetos que contenham objetos aninhados. Os grafos de objeto nivelados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem de 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 ocultado neste aplicativo, mas um aplicativo administrativo poderia optar por mostrá-lo.

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

Crie um modelo de DTO:

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

Atualize o TodoItemsController para usar TodoItemDTO:

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

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

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

    return ItemToDTO(todoItem);
}

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

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

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

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

    return NoContent();
}

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

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

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

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

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

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

    return NoContent();
}

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

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

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

Chamar a API Web com o JavaScript

Confira Tutorial: Chamar uma API Web do ASP.NET Core com o JavaScript.

Adicionar suporte de autenticação a uma API Web

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

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

  • AaaS (autenticação como serviço)
  • SSO (logon único) em vários tipos de aplicativo
  • Controle de acesso para APIs
  • Federation Gateway

Importante

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

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

Publicar no Azure

Para obter informações sobre como implantar no Azure, consulte Início Rápido: Implantar um aplicativo Web ASP.NET.

Recursos adicionais

Exibir ou baixar o código de exemplo para este tutorial. Consulte como baixar.

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

Este tutorial ensina os conceitos básicos da criação de uma API 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 na escolha entre APIs mínimas e APIs baseadas em controlador, confira a visão geral das APIs. Para obter um tutorial sobre como criar uma API mínima, confira Tutorial: Criar uma API mínima com ASP.NET Core.

Neste tutorial, você aprenderá como:

  • Criar um projeto de API Web.
  • Adicione uma classe de modelo e um contexto de banco de dados.
  • Faça scaffold de um controlador com métodos CRUD.
  • Configure o roteamento, os caminhos de URL e os valores retornados.
  • Chamar a API Web com o Postman.

No final, você terá uma API Web que pode gerenciar itens de "tarefas pendentes" armazenados em um banco de dados.

Visão geral

Este tutorial cria a seguinte API:

API Descrição Corpo da solicitação Corpo da resposta
GET /api/todoitems Obter todos os itens de tarefas pendentes Nenhum Matriz de itens de tarefas pendentes
GET /api/todoitems/{id} Obter um item por ID Nenhum Item de tarefas pendentes
POST /api/todoitems Adicionar um novo item Item de tarefas pendentes Item de tarefas pendentes
PUT /api/todoitems/{id} Atualizar um item existente Item de tarefas pendentes Nenhum
DELETE /api/todoitems/{id}     Excluir um item Nenhum Nenhum

O diagrama a seguir mostra o design do aplicativo.

O cliente é representado por uma caixa à esquerda. Ele envia uma solicitação e recebe uma resposta do aplicativo, uma caixa desenhada à direita. Dentro da caixa do aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação é recebida 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 para o cliente na resposta.

Pré-requisitos

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Selecione o modelo Aplicativo Web ASP.NET Core e clique em Próximo.
  • Nomeie o projeto como TodoApi e clique em Criar.
  • Na caixa de diálogo Criar um aplicativo Web ASP.NET Core, confirme se .NET Core e ASP.NET Core 3.1 estão selecionados. Selecione o modelo API e clique em Criar.

Caixa de diálogo Novo projeto do VS

Observação

Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

Testar a API

O modelo de projeto cria uma API WeatherForecast. Chame o método Get em um navegador para testar o aplicativo.

Pressione CTRL+F5 para executar o aplicativo. O Visual Studio inicia um navegador e navega para https://localhost:<port>/weatherforecast, em que <port> é um número de porta escolhido aleatoriamente.

Se você receber uma caixa de diálogo perguntando se você deve confiar no certificado do IIS Express, selecione Sim. Na caixa de diálogo Aviso de Segurança exibida em seguida, selecione Sim.

É retornado um JSON semelhante ao seguinte:

[
    {
        "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 gerenciados pelo aplicativo. O modelo para esse aplicativo é uma única classe TodoItem.

  • No Gerenciador de Soluções, clique com o botão direito do mouse no nome do projeto. Selecione Adicionar>Nova Pasta. Nomeie a pasta Models.

  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Dê à classe o nome TodoItem e selecione Adicionar.

  • Substitua o código do modelo pelo seguinte código:

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

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

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

Adicionar um contexto de banco de dados

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

Adicionar pacotes do NuGet

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

Gerenciador de Pacotes NuGet

Adicione o contexto de banco de dados TodoContext

  • Clique com o botão direito do mouse na pasta Models e selecione Adicionar>Classe. Nomeie a classe como 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; }
        }
    }
    

Registrar o contexto de banco de dados

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

Atualize Startup.cs com o seguinte código realçado:

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

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

        public IConfiguration Configuration { get; }

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

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

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

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

O código anterior:

  • Remove as declarações using não utilizadas.
  • Adiciona o contexto de banco de dados ao contêiner de DI.
  • Especifica que o contexto de banco de dados usará um banco de dados em memória.

Faça scaffold de um controlador

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

  • Selecione Adicionar>Novo item com scaffold.

  • 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 Modelo.
    • Selecione TodoContext (TodoApi.Models) na classe Contexto de Dados.
    • Selecione Adicionar.

O código gerado:

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

Os modelos do ASP.NET Core para:

  • Os controladores com exibições incluem [action] no modelo de rota.
  • Os controladores de API não incluem [action] no modelo de rota.

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

Examine o método criar do PostTodoItem

Substitua a instrução return no PostTodoItem para usar o operador nameof:

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

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

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

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

O método CreatedAtAction:

  • retorna um código de status HTTP 201 em caso de êxito. HTTP 201 é a resposta padrão para um método HTTP POST que cria um novo recurso no servidor.
  • Adiciona um cabeçalho de Local à resposta. O cabeçalho Location especifica o URI do item de tarefas pendentes recém-criado. Para obter mais informações, confira 201 Criado.
  • Faz referência à ação GetTodoItem para criar o URI de Location do cabeçalho. A palavra-chave nameof do C# é usada para evitar o hard-coding do nome da ação, na chamada CreatedAtAction.

Instale o Postman

Este tutorial usa o Postman para testar a API Web.

  • Instale o Postman
  • Inicie o aplicativo Web.
  • Inicie o Postman.
  • Desabilite a Verificação do certificado SSL:
    • Postman para Windows: selecione Arquivo>Configurações (guia Geral), desabilite a verificação do certificado SSL.
    • Postman para macOS: selecione Postman>Configurações (guia Geral), desabilite a verificação do certificado SSL.

      Aviso

      Habilite novamente a verificação do certificado SSL depois de testar o controlador.

Teste o PostTodoItem com o Postman

  • Crie uma solicitação.

  • Defina o método HTTP como POST.

  • Defina o URI como https://localhost:<port>/api/todoitems. Por exemplo, https://localhost:5001/api/todoitems.

  • Selecione a guia Corpo.

  • Selecione o botão de opção bruto.

  • Defina o tipo como JSON (application/json).

  • No corpo da solicitação, insira JSON para um item de tarefa pendente:

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

    Postman com a solicitação Create

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

  • Selecione a guia Cabeçalhos no painel Resposta.

  • Copie o valor do cabeçalho Local:

    Guia Cabeçalhos do console do Postman

  • Defina o método HTTP como GET.

  • Defina o URI como https://localhost:<port>/api/todoitems/1. Por exemplo, https://localhost:5001/api/todoitems/1.

  • Selecione Enviar.

Examine os métodos GET

Esses métodos implementam dois pontos de extremidade GET:

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

Teste o aplicativo chamando os dois pontos de extremidade de um navegador ou do Postman. Por exemplo:

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

Uma resposta semelhante à seguinte é produzida pela chamada a GetTodoItems:

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

Teste o GET com o Postman

  • Crie uma solicitação.
  • Defina o método HTTP como GET.
  • Defina o URI da solicitação como https://localhost:<port>/api/todoitems. Por exemplo, https://localhost:5001/api/todoitems.
  • Defina Exibição de dois painéis no Postman.
  • Selecione Enviar.

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

Roteamento e caminhos de URL

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

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

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • Substitua [controller] pelo nome do controlador, que é o nome de classe do controlador menos o sufixo "Controlador" por convenção. Para esta amostra, o nome da classe do controlador é TodoItemsController. Portanto, o nome do controlador é "TodoItems". O roteamento do ASP.NET Core não diferencia maiúsculas de minúsculas.

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

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

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

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

    return todoItem;
}

Valores de retorno

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

Os tipos de retorno ActionResult podem representar uma ampla variedade 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 código de erro 404 NotFound.
  • Caso contrário, o método retornará 200 com um corpo de resposta JSON. Retornar item resulta em uma resposta HTTP 200.

O método PutTodoItem

Examine o método PutTodoItem:

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

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

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

    return NoContent();
}

PutTodoItem é semelhante a PostTodoItem, exceto pelo uso de HTTP PUT. A resposta é 204 (Sem conteúdo). De acordo com a especificação de HTTP, uma solicitação PUT exige que o cliente envie a entidade inteira atualizada, não apenas as alterações. Para dar suporte a atualizações parciais, use HTTP PATCH.

Se você vir um erro ao chamar PutTodoItem, chame GET para garantir que existe um item no banco de dados.

Testar o método PutTodoItem

Este exemplo usa um banco de dados em memória que precisará ser iniciado sempre que o aplicativo for iniciado. Deverá haver um item no banco de dados antes de você fazer uma chamada PUT. Chame GET para garantir a existência de um item no banco de dados antes de fazer uma chamada PUT.

Atualize o item pendente que tem a ID = 1 e defina seu nome como "feed fish":

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

A seguinte imagem mostra a atualização do Postman:

Console do Postman mostrando a resposta 204 (sem conteúdo)

O método DeleteTodoItem

Examine o método DeleteTodoItem:

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

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

    return todoItem;
}

Testar o método DeleteTodoItem

Use o Postman para excluir um item pendente:

  • Defina o método como DELETE.
  • Defina o URI do objeto a ser excluído (por exemplo, https://localhost:5001/api/todoitems/1).
  • Selecione Enviar.

Impedir o excesso de postagem

Atualmente, o aplicativo de exemplo expõe todo o objeto TodoItem. Os aplicativos de produção normalmente limitam os dados alimentados e retornados pelo uso de 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 é chamado de DTO (Objeto de Transferência de Dados), modelo de entrada ou modelo de exibição. O DTO é usado neste artigo.

Um DTO pode ser usado para:

  • Impedir o excesso de postagem.
  • Ocultar as propriedades que os clientes não devem exibir.
  • Omitir algumas propriedades para reduzir o tamanho da carga.
  • Nivelar gráficos de objetos que contenham objetos aninhados. Os grafos de objeto nivelados podem ser mais convenientes para os clientes.

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

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 ocultado neste aplicativo, mas um aplicativo administrativo poderia optar por mostrá-lo.

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

Crie um modelo de DTO:

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

Atualize o TodoItemsController para usar TodoItemDTO:

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

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

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

        return ItemToDTO(todoItem);
    }

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

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

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

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

        return NoContent();
    }

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

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

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

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

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

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

        return NoContent();
    }

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

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

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

Chamar a API Web com o JavaScript

Confira Tutorial: Chamar uma API Web do ASP.NET Core com o JavaScript.

Adicionar suporte de autenticação a uma API Web

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

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

  • AaaS (autenticação como serviço)
  • SSO (logon único) em vários tipos de aplicativo
  • Controle de acesso para APIs
  • Federation Gateway

Importante

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

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

Publicar no Azure

Para obter informações sobre como implantar no Azure, consulte Início Rápido: Implantar um aplicativo Web ASP.NET.

Recursos adicionais

Exibir ou baixar o código de exemplo para este tutorial. Consulte como baixar.

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