Nedo.AspNet.Messaging
1.0.0
dotnet add package Nedo.AspNet.Messaging --version 1.0.0
NuGet\Install-Package Nedo.AspNet.Messaging -Version 1.0.0
<PackageReference Include="Nedo.AspNet.Messaging" Version="1.0.0" />
<PackageVersion Include="Nedo.AspNet.Messaging" Version="1.0.0" />
<PackageReference Include="Nedo.AspNet.Messaging" />
paket add Nedo.AspNet.Messaging --version 1.0.0
#r "nuget: Nedo.AspNet.Messaging, 1.0.0"
#:package Nedo.AspNet.Messaging@1.0.0
#addin nuget:?package=Nedo.AspNet.Messaging&version=1.0.0
#tool nuget:?package=Nedo.AspNet.Messaging&version=1.0.0
Nedo.AspNet.Messaging
ASP.NET Core messaging library for multi-channel notifications. Send messages through Email, Slack, Webhook, Microsoft Teams, and Telegram with a unified API, fluent builders, and built-in resilience.
Features
| Feature | Description |
|---|---|
| 5 Channels | Email (SMTP), Slack, Microsoft Teams, Telegram, Webhook |
| Fluent Builders | Typed, validated builders — no raw metadata dictionaries |
| Broadcast | Send one message to multiple channels in parallel |
| Channel Fallback | If primary channel fails, automatically try fallback channels |
| Polly Resilience | Automatic retry (3x exponential) + circuit breaker on HTTP providers |
| Deduplication | Idempotency key support to prevent duplicate sends |
| Attachments | File attachments for Email channel |
| Health Checks | ASP.NET Core IHealthCheck integration |
| Options Validation | Fail-fast startup validation for provider configuration |
| Structured Logging | Zero-allocation [LoggerMessage] source-generated logging |
| Extensible | Add new channels by implementing IMessageSender |
Table of Contents
- Project Structure
- Getting Started
- Installation
- Configuration
- Usage
- Broadcast
- Channel Fallback
- Deduplication
- Attachments
- Health Checks
- Channels
- Resilience (Polly)
- Adding a Custom Channel
- Documentation
- Architecture
Project Structure
├── src/
│ └── Nedo.AspNet.Messaging/
│ ├── Abstractions/ # IMessageSender, IMessagingService
│ ├── Builders/ # Typed fluent builders per channel
│ ├── Extensions/ # DI + health check registration
│ ├── Health/ # MessagingHealthCheck
│ ├── Models/ # MessageRequest, MessageResult, etc.
│ ├── Providers/
│ │ ├── Email/ # SMTP provider
│ │ ├── Slack/ # Slack incoming webhook
│ │ ├── Teams/ # Teams Adaptive Card
│ │ ├── Telegram/ # Telegram Bot API
│ │ └── Webhook/ # Generic HTTP POST
│ ├── Services/ # MessagingService, LogMessages
│ └── Validation/ # Options validators (startup)
├── test/
│ └── Nedo.AspNet.Messaging.Tests/ # Unit tests (xUnit, 49 tests)
├── sample/
│ └── Nedo.AspNet.Messaging.Sample/ # Sample minimal API with Swagger
└── docs/ # Per-channel setup guides
Getting Started
dotnet build # Build
dotnet test # Run tests
dotnet run --project sample/Nedo.AspNet.Messaging.Sample # Run sample (Swagger at /swagger)
Installation
<ProjectReference Include="path/to/src/Nedo.AspNet.Messaging/Nedo.AspNet.Messaging.csproj" />
Or as a NuGet package:
dotnet add package Nedo.AspNet.Messaging
Configuration
Add to appsettings.json — only include the channels you need:
{
"Messaging": {
"Email": {
"Host": "smtp.example.com",
"Port": 587,
"Username": "your-username",
"Password": "your-password",
"FromAddress": "noreply@example.com",
"FromName": "My Application",
"UseSsl": true
},
"Slack": {
"WebhookUrl": "https://hooks.slack.com/services/T.../B.../xxx",
"DefaultChannel": "#general",
"Username": "NedoBot",
"IconEmoji": ":robot_face:"
},
"Teams": {
"WebhookUrl": "https://outlook.office.com/webhook/..."
},
"Telegram": {
"BotToken": "123456:ABC-DEF...",
"DefaultChatId": "987654321",
"ParseMode": "HTML"
},
"Webhook": {
"DefaultUrl": "https://api.example.com/webhook",
"Headers": {
"X-Api-Key": "your-api-key"
}
}
}
}
Usage
1. Register Services
using Nedo.AspNet.Messaging.Extensions;
var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
builder.Services.AddMessaging()
.AddEmailProvider(config.GetSection("Messaging:Email"))
.AddSlackProvider(config.GetSection("Messaging:Slack"))
.AddTeamsProvider(config.GetSection("Messaging:Teams"))
.AddTelegramProvider(config.GetSection("Messaging:Telegram"))
.AddWebhookProvider(config.GetSection("Messaging:Webhook"));
// Optional: health checks
builder.Services.AddHealthChecks()
.AddMessagingHealthCheck();
You can also configure providers with an action delegate:
builder.Services.AddMessaging()
.AddEmailProvider(opts =>
{
opts.Host = "smtp.example.com";
opts.Port = 587;
opts.FromAddress = "noreply@example.com";
opts.UseSsl = true;
});
2. Send Messages
Each channel has a typed fluent builder with validation:
using Nedo.AspNet.Messaging.Abstractions;
using Nedo.AspNet.Messaging.Builders;
public class NotificationService
{
private readonly IMessagingService _messaging;
public NotificationService(IMessagingService messaging)
{
_messaging = messaging;
}
public async Task NotifyUserAsync(string email, string message)
{
var request = new EmailMessageBuilder()
.To(email)
.Subject("Notification")
.Body(message)
.AsHtml()
.Build(); // throws if To or Body is missing
var result = await _messaging.SendAsync(request);
if (!result.IsSuccess)
Console.WriteLine($"Send failed: {result.ErrorMessage}");
}
}
3. Check Supported Channels
var channels = _messaging.SupportedChannels;
// Returns: [Email, Slack, Teams, Telegram, Webhook]
Broadcast
Send one message to multiple channels in parallel:
var request = new EmailMessageBuilder()
.To("user@example.com")
.Subject("Critical Alert")
.Body("Server is down!")
.Build();
var result = await messaging.BroadcastAsync(
[MessageChannel.Email, MessageChannel.Slack, MessageChannel.Telegram],
request);
if (result.AllSucceeded)
Console.WriteLine("All channels delivered");
else
Console.WriteLine($"Failed: {string.Join(", ", result.FailedChannels)}");
BroadcastResult properties:
| Property | Type | Description |
|---|---|---|
AllSucceeded |
bool |
All channels succeeded |
AnySucceeded |
bool |
At least one channel succeeded |
Results |
IReadOnlyList<MessageResult> |
Per-channel results |
SucceededChannels |
IEnumerable<MessageChannel> |
Channels that succeeded |
FailedChannels |
IEnumerable<MessageChannel> |
Channels that failed |
Channel Fallback
If the primary channel fails, automatically try fallback channels in order:
var request = new EmailMessageBuilder()
.To("user@example.com")
.Subject("Alert")
.Body("Server is down!")
.Build();
var result = await messaging.SendWithFallbackAsync(
request,
primary: MessageChannel.Slack,
fallbacks: [MessageChannel.Email, MessageChannel.Telegram]);
// Tries Slack first → if it fails, tries Email → if it fails, tries Telegram
Deduplication
Prevent duplicate message sends using an idempotency key:
var request = new MessageRequest
{
Channel = MessageChannel.Email,
To = "user@example.com",
Body = "Order #123 confirmed",
IdempotencyKey = "order-123-confirmation" // unique key
};
await messaging.SendAsync(request); // sends
await messaging.SendAsync(request); // silently skipped (same key within 5 min)
- Messages with the same
IdempotencyKey+ channel within a 5-minute window are considered duplicates - Duplicate sends return success without actually sending
- Useful for retry scenarios at the application level
Attachments
Email supports file attachments via the builder:
var pdfBytes = await File.ReadAllBytesAsync("report.pdf");
var request = new EmailMessageBuilder()
.To("user@example.com")
.Subject("Monthly Report")
.Body("Please find the report attached.")
.Attach("report.pdf", pdfBytes, "application/pdf")
.Attach("summary.csv", csvBytes, "text/csv")
.Build();
You can also attach from a stream:
using var stream = File.OpenRead("large-file.zip");
builder.Attach("large-file.zip", stream, "application/zip");
Health Checks
Register the ASP.NET Core health check:
builder.Services.AddHealthChecks()
.AddMessagingHealthCheck(name: "messaging", tags: new[] { "ready" });
app.MapHealthChecks("/health");
Response when healthy:
{
"status": "Healthy",
"description": "5 messaging channel(s) registered.",
"data": {
"registered_channels": "Email, Slack, Webhook, Teams, Telegram",
"channel_count": 5
}
}
Channels
Email (SMTP)
var request = new EmailMessageBuilder()
.To("user@example.com") // semicolon-separated for multiple
.Subject("Weekly Report")
.Body("<h1>Report</h1><p>Details...</p>")
.AsHtml()
.Cc("manager@example.com; lead@example.com")
.Bcc("archive@example.com")
.ReplyTo("support@example.com")
.Priority(MessagePriority.High)
.Build();
| Property | Type | Default | Description |
|---|---|---|---|
Host |
string |
localhost |
SMTP server hostname |
Port |
int |
587 |
SMTP server port |
Username |
string? |
null |
SMTP auth username |
Password |
string? |
null |
SMTP auth password |
FromAddress |
string |
noreply@example.com |
Sender email address |
FromName |
string |
Nedo Messaging |
Sender display name |
UseSsl |
bool |
true |
Enable SSL/TLS |
Slack
var request = new SlackMessageBuilder()
.Subject("Deploy Status") // bold prefix
.Body("Deployment completed :rocket:")
.Channel("#deployments")
.Username("DeployBot")
.IconEmoji(":rocket:")
.Build();
| Property | Type | Default | Description |
|---|---|---|---|
WebhookUrl |
string |
"" |
Slack incoming webhook URL |
DefaultChannel |
string? |
null |
Default channel |
Username |
string? |
null |
Bot username |
IconEmoji |
string? |
null |
Bot icon emoji |
Microsoft Teams
var request = new TeamsMessageBuilder()
.Subject("Build Failed") // Adaptive Card title
.Body("Build #1234 failed on branch main.")
.WebhookUrl("https://...") // optional override
.Build();
| Property | Type | Default | Description |
|---|---|---|---|
WebhookUrl |
string |
"" |
Teams incoming webhook URL |
Telegram
var request = new TelegramMessageBuilder()
.ChatId("123456789") // or use default from config
.Subject("Alert") // bold prefix
.Body("Server CPU is at 95%")
.ParseMode("HTML")
.Build();
| Property | Type | Default | Description |
|---|---|---|---|
BotToken |
string |
"" |
Bot API token from @BotFather |
DefaultChatId |
string? |
null |
Default chat/group ID |
ParseMode |
string? |
null |
"HTML", "MarkdownV2", or empty |
Webhook
var request = new WebhookMessageBuilder()
.To("user-123")
.Subject("Order Placed")
.Body("Order #5678 has been placed.")
.Url("https://api.partner.com/notify") // optional override
.Build();
| Property | Type | Default | Description |
|---|---|---|---|
DefaultUrl |
string |
"" |
Default HTTP endpoint URL |
Headers |
Dictionary<string, string> |
{} |
Default headers |
Resilience (Polly)
All HTTP-based providers (Slack, Teams, Telegram, Webhook) automatically include:
- Retry: 3 attempts with exponential backoff (2s → 4s → 8s) for transient HTTP errors (5xx, 408, network failures)
- Circuit Breaker: Opens after 5 consecutive failures, stays open for 30s
No configuration needed — policies are applied at DI registration.
Adding a Custom Channel
- Add a value to the
MessageChannelenum - Create
YourOptionsclass andYourMessageSender : IMessageSender - Add an
AddYourProvider()DI extension method - Optionally create a typed
YourMessageBuilder
See docs/06-extending.md for a full step-by-step example using SMS/Twilio.
Documentation
Detailed guides for each channel and advanced features are in the docs/ folder:
| Doc | Content |
|---|---|
| Getting Started | |
| 00-overview.md | Architecture, features, quick start |
| 01-email.md | Gmail, SendGrid, SES, O365 setup, attachments |
| 02-slack.md | Webhook creation, mrkdwn formatting reference |
| 03-teams.md | Connectors, Power Automate, Adaptive Cards |
| 04-telegram.md | BotFather, Chat IDs, HTML/MarkdownV2 reference |
| 05-webhook.md | Custom headers, payload format, integration patterns |
| Advanced | |
| 06-extending.md | Full custom channel walkthrough (SMS/Twilio example) |
| 07-broadcast-and-fallback.md | Multi-channel send, fallback chains, deduplication |
| 08-resilience.md | Polly retry, circuit breaker, customization |
| 09-health-checks.md | Health endpoint, Kubernetes probes, Docker |
| 10-troubleshooting.md | Common errors, debugging, FAQ |
Architecture
┌─────────────────────────────────────────────────┐
│ Consumer Code │
│ (inject IMessagingService, use Builders) │
└──────────────────┬──────────────────────────────┘
│ SendAsync / BroadcastAsync
▼
┌─────────────────────────────────────────────────┐
│ MessagingService (router) │
│ ┌─────────────────────────────────────────┐ │
│ │ Polly: Retry + Circuit Breaker (HTTP) │ │
│ └─────────────────────────────────────────┘ │
└──┬──────┬──────┬──────┬──────┬──────────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Email Slack Webhook Teams Telegram
SMTP Hook HTTP Card Bot API
Key components:
- IMessagingService — consumer interface (
SendAsync,BroadcastAsync,SupportedChannels) - IMessageSender — provider contract, one per channel
- Builders —
EmailMessageBuilder,SlackMessageBuilder,TeamsMessageBuilder,TelegramMessageBuilder,WebhookMessageBuilder - MessageRequest — channel-agnostic payload with
AttachmentsandMetadata - MessageResult / BroadcastResult — delivery results
- MessagingHealthCheck — ASP.NET Core
IHealthCheck
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Microsoft.Extensions.Http.Polly (>= 9.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 144 | 3/2/2026 |