Chegamos a um dos usos mais importantes e demandados do C# no mundo real: servir dados pela rede. Até agora, seus programas conversaram com o terminal ou com um banco de dados. Agora eles vão conversar com o mundo — com navegadores, aplicativos de celular, outros programas — através de uma API REST, que é o alicerce de praticamente todo sistema moderno conectado à internet. Usaremos o ASP.NET Core, a tecnologia web do C#, reconhecida por estar entre as mais rápidas do mundo. E aqui está a boa notícia que atravessa esta fase: construir uma API não exige aprender uma linguagem nova, apenas aplicar tudo o que você já sabe — objetos, LINQ, tratamento de erros — a um novo tipo de programa. Aquele Console.WriteLine que sempre falou com o terminal vai se transformar, agora, numa resposta que viaja pela internet.
O que é uma API, em termos práticos
Antes do código, o conceito. Uma API (das iniciais, em inglês, de "interface de programação de aplicações") web é, na prática, um programa que fica escutando pedidos que chegam pela rede e respondendo a eles. Em vez de interagir com um usuário através de uma tela, ela interage com outros programas através de mensagens. Um aplicativo de celular que mostra a previsão do tempo, por exemplo, não calcula o tempo sozinho — ele pede os dados a uma API pela internet, e a API responde com as informações, que o app então exibe. As APIs são, portanto, os "servidores de dados" que alimentam aplicativos, sites e serviços.
REST é um estilo popular de organizar essa comunicação, baseado em duas ideias simples. A primeira: os recursos — as coisas que o sistema manipula (produtos, usuários, pedidos) — são identificados por endereços (como /produtos, ou /produtos/5 para um produto específico). A segunda: verbos padronizados indicam qual ação realizar com um recurso. Os quatro verbos essenciais correspondem exatamente às operações que você já conhece de bancos de dados:
- GET — ler um recurso (buscar produtos). Não altera nada. - POST — criar um novo recurso (adicionar um produto). - PUT — atualizar um recurso existente. - DELETE — remover um recurso.
Os dados trafegam, quase sempre, no formato JSON — aquele texto estruturado que já vimos, parecido com um objeto. A ideia geral: um programa envia um pedido como "GET /produtos", e sua API responde com um JSON contendo os produtos. É assim que aplicativos buscam seus dados, que sites carregam conteúdo, que serviços conversam entre si — um vocabulário universal da web.
Passo 1: criar o projeto web
O C# oferece um modelo pronto para APIs. Criamos e executamos:
dotnet new webapi -n LojaApi # cria um projeto de API web
cd LojaApi
dotnet run
Ao executar, o terminal mostra que a aplicação está escutando num endereço (algo como http://localhost:5000). Diferentemente dos programas anteriores, ele não termina — fica no ar, aguardando pedidos, como um servidor deve fazer. Para pará-lo, pressione Ctrl+C. Essa é a primeira diferença conceitual de um programa web: ele não roda e encerra; ele permanece, atendendo a quem o procurar.
Passo 2: escrevendo as rotas
O ASP.NET Core moderno permite escrever uma API de forma concisa, declarando rotas diretamente — cada rota associa um verbo e um endereço a uma função que produz a resposta. Vamos construir uma API de produtos, começando com uma lista em memória (o banco de dados virá na próxima aula). Substitua o conteúdo do Program.cs:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// "Banco" temporário em memória, só para este primeiro exemplo:
var produtos = new List<Produto>
{
new Produto { Id = 1, Nome = "Caderno", Preco = 12.5m },
new Produto { Id = 2, Nome = "Caneta", Preco = 2m }
};
// ROTA GET /produtos — retorna todos os produtos (vira JSON automaticamente).
app.MapGet("/produtos", () => produtos);
// ROTA GET /produtos/{id} — retorna um produto específico pelo id.
app.MapGet("/produtos/{id}", (int id) =>
{
var produto = produtos.FirstOrDefault(p => p.Id == id); // LINQ (fase de coleções)
return produto != null ? Results.Ok(produto) : Results.NotFound(); // trata ausência
});
// ROTA POST /produtos — cria um novo produto a partir do JSON recebido.
app.MapPost("/produtos", (Produto novo) =>
{
produtos.Add(novo);
return Results.Created($"/produtos/{novo.Id}", novo); // 201 Criado
});
// ROTA DELETE /produtos/{id} — remove um produto.
app.MapDelete("/produtos/{id}", (int id) =>
{
var removidos = produtos.RemoveAll(p => p.Id == id);
return removidos > 0 ? Results.NoContent() : Results.NotFound();
});
app.Run(); // coloca a aplicação para escutar (não retorna — fica servindo)
// A classe que representa um produto; vira JSON automaticamente.
class Produto
{
public int Id { get; set; }
public string Nome { get; set; } = "";
public decimal Preco { get; set; }
}
Leia o que cada Map... faz: associa um verbo + endereço a uma função que produz a resposta. O MapGet("/produtos", () => produtos) diz "quando chegar um GET em /produtos, devolva a lista de produtos" — e o ASP.NET Core transforma automaticamente essa lista de objetos em JSON. Note como tudo o que você já sabe reaparece: o FirstOrDefault é LINQ (fase de coleções), a classe Produto é da fase de objetos, o tratamento de ausência (!= null) é o cuidado com nulos da fase de robustez. A novidade é apenas o empacotamento dessas capacidades como rotas web.
Passo 3: os códigos de resposta
Repare nos Results.*: Ok, NotFound, Created, NoContent. Eles representam os códigos de status HTTP — a forma padronizada de a API comunicar o que aconteceu com o pedido. Os essenciais:
- 200 OK (Results.Ok) — deu certo, aqui está o resultado. - 201 Criado (Results.Created) — o recurso foi criado com sucesso. - 204 Sem Conteúdo (Results.NoContent) — deu certo, sem nada a retornar (típico de uma remoção). - 404 Não Encontrado (Results.NotFound) — o recurso pedido não existe. - 400 Pedido Inválido (Results.BadRequest) — o pedido estava malformado (veremos adiante).
Usar o código certo é parte essencial de uma boa API: um programa que recebe 404 sabe que o produto não existe; um que recebe 201 sabe que a criação funcionou. É a diferença entre uma API que comunica claramente e uma que apenas devolve dados crus. Repare na coerência: GET devolve dados (200), POST confirma a criação (201), DELETE confirma sem conteúdo (204) — essa previsibilidade é o que torna as APIs REST fáceis de consumir.
Passo 4: testar a API
Como testar sem um aplicativo cliente? O modelo webapi já inclui, em ambiente de desenvolvimento, uma página web interativa (chamada Swagger ou OpenAPI) que lista suas rotas e permite chamá-las direto do navegador. Ao rodar dotnet run, acesse o endereço indicado (geralmente com /swagger ao final) e você verá cada rota, podendo testá-las com cliques. Alternativamente, pela linha de comando, a ferramenta curl faz pedidos:
curl http://localhost:5000/produtos # GET: lista os produtos
curl http://localhost:5000/produtos/1 # GET: o produto de id 1
Ver o JSON retornar no terminal é o momento em que a abstração vira concreta: seu programa C# está respondendo a pedidos de rede reais. O Console.WriteLine virou uma resposta que viaja pela internet — a promessa da aula, cumprida.
O C# passou a servir dados pela rede. Uma API expõe operações que outros programas consomem, e as Minimal APIs do ASP.NET Core permitem declarar rotas de forma concisa, diretamente no arquivo principal do projeto.
O vocabulário da web ficou assentado: recursos identificados por caminhos, verbos que indicam a intenção da operação e códigos de resposta que comunicam o desfecho de forma padronizada. E a parte que costuma surpreender é o quanto já era conhecido — por baixo das rotas há classes, coleções e consultas. A camada nova é fina; o que a sustenta é o C# que já estava dominado.
Fontes e leituras recomendadas
- Visão geral do ASP.NET Core — a introdução oficial à tecnologia web do C#; a base desta fase.
- Tutorial de APIs mínimas — o passo a passo oficial de criação de uma API, no espírito desta aula.
- Verbos HTTP e design REST — o guia de design de APIs REST, recursos e verbos.
- Resultados e códigos de status — como retornar os códigos HTTP corretos.
- Swagger/OpenAPI no ASP.NET Core — a página interativa para testar a API durante o desenvolvimento.
Exercícios
Exercício 1
Crie um projeto webapi, execute-o, e acesse a página do Swagger no navegador. Identifique as rotas que o modelo já traz e teste uma delas pela interface. Descreva o que você viu e o que significa a aplicação "ficar escutando" em vez de encerrar.
Ver resposta
✓ Resposta: Ao rodar o modelo e acessar /swagger, aparece uma página interativa listando as rotas que o modelo padrão traz (geralmente um exemplo de previsão do tempo). Clicando numa rota e em "executar", o Swagger envia o pedido e mostra a resposta em JSON e o código de status, permitindo testar a API sem escrever um cliente. A aplicação "ficar escutando" em vez de encerrar significa que, ao contrário dos programas anteriores que rodavam e terminavam, um programa web permanece em execução aguardando pedidos que chegam pela rede, atendendo cada um conforme chega — é o comportamento próprio de um servidor.
Exercício 2
Substitua o Program.cs pela API de produtos da aula e teste as rotas (GET da lista, GET por id, POST, DELETE) usando o Swagger ou o curl. Confirme que o POST cria um produto que aparece no GET seguinte.
Ver resposta
✓ Resposta: Após substituir o Program.cs, testando: GET /produtos retorna os dois produtos iniciais; GET /produtos/1 retorna o primeiro; um POST /produtos com um JSON de novo produto retorna 201 e, no GET /produtos seguinte, o novo produto aparece na lista; DELETE /produtos/{id} remove e retorna 204. O fluxo confirma as operações básicas funcionando sobre a lista em memória.
Exercício 3
Adicione uma rota GET /produtos/baratos que retorne, usando LINQ, apenas os produtos com preço abaixo de 10. Explique como essa rota reaproveita diretamente o conhecimento da fase de coleções.
Ver resposta
✓ Resposta: A rota:
app.MapGet("/produtos/baratos", () => produtos.Where(p => p.Preco < 10));
Ela reaproveita a fase de coleções diretamente: o Where(p => p.Preco < 10) é o mesmo operador LINQ de filtragem que você usa sobre qualquer coleção, e o ASP.NET Core transforma o resultado em JSON automaticamente. Construir a rota foi apenas "empacotar" uma consulta LINQ que você já sabia escrever como uma resposta web.
Exercício 4
Para cada uma das quatro rotas da API, diga qual código de status ela retorna em caso de sucesso e por que esse código é o apropriado (por exemplo, por que o DELETE retorna 204 e não 200).
Ver resposta
✓ Resposta: GET /produtos e GET /produtos/{id} retornam 200 OK em sucesso, pois entregam o recurso solicitado. POST /produtos retorna 201 Criado, indicando que um novo recurso foi criado (e informando seu endereço) — mais específico que um genérico 200. DELETE /produtos/{id} retorna 204 Sem Conteúdo porque a remoção foi bem-sucedida mas não há conteúdo a devolver; o 204 comunica exatamente "deu certo, e não há corpo na resposta", enquanto um 200 sugeriria que há um resultado a retornar, o que não é o caso de uma remoção.
Exercício 5
Sem código: explique, com suas palavras, o que significa dizer que uma API REST "organiza a comunicação em torno de recursos e verbos". Dê o exemplo de como buscar e como remover o produto de id 7 seriam expressos (o verbo e o endereço de cada um).
Ver resposta
✓ Resposta: Significa que a API é organizada em torno das coisas que ela manipula (os recursos, como "produtos"), cada uma identificada por um endereço, e das ações sobre elas, expressas pelos verbos HTTP. Assim, o mesmo endereço representa o recurso, e o verbo diz o que fazer com ele. Buscar o produto de id 7 seria GET /produtos/7 (o verbo GET = ler, o endereço identifica o produto 7); removê-lo seria DELETE /produtos/7 (mesmo endereço, mas o verbo DELETE = remover). A clareza vem de separar "qual recurso" (o endereço) de "qual ação" (o verbo), tornando a comunicação previsível e padronizada.