Skip to content
Comece com 100 créditos de verificação grátis
Qualisend
Todos os artigos
Engenharia / 13 de junho de 2026

Como validar um endereço de e-mail em C#

8 minutes read

Qualisend team
Uma janela de código C# validando um e-mail pelas camadas de sintaxe, DNS e SMTP

Para validar um endereço de e-mail em C#, a maioria dos desenvolvedores recorre a System.Net.Mail.MailAddress e segue em frente. É um primeiro passo razoável — o tipo é embutido no framework e rejeita lixo sem precisar de uma regex feita à mão — mas ele responde apenas à primeira das três perguntas que a validação de verdade faz. O endereço tem o formato correto? O domínio dele consegue receber e-mail? A caixa postal de fato existe? A biblioteca base do .NET responde a primeira com clareza, precisa de um pacote NuGet bem conhecido para a segunda e deixa a terceira para um problema de rede que vale a pena delegar. Este guia constrói a validação de e-mail em C# como três camadas, com código async/await idiomático para cada uma, e mostra exatamente onde o MailAddress para.

A resposta curta#

Use MailAddress.TryCreate para a sintaxe, o pacote DnsClient.NET para a consulta de MX e uma API de verificação para a checagem SMTP da caixa postal — o mais barato primeiro, com curto-circuito assim que uma camada for decisiva. Não abra sockets SMTP crus da sua aplicação para sondar caixas postais por conta própria: a porta 25 de saída é bloqueada na maioria dos hosts, e a resposta depende da reputação do IP de envio e do greylisting que você não quer reimplementar. Cada camada elimina endereços de forma mais barata do que a anterior; só a última consegue confirmar um endereço como válido. Se você só quer um veredito para um único endereço agora, cole-o no verificador de e-mail gratuito e pule o código — o restante deste guia é para conectar a checagem a uma aplicação. Para os conceitos por trás de cada etapa, veja o que é a verificação de e-mail.

Camada 1: sintaxe com MailAddress.TryCreate#

MailAddress é a ferramenta certa para a camada um. Prefira a sobrecarga TryCreate adicionada no .NET 5: ela retorna um bool em vez de lançar uma FormatException com entrada inválida, então você pode usá-la como filtro sem envolver cada chamada em um try/catch.

using System.Net.Mail;

public static bool IsValidSyntax(string email)
{
    return !string.IsNullOrWhiteSpace(email)
        && email.Length <= 320
        && MailAddress.TryCreate(email, out var parsed)
        && parsed.Address == email;
}
IsValidSyntax("jane@example.com");           // true
IsValidSyntax("not-an-email");               // false
IsValidSyntax("Jane <jane@example.com>");    // false

Aquela última comparação importa. MailAddress é um parser de e-mail, não um validador estrito — ele aceita de bom grado formas com nome de exibição como Jane <jane@example.com> e expõe a parte do endereço em parsed.Address. Comparar parsed.Address de volta com a entrada crua rejeita essas formas e espaços em branco perdidos, que é o que você quer de um filtro de sintaxe sim/não sobre a entrada do usuário. A guarda de 320 caracteres é proteção extra: nada mais longo pode ser um endereço de verdade, e é mais barato rejeitar cedo do que entregar uma string gigante para qualquer coisa mais adiante.

No .NET Framework ou em qualquer alvo mais antigo que o .NET 5, o TryCreate não existe, então recorra ao construtor e capture a exceção:

try { _ = new MailAddress(email); /* syntax ok */ }
catch (FormatException) { /* reject */ }

Se você já usa data annotations em um modelo, o System.ComponentModel.DataAnnotations.EmailAddressAttribute ([EmailAddress]) faz o mesmo trabalho de forma declarativa e é uma boa escolha dentro da validação de modelo do ASP.NET Core. Qualquer que seja a sua escolha, trate uma aprovação como "vale a pena checar direito", nunca como "válido". Uma checagem de sintaxe não sabe nada sobre DNS nem caixas postais — é o mesmo motivo pelo qual a validação de e-mail por regex falha: formato e entregabilidade são perguntas diferentes, uma um fato sobre a string e a outra um fato sobre a internet.

Camada 2: o domínio consegue receber e-mail?#

Um domínio sem rota de e-mail não consegue aceitar e-mail de ninguém, então essa única consulta elimina domínios mortos, nomes de empresa escritos errado e TLDs inventados antes de você sequer tocar a rede de verdade. O detalhe: o .NET não tem um resolvedor de MX nativo. O System.Net.Dns resolve registros A e AAAA, mas não MX, então a resposta idiomática é o DnsClient.NET — um pacote NuGet amplamente usado e bem mantido, que a maioria dos projetos .NET recorre quando precisa de DNS além das consultas de host. Instale-o e consulte o tipo de registro MX:

using System.Linq;
using DnsClient; // dotnet add package DnsClient

// LookupClient is thread-safe and caches responses — create one and reuse it.
private static readonly LookupClient Dns = new();

public static async Task<bool> HasMailRouteAsync(string domain)
{
    try
    {
        var response = await Dns.QueryAsync(domain, QueryType.MX);
        return response.Answers.MxRecords().Any();
    }
    catch (DnsResponseException)
    {
        // No reachable resolver, SERVFAIL, and similar — treat as "check later".
        return false;
    }
}
await HasMailRouteAsync("gmail.com");               // true
await HasMailRouteAsync("company-that-folded.com"); // false

Dois idiomas que vale a pena copiar. Crie o LookupClient uma vez e reutilize-o — ele é thread-safe e faz cache das respostas, então uma instância por requisição simplesmente joga fora esse cache. E separe o domínio do endereço no último @ com uma expressão de intervalo para nunca interpretar errado um endereço que (legalmente) contém mais de um:

var domain = email[(email.LastIndexOf('@') + 1)..];

Alguns domínios aceitam e-mail em um registro A sem MX (um MX implícito). Se você quiser respeitar esse caso extremo, recorra ao QueryType.A quando o conjunto de MX estiver vazio — mas para a esmagadora maioria dos endereços reais, uma checagem de MX é o filtro certo. Um cuidado para o fluxo de cadastro: uma consulta de DNS trava esperando um resolvedor, então uma falha transitória de DNS não deveria rejeitar categoricamente um cliente real. Trate uma consulta que falhou como "checar depois", não como "inválido".

Camada 3: a caixa postal de fato existe?#

As camadas um e dois só conseguem eliminar um endereço. Um domínio pode publicar registros MX perfeitos e ainda assim não ter caixa postal no endereço que você tem em mãos — noreply-9f2x@gmail.com tem sintaxe válida em um domínio com rota de e-mail ativa, e mesmo assim é uma caixa postal que nunca foi criada. Confirmar que uma caixa postal específica existe significa a conversa de entrega SMTP: conectar ao host de e-mail, emitir RCPT TO, ler a resposta e desconectar antes de enviar qualquer coisa. Como funciona a verificação de e-mail percorre esse pipeline completo, incluindo domínios catch-all que aceitam todo endereço e derrotam uma sondagem ingênua.

Em princípio, você pode escrever isso em C# com um TcpClient e comandos SMTP crus. Na prática, você não deveria rodá-lo do seu servidor de aplicação. A maioria dos provedores de nuvem bloqueia a porta 25 de saída, então uma sondagem que funciona no seu notebook falha silenciosamente em produção. A resposta depende da reputação do IP a partir do qual você conecta, não apenas do endereço que você está checando. E os servidores receptores aplicam greylisting e limitam a taxa de remetentes desconhecidos, então sondar em qualquer volume te faz ser adiado ou colocado em blocklist. Essa é a camada que vale a pena delegar a uma infraestrutura feita para isso.

Chamando uma API de verificação a partir do C##

O endpoint de verificação da Qualisend roda o pipeline inteiro — sintaxe, DNS e a sondagem SMTP da caixa postal — a partir de uma infraestrutura com reputação gerenciada, e retorna um veredito em tempo real. HttpClient mais System.Text.Json é tudo o que você precisa; modele o envelope da resposta como records e deixe o gerador de código-fonte JSON ou o desserializador por reflexão preenchê-los:

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Serialization;

public record VerifyResponse(
    [property: JsonPropertyName("result")] VerifyResult Result);

public record VerifyResult(
    [property: JsonPropertyName("status")] string Status,
    [property: JsonPropertyName("score")] int Score,
    [property: JsonPropertyName("reason")] string? Reason,
    [property: JsonPropertyName("sub_flags")] IReadOnlyDictionary<string, bool> SubFlags);
// One HttpClient for the whole app — don't new one up per request.
private static readonly HttpClient Http = new()
{
    BaseAddress = new Uri("https://api.qualisend.com/v1/"),
};

public static async Task<VerifyResult> VerifyAsync(string email)
{
    using var request = new HttpRequestMessage(HttpMethod.Post, "verify")
    {
        Content = JsonContent.Create(new { email }),
    };
    request.Headers.Authorization = new AuthenticationHeaderValue(
        "Bearer", Environment.GetEnvironmentVariable("QUALISEND_API_KEY"));

    using var response = await Http.SendAsync(request);
    response.EnsureSuccessStatusCode();

    var body = await response.Content.ReadFromJsonAsync<VerifyResponse>();
    return body!.Result; // status: deliverable | risky | undeliverable | unknown
}

