Moldando os campos retornados
ResumoData shaping permite ao cliente pedir apenas os campos que precisa (
fields=nome,preco), reduzindo o payload. Implementamos com reflexão e um dicionário de propriedades (ExpandoObject).
1. Objetivos de Aprendizagem#
- Explicar o benefício de data shaping.
- Implementar shaping por reflexão sobre o DTO.
- Lidar com serialização XML/JSON do resultado moldado.
2. Pré-requisitos#
- Ordenação dinâmica (Módulo 33).
3. Conceito#
Data shaping devolve subconjuntos de campos por objeto conforme o cliente pede. O porquê: economizar banda e permitir que diferentes clientes obtenham exatamente o que consomem, sem múltiplos endpoints. Implementamos com reflexão: lemos as propriedades solicitadas do DTO e montamos um ExpandoObject com apenas elas.
4. Mão na Massa#
4.1. Setup#
Sem pacotes novos.
4.2. Implementação Passo a Passo#
- Serviço de shaping:
using System.Dynamic;
using System.Reflection;
public class DataShaper<T> where T : class
{
private readonly PropertyInfo[] _properties =
typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
public IEnumerable<ExpandoObject> ShapeData(IEnumerable<T> entities, string? fields)
{
var required = GetRequiredProperties(fields);
return entities.Select(e => FetchData(e, required));
}
private IEnumerable<PropertyInfo> GetRequiredProperties(string? fields)
{
if (string.IsNullOrWhiteSpace(fields)) return _properties;
var wanted = fields.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(f => f.Trim());
return _properties.Where(p =>
wanted.Contains(p.Name, StringComparer.OrdinalIgnoreCase));
}
private static ExpandoObject FetchData(T entity, IEnumerable<PropertyInfo> props)
{
var shaped = new ExpandoObject();
foreach (var p in props)
((IDictionary<string, object?>)shaped)[p.Name] = p.GetValue(entity);
return shaped;
}
}
- Registro e uso:
builder.Services.AddScoped<DataShaper<ProdutoDto>>();
// no serviço/controller:
var shaped = _shaper.ShapeData(produtosDto, parameters.Fields);
return Ok(shaped);
4.3. Executando#
curl "http://localhost:5000/api/produtos?fields=nome,preco"
5. Exemplo Completo#
Campos inexistentes são ignorados; sem fields, retornamos o DTO completo. O ExpandoObject serializa naturalmente em JSON.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
| Moldar sobre o DTO, não sobre a entidade | Expor campos internos via reflexão na entidade |
| Ignorar campos desconhecidos | Lançar exceção para cada campo inválido |
| Usar shaping só onde há ganho real | Complicar todos os endpoints com shaping |
Atençãoshaping quebra a tipagem forte da resposta (vira
ExpandoObject), dificultando documentação (Swagger) e clientes tipados. Use com parcimônia e documente bem.
7. Segurança e Produção#
- Como refletimos sobre o DTO (não a entidade), campos sensíveis nunca entram no shaping.
- XML shaping é mais complexo (o
ExpandoObjectnão serializa bem em XML) — trate esse cenário à parte se oferecer XML.
8. Exercícios#
- Fácil: peça só
id,nomee valide o resultado. - Médio: garanta que o campo chave (
Id) esteja sempre presente. - Desafio: resolva a serialização XML do objeto moldado.
9. Resumo#
Data shaping devolve apenas os campos pedidos usando reflexão sobre o DTO e ExpandoObject, economizando banda ao custo de tipagem forte na resposta.
10. Próximos Passos#
Módulo 35: HATEOAS, elevando a API ao nível 3 de maturidade REST.
11. Referências#
- Microsoft Learn — Reflexão em .NET
- Microsoft Learn — ExpandoObject
- Microsoft Learn — System.Text.Json