Modele recursos e operações HTTP

Use caminhos que representem recursos, como /api/pedidos e /api/pedidos/{id}, e métodos HTTP para expressar a operação: GET consulta, POST cria, PUT substitui, PATCH altera parcialmente e DELETE remove. Mantenha nomes e formatos coerentes; não transforme cada verbo de negócio em uma rota sem considerar o recurso e seu contrato.

Respeite a semântica de cada método. GET deve ser seguro e não provocar mudanças; PUT e DELETE são idempotentes pela semântica HTTP, embora a implementação precise preservar esse comportamento. POST pode exigir uma chave de idempotência quando clientes podem repetir uma solicitação após timeout.

Separe o contrato HTTP do domínio

Receba e devolva DTOs próprios da API, em vez de expor entidades de persistência. Isso reduz acoplamento, evita publicar campos internos acidentalmente e permite validar o formato de entrada. Valide limites e formato na borda; regras de negócio e invariantes continuam no domínio ou na aplicação.

app.MapPost("/api/pedidos", async (
    CriarPedidoRequest request,
    IPedidoService pedidos,
    CancellationToken cancellationToken) =>
{
    if (request.Itens.Count == 0)
        return Results.ValidationProblem(new Dictionary<string, string[]>
        {
            ["itens"] = ["Inclua ao menos um item."]
        });

    var criado = await pedidos.CriarAsync(request, cancellationToken);
    return Results.Created($"/api/pedidos/{criado.Id}", criado);
});

O exemplo é um recorte: a aplicação também deve limitar quantidades e tamanhos, tratar autorização e aplicar as regras do caso de uso. Não confie em validação feita apenas pelo cliente.

Escolha respostas HTTP que comuniquem o resultado

Retorne 201 Created ao criar um recurso e, quando apropriado, inclua seu endereço em Location; 200 para uma resposta com representação; 204 quando não há corpo; 400 para entrada inválida; 401 quando falta autenticação; 403 quando a identidade não tem permissão; 404 para recurso ausente e 409 para conflito de estado. Não use 200 com uma mensagem de erro no corpo.

Configure respostas de erro consistentes, por exemplo com AddProblemDetails e tratamento de exceções do ASP.NET Core. Não exponha stack traces, SQL, tokens nem mensagens internas. Registre contexto seguro nos logs e devolva ao cliente apenas detalhes apropriados.

Paginação, filtros e limites

Uma coleção não deve retornar um volume ilimitado de registros. Defina paginação com tamanho máximo controlado pelo servidor, ordenação estável e filtros documentados. Offset é simples para conjuntos menores; cursores/keyset podem funcionar melhor em listas grandes e mutáveis. Seja explícito sobre o formato de metadados e o que acontece quando um filtro é inválido.

Evolua o contrato sem quebrar clientes

Adicionar campos opcionais costuma ser mais compatível que renomear ou mudar o significado de campos existentes. Para mudanças incompatíveis, estabeleça uma estratégia de versão e um período de migração observável. Documente exemplos e erros, use testes de contrato quando produtor e consumidores evoluem separadamente e evite versionar toda mudança aditiva.

Autenticação e autorização devem ser aplicadas no servidor, por políticas e permissões adequadas ao recurso — veja autenticação e autorização em ASP.NET Core. Teste o pipeline HTTP com testes de integração.

Documentação e leituras relacionadas