Desserialização em endpoints | API | CSharp
- Desserialização em endpoints | API | CSharp
-
Youtube - Zoran C#: 🔗 Clique aqui
-
Código demonstrado
app.MapPost("/streaming", async (HttpContext context, [FromQuery] int? delayUs) =>
{
using var body = context.Request.Body;
var jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
IAsyncEnumerable<Person?> people =
JsonSerializer.DeserializeAsyncEnumerable<Person>(body, jsonOptions);
TimeSpan delayPerItem = delayUs is null
? TimeSpan.Zero
: TimeSpan.FromMicroseconds(delayUs.Value);
TimeSpan pendingDelay = TimeSpan.Zero;
TimeSpan threshold = TimeSpan.FromMilliseconds(1);
await foreach (var person in people)
{
if (person is null) continue;
// Simulate persistence delay or similar
pendingDelay += delayPerItem;
if (pendingDelay >= threshold)
{
await Task.Delay(pendingDelay);
pendingDelay = TimeSpan.Zero;
}
}
return Results.Ok();
});
Contexto
No ASP.NET Core, quando um endpoint recebe um objeto via body da requisição, o framework realiza automaticamente a desserialização:
- JSON → Objeto C#
- Executado antes do método do endpoint
- Utiliza
System.Text.Jsonpor padrão
Exemplo comum:
app.MapPost("/dados", (MeuDto dto) =>
{
return Results.Ok(dto);
});
Nesse modelo:
- O objeto é totalmente materializado
- Todo o JSON é parseado
- Existe custo de CPU e memória
Como a desserialização funciona
O pipeline executa:
- Leitura do body
- Parsing completo do JSON
- Criação de objetos em memória
- Binding para o parâmetro do método
Características:
- Sempre desserializa o payload completo
- Independente do uso parcial dos dados
- Pode gerar alocações desnecessárias
Problema
Em determinados cenários, esse comportamento pode gerar:
- Alto consumo de memória
- Pressão no Garbage Collector
- Uso desnecessário de CPU
- Latência adicional
Principal ponto:
- O custo ocorre mesmo quando você usa apenas parte dos dados
Estratégias alternativas
Leitura manual do body
Permite controle total sem desserialização automática:
app.MapPost("/dados", async (HttpContext context) =>
{
using var reader = new StreamReader(context.Request.Body);
var json = await reader.ReadToEndAsync();
return Results.Ok();
});
Parsing parcial com JsonDocument
Permite acessar apenas os campos necessários:
app.MapPost("/dados", async (HttpContext context) =>
{
using var document = await JsonDocument.ParseAsync(context.Request.Body);
var nome = document.RootElement.GetProperty("nome").GetString();
return Results.Ok(new { nome });
});
Uso de JsonElement
Evita criação de classes:
app.MapPost("/dados", (JsonElement json) =>
{
var nome = json.GetProperty("nome").GetString();
return Results.Ok(new { nome });
});
Comparação de abordagens
| Abordagem | Alocação | Performance | Complexidade | Validação automática |
|---|---|---|---|---|
| DTO (padrão) | Alta | Média | Baixa | Sim |
| JsonElement | Média | Alta | Média | Não |
| JsonDocument | Baixa | Alta | Média | Não |
| Leitura manual | Muito baixa | Muito alta | Alta | Não |
Casos de uso
Quando usar desserialização automática (DTO)
- CRUD padrão
- APIs internas
- Baixo volume de requisições
- Quando legibilidade é prioridade
- Quando validação de modelo é necessária
Quando evitar desserialização completa
- Alto throughput (muitas requisições por segundo)
- Payloads grandes
- Uso parcial dos dados
- Endpoints críticos (hot paths)
Quando usar JsonDocument / JsonElement
- Extração de poucos campos
- Validação leve
- Roteamento baseado em conteúdo
- Processamento condicional
Quando usar leitura manual
- Streaming de dados
- Proxy de payload
- Processamento customizado
- Controle total do fluxo
Trade-offs
Vantagens de evitar desserialização
- Menor uso de memória
- Redução de alocações
- Melhor performance
- Controle total do processamento
Desvantagens
- Código mais complexo
- Perda de validação automática
- Maior risco de erro
- Menor legibilidade
Boas práticas
- Use DTO por padrão
- Otimize apenas quando necessário
- Meça antes de alterar abordagem
- Evite otimização prematura
- Prefira JsonElement para casos intermediários
- Use leitura manual apenas em cenários críticos