POST https://api.qualisend.com/v1/verify é um formato de exemplo — confira a referência da API em /developers para o endpoint exato, o formato da chave com escopo e todos os campos. Leia a chave de uma variável de ambiente em vez de embutir YOUR_API_KEY no código, e reutilize um único HttpClient para evitar esgotamento de sockets. A resposta volta dentro de um envelope result:

{
  "result": {
    "status": "deliverable",
    "score": 95,
    "reason": null,
    "sub_flags": { "role": false, "disposable": false, "free": false, "catch_all": false }
  }
}

status é o seu veredito principal — deliverable, risky, undeliverable ou unknown. score classifica a confiança, reason explica um resultado negativo e sub_flags detalha sinais como role, disposable, free e catch-all para você aplicar a sua própria política (rejeitar undeliverable, segurar risky para revisão, sinalizar disposable no cadastro).

Juntando as camadas para validar um endereço de e-mail em C##

O mais barato primeiro, parando assim que tiver uma resposta:

public static async Task<VerifyResult> ValidateEmailAsync(string email)
{
    if (!IsValidSyntax(email))
        return new VerifyResult("undeliverable", 0, "invalid_email",
            new Dictionary<string, bool>());

    var domain = email[(email.LastIndexOf('@') + 1)..];
    if (!await HasMailRouteAsync(domain))
        return new VerifyResult("undeliverable", 0, "invalid_domain",
            new Dictionary<string, bool>());

    return await VerifyAsync(email); // deliverable | risky | undeliverable | unknown
}

As duas camadas locais não custam nada e pegam a maior parte do lixo na hora; a camada da API roda só nos endereços que valem a ida e volta pela rede. Essa ordenação é o truque inteiro — o mesmo formato que você vai encontrar nas versões Node.js, Python e PHP deste guia, porque é o empilhamento de camadas, não a linguagem, que faz a validação funcionar.

Perguntas frequentes#

MailAddress ou EmailAddressAttribute bastam para validar um e-mail em C#?#

Para a sintaxe, sim — ambos são checagens sólidas de primeira camada e uma aposta melhor do que uma regex feita à mão, com MailAddress.TryCreate te devolvendo um booleano que não lança exceção. Mas eles validam o formato, não a entregabilidade: nenhum resolve DNS nem contata um servidor de e-mail, então uma aprovação significa "parece um e-mail", não "vai entregar". Combine a checagem de sintaxe com uma consulta de MX e uma verificação SMTP da caixa postal antes de confiar no endereço.

O .NET tem uma forma nativa de consultar registros MX?#

Não. O System.Net.Dns resolve registros A e AAAA de host, mas não tem suporte a MX, e é por isso que a abordagem padrão é o pacote NuGet DnsClient.NET. Consulte QueryType.MX, reutilize um único LookupClient e trate um conjunto de respostas vazio — ou uma DnsResponseException — como ausência de rota de e-mail, em vez de deixar a exceção ser lançada.

Dá para checar se uma caixa postal existe em C# sem uma API?#

Só em parte. O DnsClient.NET confirma que o domínio aceita e-mail, o que descarta domínios mortos de forma barata. Confirmar a caixa postal em si exige uma conversa SMTP que você pode tentar com um TcpClient, mas não deveria rodar do seu servidor de aplicação — a porta 25 de saída é amplamente bloqueada e o resultado depende da reputação do seu IP de envio. Essa é a camada que um serviço de verificação existe para cuidar, e vale a pena comparar provedores antes de construí-la por conta própria.

Devo validar e-mails no cadastro ou ao limpar uma lista?#

Os dois, com profundidades diferentes. Rode a sintaxe e a consulta de MX de forma síncrona no cadastro — elas são rápidas o suficiente para bloquear a requisição e dar feedback instantâneo — e aja também sobre o veredito imediato da API ali; o guia de cadastro serverless mostra o padrão de ponta a ponta. Reserve a verificação mais profunda e em lote para limpar uma lista existente, onde a latência não importa e a minúcia sim.


Pronto para adicionar a camada SMTP? A referência da API tem o formato exato de /verify, chaves com escopo e exemplos para copiar e colar, ou jogue um endereço no verificador de e-mail gratuito para ver uma string aprovada pelo MailAddress voltar como undeliverable.

Your reputation, protected.

Clean your first list in minutes. 100 free credits, no card required.

Get started