Skip to main content

Desserialização em endpoints | API | CSharp

  • Desserialização em endpoints | API | CSharp

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.Json por 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

AbordagemAlocaçãoPerformanceComplexidadeValidação automática
DTO (padrão)AltaMédiaBaixaSim
JsonElementMédiaAltaMédiaNão
JsonDocumentBaixaAltaMédiaNão
Leitura manualMuito baixaMuito altaAltaNã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