Model binding na API
ResumoModel binding mapeia dados da requisição (rota, query string, corpo, cabeçalhos) para os parâmetros da ação. Quando o formato não é padrão — como
ids=1,2,3— implementamos um model binder customizado.
1. Objetivos de Aprendizagem#
- Explicar as fontes de binding e os atributos
[FromRoute],[FromQuery],[FromBody],[FromHeader]. - Implementar um
IModelBinderpara converter uma string de ids em coleção tipada. - Registrar e aplicar o binder customizado.
2. Pré-requisitos#
- Criação de coleção (tópico 23.2).
3. Conceito#
O ASP.NET Core resolve os parâmetros da ação a partir de várias fontes. Com [ApiController], a inferência é automática: tipos complexos vêm do corpo, tipos simples da rota/query. Quando precisamos de um formato especial (uma lista separada por vírgulas na URL), escrevemos um binder customizado implementando IModelBinder.
4. Mão na Massa#
4.1. Setup#
Sem pacotes extras.
4.2. Implementação Passo a Passo#
- Binder para
IEnumerable<Guid>a partir de"1,2,3":
using Microsoft.AspNetCore.Mvc.ModelBinding;
public class ArrayModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (!bindingContext.ModelMetadata.IsEnumerableType)
{
bindingContext.Result = ModelBindingResult.Failed();
return Task.CompletedTask;
}
var provided = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName).ToString();
if (string.IsNullOrWhiteSpace(provided))
{
bindingContext.Result = ModelBindingResult.Success(null);
return Task.CompletedTask;
}
var guids = provided.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(s => Guid.Parse(s.Trim()))
.ToArray();
bindingContext.Result = ModelBindingResult.Success(guids);
return Task.CompletedTask;
}
}
- Aplique com
[ModelBinder]:
[HttpGet("colecao/{ids}", Name = "ProdutoCollection")]
public async Task<IActionResult> GetPorIds(
[ModelBinder(BinderType = typeof(ArrayModelBinder))] IEnumerable<Guid> ids)
{
var produtos = await _service.Produto.GetByIdsAsync(ids, trackChanges: false);
return Ok(produtos);
}
4.3. Executando#
curl http://localhost:5000/api/produtos/colecao/{id1},{id2}
5. Exemplo Completo#
Fluxo completo: POST colecao (9.2) retorna Location com ids=...; GET colecao/{ids} usa o ArrayModelBinder para materializar a lista e buscar os recursos.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
| Validar/tratar entradas malformadas no binder | Deixar Guid.Parse lançar exceção não tratada |
| Usar binder só quando o formato padrão não basta | Reimplementar binding que o framework já faz |
| Limitar a quantidade de ids aceitos | Aceitar milhares de ids numa URL |
Atenção
Guid.Parselança em entrada inválida. PrefiraGuid.TryParsee retorne400de forma controlada para não vazar exceções.
7. Segurança e Produção#
- URLs muito longas (muitos ids) podem estourar limites do servidor/proxy. Considere
POSTcom corpo para lotes grandes.
8. Exercícios#
- Fácil: troque
Guid.ParseporTryParsee falhe graciosamente. - Médio: limite o binder a 50 ids.
- Desafio: generalize o binder para
IEnumerable<T>usandoTypeConverter.
9. Resumo#
Model binding conecta a requisição aos parâmetros da ação; um IModelBinder customizado resolve formatos não padrão como listas separadas por vírgula, com validação segura de entrada.
10. Próximos Passos#
Módulo 24: requisições DELETE, incluindo remoção de pai com filhos.
11. Referências#
- Microsoft Learn — Model binding customizado
- Microsoft Learn — Model binding
- Microsoft Learn — Guid.TryParse