Compartilhar via


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.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. 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, veja Visão geral de 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>New Scaffolded Item.

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

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

    • Selecione TodoItem (TodoApi.Models) na classe 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("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), 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 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.

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 às ID solicitadas, o método retornará um código de erro status 404 NotFound.
  • 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

Confira 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: