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

Por Rick Anderson e Kirk Larkin

Este tutorial ensina os conceitos básicos da criação de uma API Web com o ASP.NET Core.

Neste tutorial, você aprenderá a:

  • 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.
  • Chame 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 de aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação entra no controlador do aplicativo e as operações de leitura/gravação ocorrem entre o controlador e a camada de acesso a dados. O modelo é serializado e retornado ao cliente na resposta.

Pré-requisitos

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Insira API Web na caixa de pesquisa.
  • Selecione o modelo de API Web ASP.NET Core e selecione Avançar.
  • Na caixa de diálogo Configurar seu 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 para Usar controladores (desmarcar para usar 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 WeatherForecast API com suporte para o 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 o SSL. Para evitar avisos SSL no navegador, você pode optar por confiar no certificado autoassinado gerado IIS Express. Deseja confiar no certificado SSL 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, consulte o Firefox SEC_ERROR_INADEQUATE_KEY_USAGE erro de certificado.

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

A página /swagger/index.html swagger é exibida. Selecione EXECUTAR>>. A página é exibida:

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

Se a página do Swagger não aparecer, consulte 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, consulte ASP.NET Core documentação da API Web com o Swagger/OpenAPI.

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

JSON semelhante ao exemplo a seguir é retornado:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

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 altera 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.

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

  • Clique com o botão direito do mouse na Models pasta 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 ir para qualquer lugar do projeto, mas a Models pasta é 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 NuGet

  • No menu Ferramentas , selecione Gerenciar Pacotes NuGet do Gerenciador de Pacotes > NuGet para 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.

Adicione o contexto de banco de dados TodoContext

  • Clique com o botão direito do mouse na Models pasta 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 do 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:

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 não utilizados 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 Scaffolded.

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

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

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

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

O código gerado:

  • Marca a classe com o [ApiController] atributo. Esse atributo indica se o controlador responde às solicitações da API Web. Para obter informações sobre comportamentos específicos que o atributo habilita, consulte Criar APIs Web com 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 de ASP.NET Core para:

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

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

Atualizar o método de criação do PostTodoItem

Atualize a instrução de retorno para PostTodoItem 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 [HttpPost] atributo. 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 se bem-sucedido. 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 seguinte comando em um prompt de comando:

    dotnet tool install -g Microsoft.dotnet-httprepl
    
  • 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 localização, copie-o 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ê tiver terminado a sessão httprepl anterior, substitua connecthttprepl 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 /api/todoitems/{id} rota. Teste a /api/todoitems rota:

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 da "{id}" URL é fornecido ao método em seu id parâmetro.

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

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

    return todoItem;
}

Valores retornados

O tipo de retorno e métodos é o GetTodoItemsGetTodoItemtipo ActionResult<T>. ASP.NET Core serializa automaticamente o objeto para JSON e grava o JSON no corpo da mensagem de resposta. O código de resposta para esse tipo de retorno é 200 OK, supondo que não haja exceções 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 de status NotFound404.
  • Caso contrário, o método retornará 200 com um JScorpo de resposta ON. 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 ao chamar 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 na memória que deve ser inicializado 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 que haja um item no banco de dados antes de fazer uma chamada PUT.

Atualize o item pendente que tem 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 pendente 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 TodoItem o objeto. Normalmente, os aplicativos de produção limitam os dados que são de entrada e retornados usando um subconjunto do modelo. Há várias razões por trás disso, e a segurança é uma das principais. O subconjunto de um modelo geralmente é conhecido como um 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 propriedades que os clientes não devem exibir.
  • Omita algumas propriedades para reduzir o tamanho da carga.
  • Nivelar grafos de objeto que contêm objetos aninhados. Grafos de objeto nivelados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem de DTO, atualize a TodoItem classe 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 estar oculto deste aplicativo, mas um aplicativo administrativo pode optar por expô-lo.

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

Criar 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 nem obter o campo secreto.

Chamar a API Web com o JavaScript

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

Neste tutorial, você aprenderá a:

  • 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 de aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação entra no controlador do aplicativo e as operações de leitura/gravação ocorrem entre o controlador e a camada de acesso a dados. O modelo é serializado e retornado ao cliente na resposta.

Pré-requisitos

Criar um projeto Web

  • No menu Arquivo, selecione Novo>Projeto.
  • Selecione o modelo de API Web ASP.NET Core e clique em Avançar.
  • Nomeie o projeto como TodoApi e clique em Criar.
  • Na caixa de diálogo Criar um novo aplicativo Web ASP.NET Core, confirme se o .NET Core e o 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 WeatherForecast API com suporte para o 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 o SSL. Para evitar avisos SSL no navegador, você pode optar por confiar no certificado autoassinado gerado IIS Express. Deseja confiar no certificado SSL 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, consulte o Firefox SEC_ERROR_INADEQUATE_KEY_USAGE erro de certificado.

O Visual Studio é iniciado:

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

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

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

Se a página swagger não aparecer, consulte 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, consulte ASP.NET Core documentação da API Web com o Swagger/OpenAPI.

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

JSON semelhante ao seguinte é retornado:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

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 altera a URL que é iniciada no 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.

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

  • Clique com o botão direito do mouse na Models pasta 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 ir para qualquer lugar no projeto, mas a Models pasta é 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 NuGet

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

Gerenciador de Pacotes NuGet

Adicione o contexto de banco de dados TodoContext

  • Clique com o botão direito do mouse na Models pasta 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 do 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:

// 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 Scaffolded.

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

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

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

O código gerado:

  • Marca a classe com o [ApiController] atributo. Esse atributo indica se o controlador responde às solicitações da API Web. Para obter informações sobre comportamentos específicos que o atributo habilita, consulte Criar APIs Web com 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 de ASP.NET Core para:

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

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

Atualizar o método de criação do PostTodoItem

