Changelog
Notable changes in each release.
Utilities for different messaging formats and protocols for .NET applications. Right now, the library includes a Mailgun email service, but more features and protocols may be added in the future, as well as support for more email providers.
Contributions are welcome!
Install the package via the .NET CLI:
dotnet add package ArturRios.Messaging
Or via the NuGet Package Manager:
Install-Package ArturRios.Messaging
| Package | Purpose |
|---|---|
| ArturRios.Output | Structured operation result type (ProcessOutput) |
IServiceCollection patternProcessOutput results (from ArturRios.Output): missing configuration and messages Mailgun rejects are reported as errors rather than thrown. Transport failures from HttpClient (no network, a timeout) still throwSendEmailAsync has an overload taking a CancellationToken, which aborts the HTTP requestSet the following environment variables before calling SendEmailAsync:
| Variable | Required | Default | Description |
|---|---|---|---|
MAILGUN_API_KEY | Yes | — | Your Mailgun private API key |
MAILGUN_DOMAIN | Yes | — | Your verified Mailgun sending domain |
MAILGUN_FROM | No | postmaster@{MAILGUN_DOMAIN} | The sender of every message: an address ([email protected]) or a display name and an address (Example <[email protected]>). When unset or blank, the domain’s postmaster is used, with no display name |
MAILGUN_API_VERSION | No | v3 | Mailgun API version used to build the request URL. When unset or blank, v3 is used |
Requests are sent to https://api.mailgun.net/{MAILGUN_API_VERSION}/{MAILGUN_DOMAIN}/messages, as plain text
from MAILGUN_FROM, or from postmaster@{MAILGUN_DOMAIN} when it is not set. Set MAILGUN_FROM in production:
the postmaster default is a working fallback, not a sender recipients should see. Its address normally has to be
on MAILGUN_DOMAIN (or another domain verified in Mailgun) for Mailgun to accept it.
Environment variables are read on every SendEmailAsync call, so changes take effect without recreating the service.
If MAILGUN_API_KEY or MAILGUN_DOMAIN is unset or blank, or MAILGUN_FROM is set but is not exactly one
address (with or without a display name), SendEmailAsync returns a failed ProcessOutput naming the
variable and sends nothing — rather than issuing an unauthenticated
request against an empty domain and reporting whatever Mailgun makes of it.
The credential is attached to each request, never to the client’s DefaultRequestHeaders. That matters
because the documented registration is AddHttpClient, which hands the service a client it does not own:
writing a credential onto that client’s defaults would race with concurrent sends and leave the Mailgun key
attached to every later request the client makes.
The recipient address is personal data, so it is never logged. Log lines carry a stable reference to it instead — the first twelve hex characters of the SHA-256 of the trimmed, lower-cased address — which is enough to tell whether several lines concern the same recipient without disclosing who it is.
Register MailgunEmailService with your DI container:
using ArturRios.Messaging.Email;
builder.Services.AddHttpClient<IEmailService, MailgunEmailService>();
using ArturRios.Messaging.Email;
public class NotificationService(IEmailService emailService)
{
public async Task NotifyAsync(string recipient)
{
var output = await emailService.SendEmailAsync(
to: recipient,
subject: "Welcome!",
body: "Thanks for signing up."
);
if (!output.Success)
{
foreach (var error in output.Errors)
Console.WriteLine(error);
}
}
}
Pass a CancellationToken — a request’s HttpContext.RequestAborted, a worker’s stopping token — to abort the
send:
var output = await emailService.SendEmailAsync(recipient, "Welcome!", "Thanks for signing up.", cancellationToken);
An already-canceled token throws OperationCanceledException before anything is sent; canceling mid-send aborts
the HTTP request and throws the same. The three-argument overload is unchanged. On IEmailService the new
overload has a default implementation, so existing implementations keep compiling: it checks the token and then
calls the three-argument overload.
using ArturRios.Messaging.Email;
using Microsoft.Extensions.Logging.Abstractions;
var service = new MailgunEmailService(NullLogger<MailgunEmailService>.Instance);
var output = await service.SendEmailAsync("[email protected]", "Hello", "World");
classDiagram
class IEmailService {
<<interface>>
+SendEmailAsync(to: string, subject: string, body: string) Task~ProcessOutput~
+SendEmailAsync(to: string, subject: string, body: string, cancellationToken: CancellationToken) Task~ProcessOutput~
}
class MailgunEmailService {
-ILogger~MailgunEmailService~ _logger
-HttpClient _httpClient
-string MailgunApiBaseUrl$
-string MailgunMessagesEndpoint$
+string ApiKeyVariable$
+string DomainVariable$
+string ApiVersionVariable$
+string DefaultMailgunApiVersion$
+MailgunEmailService(logger: ILogger~MailgunEmailService~, httpClient: HttpClient?)
+SendEmailAsync(to: string, subject: string, body: string) Task~ProcessOutput~
+SendEmailAsync(to: string, subject: string, body: string, cancellationToken: CancellationToken) Task~ProcessOutput~
-RecipientReference(address: string?) string$
-GetApiVersion() string$
}
class ProcessOutput {
<<ArturRios.Output>>
+bool Success
+List~string~ Errors
+AddError(message: string) void
}
IEmailService <|.. MailgunEmailService : implements
MailgunEmailService ..> ProcessOutput : returnsThis project is licensed under the Artur Rios Attribution License. It is free and open source: you may view, use, fork, modify, and extend it, provided you credit the author, Artur Rios, with a link to artur-rios.tech or github.com/artur-rios.
Notable changes in each release.
Building from source, running the tests, branching and releasing.