Beta privadaMailbeam está en beta privada: la API pública todavía no está abierta.Únete a la lista de espera

Mailbeam
ASP.NET Core + C#Intermedio20 minutosActualizado en enero de 2025

Verificación de email en .NET

Este tutorial integra Mailbeam en una aplicación de ASP.NET Core con patrones idiomáticos de .NET: un HttpClient tipado, el patrón de opciones para la configuración, inyección de dependencias y un atributo de validación reutilizable. Funciona igual en minimal APIs y en controladores MVC.

Qué vas a construir

  • Un servicio IEmailVerifier respaldado por un HttpClient tipado
  • Un atributo de validación [VerifiedEmail] para el enlace de modelos
  • Un endpoint de registro con minimal API que rechaza las direcciones no entregables

Requisitos previos

  • SDK de .NET 8 o posterior
  • Una clave de API de Mailbeam (regístrate gratis)
  • Conocimientos básicos de inyección de dependencias en ASP.NET Core

Paso 1 — Configura la clave

Guarda la clave en los secretos de usuario en local y en variables de entorno o Key Vault en producción:

dotnet user-secrets init
dotnet user-secrets set "Mailbeam:ApiKey" "mb_live_xxxxxxxxxxxxxxxxxxxx"

Enlázala con el patrón de opciones:

// Options/MailbeamOptions.cs
public sealed class MailbeamOptions
{
    public string ApiKey { get; set; } = "";
    public int MinScore { get; set; } = 60;
}

Paso 2 — Registra un HttpClient tipado

// Program.cs
builder.Services.Configure<MailbeamOptions>(
    builder.Configuration.GetSection("Mailbeam"));

builder.Services.AddHttpClient<IEmailVerifier, MailbeamVerifier>((sp, client) =>
{
    var opts = sp.GetRequiredService<IOptions<MailbeamOptions>>().Value;
    client.BaseAddress = new Uri("https://api.mailbeam.dev/");
    client.DefaultRequestHeaders.Authorization =
        new AuthenticationHeaderValue("Bearer", opts.ApiKey);
    client.Timeout = TimeSpan.FromSeconds(5);
});

Paso 3 — Construye el servicio de verificación

// Services/IEmailVerifier.cs
public interface IEmailVerifier
{
    Task<bool> IsAcceptableAsync(string email, CancellationToken ct = default);
}

// Services/MailbeamVerifier.cs
public sealed class MailbeamVerifier : IEmailVerifier
{
    private readonly HttpClient _http;
    private readonly MailbeamOptions _opts;
    private readonly ILogger<MailbeamVerifier> _log;

    public MailbeamVerifier(
        HttpClient http,
        IOptions<MailbeamOptions> opts,
        ILogger<MailbeamVerifier> log)
    {
        _http = http;
        _opts = opts.Value;
        _log = log;
    }

    private sealed record VerifyResponse(bool Valid, int Score, string? Reason);

    public async Task<bool> IsAcceptableAsync(string email, CancellationToken ct = default)
    {
        try
        {
            var resp = await _http.PostAsJsonAsync(
                "v1/verify", new { email = email.Trim().ToLowerInvariant() }, ct);
            resp.EnsureSuccessStatusCode();

            var result = await resp.Content.ReadFromJsonAsync<VerifyResponse>(ct);
            return result is not null
                && result.Valid
                && result.Score >= _opts.MinScore;
        }
        catch (Exception ex)
        {
            // Falla en abierto: un error de la API no debería bloquear los registros
            _log.LogError(ex, "Ha fallado la verificación de Mailbeam para {Email}", email);
            return true;
        }
    }
}

Paso 4 — Añade un atributo de validación

En MVC y en el enlace de modelos, un atributo propio mantiene la validación declarativa:

// Validation/VerifiedEmailAttribute.cs
public sealed class VerifiedEmailAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(
        object? value, ValidationContext context)
    {
        if (value is not string email || string.IsNullOrWhiteSpace(email))
            return ValidationResult.Success; // deja los vacíos para [Required]

        var verifier = context.GetRequiredService<IEmailVerifier>();
        // Los atributos son síncronos; bloquea brevemente en la llamada asíncrona
        var ok = verifier.IsAcceptableAsync(email).GetAwaiter().GetResult();

        return ok
            ? ValidationResult.Success
            : new ValidationResult("Escribe una dirección de email válida y entregable.");
    }
}

Paso 5 — Verifica en un endpoint de minimal API

// Program.cs
app.MapPost("/api/signup", async (
    SignupRequest req,
    IEmailVerifier verifier,
    CancellationToken ct) =>
{
    if (!await verifier.IsAcceptableAsync(req.Email, ct))
        return Results.ValidationProblem(new Dictionary<string, string[]>
        {
            ["email"] = ["Escribe una dirección de email válida y entregable."]
        });

    var user = await CreateUserAsync(req.Email, req.Password, ct);
    return Results.Created($"/users/{user.Id}", user);
});

public record SignupRequest(string Email, string Password);

Paso 6 — Añade caché

Usa IMemoryCache (o una caché distribuida) para evitar consultas repetidas:

public async Task<bool> IsAcceptableAsync(string email, CancellationToken ct = default)
{
    var key = $"mb:{email.Trim().ToLowerInvariant()}";
    if (_cache.TryGetValue<bool>(key, out var cached))
        return cached;

    // ... haz la llamada de verificación como en el paso 3 ...
    var ok = result is not null && result.Valid && result.Score >= _opts.MinScore;
    _cache.Set(key, ok, TimeSpan.FromHours(24));
    return ok;
}

Pruebas

Simula el cliente tipado o apunta los tests a los dominios de prueba de Mailbeam, que son deterministas y no consumen cuota:

[Fact]
public async Task Disposable_Email_Is_Rejected()
{
    var verifier = CreateVerifier(); // conectado a la configuración de test
    var ok = await verifier.IsAcceptableAsync("temp@disposable.mailbeam-test.dev");
    Assert.False(ok);
}

[Fact]
public async Task Valid_Email_Is_Accepted()
{
    var verifier = CreateVerifier();
    var ok = await verifier.IsAcceptableAsync("user@valid.mailbeam-test.dev");
    Assert.True(ok);
}

Buenas prácticas

PrácticaPor qué
HttpClient tipado con AddHttpClientPool de conexiones, resiliencia y simulación fácil
Patrón de opciones para la claveConfiguración limpia, sin cadenas mágicas
Fallar en abierto ante excepcionesUna caída de la API no rompe los registros
Poner un Timeout de 5 sNo dejes la petición colgada en un sondeo lento
Cachear con IMemoryCacheReduce las llamadas duplicadas en los reintentos

Checklist de producción

  • Clave de API en los secretos de usuario o en Key Vault, nunca en el código
  • Tiempo de espera del HttpClient configurado
  • Registro de errores en los fallos de verificación
  • Caché distribuida en despliegues con varias instancias
  • Dominios de prueba usados en los tests unitarios

Siguientes pasos