Atualize a instrução de retorno para PostTodoItem 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("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 [HttpPost] atributo. 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 se bem-sucedido. 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.

Instale o Postman

Este tutorial usa o Postman para testar a API Web.

  • Instalar o Postman
  • Inicie o aplicativo Web.
  • Inicie o Postman.
  • Desabilitar a verificação do certificado SSL:
    • Postman para Windows: Selecione Configurações de Arquivo> (guia Geral),desabilitea verificação de certificado SSL.
    • Postman para macOS: selecione Configurações do Postman> (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 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 localização 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 de 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 da "{id}" URL é fornecido ao método em seu id parâmetro.

// 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 retornados

O tipo de retorno e métodos é o GetTodoItemsGetTodoItemtipo ActionResult<T>. ASP.NET Core serializa automaticamente o objeto para JSON e grava o JSON no corpo da mensagem de resposta. O código de resposta para esse tipo de retorno é 200 OK, supondo que não haja exceções 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 de status NotFound404.
  • Caso contrário, o método retornará 200 com um JScorpo de resposta ON. 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 na memória que deve ser inicializado 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 que haja um item no banco de dados antes de fazer uma chamada PUT.

Atualize o item pendente que tem 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 para excluir (por exemplo https://localhost:5001/api/todoitems/1).
  • Selecione Enviar.

Impedir o excesso de postagem

Atualmente, o aplicativo de exemplo expõe todo TodoItem o objeto. Normalmente, os aplicativos de produção limitam os dados que são de entrada e retornados usando um subconjunto do modelo. Há várias razões por trás disso e a segurança é uma das principais. O subconjunto de um modelo geralmente é conhecido como um 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 propriedades que os clientes não devem exibir.
  • Omita algumas propriedades para reduzir o tamanho da carga.
  • Nivelar grafos de objeto que contêm objetos aninhados. Gráficos de objeto achatado podem ser mais convenientes para os clientes.

Para demonstrar a abordagem DTO, atualize a TodoItem classe 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 de segredo precisa estar oculto deste aplicativo, mas um aplicativo administrativo pode optar por expô-lo.

Verifique se você pode postar e obter o campo de segredo.

Criar um modelo DTO:

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

Atualize o TodoItemsController para uso 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 de segredo.

Chamar a API Web com o JavaScript

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

Neste tutorial, você aprenderá a:

  • 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 de aplicativo, três caixas representam o controlador, o modelo e a camada de acesso a dados. A solicitação entra no controlador do aplicativo e ocorrem operações de leitura/gravação entre o controlador e a camada de acesso a dados. O modelo é serializado e retornado ao 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 novo aplicativo Web ASP.NET Core, confirme se o .NET Core e o 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.

JSON semelhante ao seguinte é retornado:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

Adicionar uma classe de modelo

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

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

  • Clique com o botão direito do mouse na Models pasta 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 ir para qualquer lugar do projeto, mas a Models pasta é 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 NuGet

  • No menu Ferramentas , selecione Gerenciar Pacotes NuGet do Gerenciador de Pacotes > NuGet para 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 Models pasta 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 do 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 Scaffolded.

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

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

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

O código gerado:

  • Marca a classe com o [ApiController] atributo. Esse atributo indica se o controlador responde às solicitações da API Web. Para obter informações sobre comportamentos específicos que o atributo habilita, consulte Criar APIs Web com 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 de ASP.NET Core para:

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

Quando o [action] token não está no modelo de rota, o nome da ação é 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("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 [HttpPost] atributo. 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.

Instale o Postman

Este tutorial usa o Postman para testar a API Web.

  • Instalar o Postman
  • Inicie o aplicativo Web.
  • Inicie o Postman.
  • Desabilitar a verificação do certificado SSL:
    • Postman para Windows: Postman para Configurações de Arquivo> do Windows (guia Geral),desabilitea verificação de certificado SSL.
    • Postman para macOS: Postman para Configurações do Postman> do Windows (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 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 de 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 da "{id}" URL é fornecido ao método em seu id parâmetro.

// 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 retornados

O tipo de retorno e métodos é o GetTodoItemsGetTodoItemtipo ActionResult<T>. ASP.NET Core serializa automaticamente o objeto para JSON e grava o JSON no corpo da mensagem de resposta. O código de resposta para esse tipo de retorno é 200, 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 JScorpo de resposta ON. 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 na memória que deve ser inicializado sempre que o aplicativo é iniciado. Deverá haver um item no banco de dados antes de você fazer uma chamada PUT. Chame GET para garantir que haja um item no banco de dados antes de fazer uma chamada PUT.

Atualize o item de tarefas pendentes que tem 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 a postagem excessiva

Atualmente, o aplicativo de exemplo expõe todo TodoItem o objeto. Normalmente, os aplicativos de produção limitam os dados que são de entrada e retornados usando um subconjunto do modelo. Há várias razões por trás disso e a segurança é importante. 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 a postagem excessiva.
  • Ocultar propriedades que os clientes não devem exibir.
  • Omita algumas propriedades para reduzir o tamanho da carga.
  • Nivelar grafos de objeto que contêm objetos aninhados. Grafos de objeto nivelados podem ser mais convenientes para os clientes.

Para demonstrar a abordagem de DTO, atualize a TodoItem classe 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 estar oculto deste aplicativo, mas um aplicativo administrativo pode optar por expô-lo.

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

Criar 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 nem obter o campo secreto.

Chamar a API Web com o JavaScript

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

Adicionar suporte à autenticação a uma API Web

Identity ASP.NET Core adiciona a funcionalidade de logon da interface do usuário para ASP.NET Core aplicativos Web. Para proteger APIs Web e SPAs, use um dos seguintes:

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

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

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, consulte a documentação do Duende Identity Server (site do 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 saber mais, consulte os recursos a seguir: