Vérification d'e-mails en .NET
Ce tutoriel intègre Mailbeam à une application ASP.NET Core avec les motifs idiomatiques de .NET : un HttpClient typé, le motif options pour la configuration, l'injection de dépendances et un attribut de validation réutilisable. Cela fonctionne à l'identique en minimal API et dans les contrôleurs MVC.
Ce que vous allez construire
- Un service
IEmailVerifierappuyé sur unHttpClienttypé - Un attribut de validation
[VerifiedEmail]pour le model binding - Un endpoint d'inscription en minimal API qui rejette les adresses non distribuables
Prérequis
- Le SDK .NET 8 ou plus récent
- Une clé d'API Mailbeam (inscription gratuite)
- Quelques bases sur l'injection de dépendances dans ASP.NET Core
Étape 1 — Configurer la clé
Rangez la clé dans les user secrets en local, et dans des variables d'environnement ou Key Vault en production :
dotnet user-secrets init
dotnet user-secrets set "Mailbeam:ApiKey" "mb_live_xxxxxxxxxxxxxxxxxxxx"Reliez-la avec le motif options :
// Options/MailbeamOptions.cs
public sealed class MailbeamOptions
{
public string ApiKey { get; set; } = "";
public int MinScore { get; set; } = 60;
}Étape 2 — Enregistrer un HttpClient typé
// 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);
});Étape 3 — Construire le service de vérification
// 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)
{
// Échec permissif : une erreur d'API ne doit pas bloquer les inscriptions
_log.LogError(ex, "Échec de la vérification Mailbeam pour {Email}", email);
return true;
}
}
}Étape 4 — Ajouter un attribut de validation
Pour MVC et le model binding, un attribut maison garde la validation déclarative :
// 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; // on laisse [Required] gérer le vide
var verifier = context.GetRequiredService<IEmailVerifier>();
// Les attributs sont synchrones ; on bloque brièvement sur l'appel asynchrone
var ok = verifier.IsAcceptableAsync(email).GetAwaiter().GetResult();
return ok
? ValidationResult.Success
: new ValidationResult("Merci de fournir une adresse e-mail valide et distribuable.");
}
}Étape 5 — Vérifier dans 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"] = ["Merci de fournir une adresse e-mail valide et distribuable."]
});
var user = await CreateUserAsync(req.Email, req.Password, ct);
return Results.Created($"/users/{user.Id}", user);
});
public record SignupRequest(string Email, string Password);Étape 6 — Ajouter un cache
Utilisez IMemoryCache (ou un cache distribué) pour éviter les recherches répétées :
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;
// … exécute l'appel de vérification comme à l'étape 3 …
var ok = result is not null && result.Valid && result.Score >= _opts.MinScore;
_cache.Set(key, ok, TimeSpan.FromHours(24));
return ok;
}Tester
Moquez le client typé, ou pointez vos tests vers les domaines de test de Mailbeam, qui sont déterministes et ne consomment pas de quota :
[Fact]
public async Task Disposable_Email_Is_Rejected()
{
var verifier = CreateVerifier(); // relié à la configuration 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);
}Bonnes pratiques
| Pratique | Pourquoi |
|---|---|
HttpClient typé via AddHttpClient | Pooling, résilience, moquage aisé |
| Motif options pour la clé | Liaison de configuration propre, sans chaînes magiques |
| Échec permissif sur exception | Une panne d'API ne casse pas les inscriptions |
Un Timeout de 5 s | Évite de suspendre la requête sur une sonde lente |
Cache avec IMemoryCache | Supprime les appels en double lors des réessais |
Liste de contrôle avant la mise en production
- Clé d'API dans les user secrets ou Key Vault, jamais dans le code source
- Délai d'expiration configuré sur
HttpClient - Journalisation des échecs de vérification
- Cache distribué pour les déploiements multi-instances
- Domaines de test utilisés dans les tests unitaires