### Example: Subscribe Audit Handler Source: https://github.com/thnak/mqttcontrollerframework/wiki/Events An example implementation of IMqttClientSubscribedTopicEvent for logging subscription events. Requires an IAuditLog service. ```csharp public class SubscribeAuditHandler(IAuditLog audit) : IMqttClientSubscribedTopicEvent { public Task OnClientSubscribedTopicAsync(string clientId, string topicFilter) => audit.RecordAsync($"SUBSCRIBE {clientId} \u2192 {topicFilter}"); } ``` -------------------------------- ### Full MqttSettings Configuration Source: https://github.com/thnak/mqttcontrollerframework/wiki/Configuration-Reference Example of a complete MqttSettings configuration section in appsettings.json. ```json "MqttSettings": { "EnableNonSsl": true, "NonSslPort": 1883, "EnableSsl": false, "SslPort": 8883, "PkcsPath": "", "PkcsPassword": "", "PkcsKeyPath": "", "ClientCertStorage": "", "RetainedMessagesFilePath": "RetainedMessages.json", "ServerOriginPropertyName": "x-mqtt-origin", "ServerOriginPropertyValue": "server" } ``` -------------------------------- ### Example: Presence Connected Handler Source: https://github.com/thnak/mqttcontrollerframework/wiki/Events An example implementation of IMqttClientConnectedEvent to notify a presence service when a client connects. Requires an IPresenceService. ```csharp public class PresenceConnectedHandler(IPresenceService presence) : IMqttClientConnectedEvent { public Task OnClientConnectedAsync(string clientId) => presence.SetOnlineAsync(clientId); } ``` -------------------------------- ### Role-Based Authorization Provider Example Source: https://github.com/thnak/mqttcontrollerframework/blob/master/docs/wiki/Authorization.md A minimal example demonstrating role-based authorization for publishing to specific topics. It checks user roles before allowing access. ```csharp public class RoleBasedAuthzProvider(IUserRoleStore roles) : IMqttAuthorizationProvider { public async ValueTask AuthorizePublishAsync( string userName, string topic, int qos, bool retain, CancellationToken ct) { var userRoles = await roles.GetAsync(userName, ct); // Only "device" role may publish to "sensors/#" if (topic.StartsWith("sensors/") && !userRoles.Contains("device")) return MqttAuthorizationResult.Deny("Insufficient role to publish sensor data"); return MqttAuthorizationResult.Allow(); } public ValueTask AuthorizeSubscribeAsync( string userName, string topicFilter, int requestedQos, CancellationToken ct) => ValueTask.FromResult(MqttAuthorizationResult.Allow()); // open subscriptions } ``` -------------------------------- ### Example: Accessing SessionItems in Middleware Source: https://github.com/thnak/mqttcontrollerframework/wiki/Connection-Validation Demonstrates how middleware can access data previously stored in SessionItems by a connection validator. This example sets the tenant context from 'tenantId'. ```csharp public class TenantMiddleware(ITenantContext ctx) : IMqttMiddleware { public Task InvokeAsync(MqttMessageContext context, MqttRequestDelegate next) { if (context.SessionItems["tenantId"] is string tid) ctx.SetTenant(tid); return next(context); } } ``` -------------------------------- ### Example: ClientId Format Validation Source: https://github.com/thnak/mqttcontrollerframework/wiki/Connection-Validation Implement IMqttConnectionValidator to enforce specific ClientId formats using regular expressions. This example checks for 'device-{guid}' format. ```csharp public class ClientIdValidator : IMqttConnectionValidator { // Expect ClientIds like "device-{guid}" private static readonly Regex _pattern = new(@"^device-[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$", RegexOptions.Compiled | RegexOptions.IgnoreCase); public ValueTask ValidateAsync( ValidatingConnectionEventArgs ctx, CancellationToken ct) { if (!_pattern.IsMatch(ctx.ClientId)) return ValueTask.FromResult( MqttConnectionValidationResult.Reject( $"Invalid ClientId format: '{ctx.ClientId}'")); return ValueTask.FromResult(MqttConnectionValidationResult.Accept()); } } ``` -------------------------------- ### Multi-tenancy Middleware Setup Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Demonstrates setting up a connection validator to resolve tenant information and a middleware to apply it for each message, enabling multi-tenancy. ```csharp // Connection validator — runs once per connection public class ConnectionValidator : IMqttConnectionValidator { public async ValueTask ValidateAsync( ValidatingConnectionEventArgs ctx, CancellationToken ct) { ctx.SessionItems["tenantId"] = await ResolveTenantAsync(ctx.UserName); return MqttConnectionValidationResult.Accept(); } } // Middleware — runs for every message public class TenantMiddleware(ITenantContext tenant) : IMqttMiddleware { public Task InvokeAsync(MqttMessageContext ctx, MqttRequestDelegate next) { if (ctx.SessionItems["tenantId"] is string tid) tenant.SetTenant(tid); return next(ctx); } } ``` ```csharp builder.Services .AddMqttServer(configuration) .WithControllers() .WithConnectionValidator() .UseMiddleware() .WithAuthentication(); ``` -------------------------------- ### Example: SubscribeAuditHandler for IMqttClientSubscribedTopicEvent Source: https://github.com/thnak/mqttcontrollerframework/blob/master/docs/wiki/Events.md An example handler that records a subscription event to an audit log. It implements the `IMqttClientSubscribedTopicEvent` interface. ```csharp public class SubscribeAuditHandler(IAuditLog audit) : IMqttClientSubscribedTopicEvent { public Task OnClientSubscribedTopicAsync(string clientId, string topicFilter) => audit.RecordAsync($"SUBSCRIBE {clientId} → {topicFilter}"); } ``` -------------------------------- ### Multi-Tenancy Example: Tenant Middleware Source: https://github.com/thnak/mqttcontrollerframework/wiki/Middleware Applies the tenant from SessionItems to the tenant context for each message. Requires ITenantContext. ```csharp // 2. Apply the tenant on every message public class TenantMiddleware(ITenantContext tenantCtx) : IMqttMiddleware { public Task InvokeAsync(MqttMessageContext ctx, MqttRequestDelegate next) { if (ctx.SessionItems["tenantId"] is string tid) tenantCtx.SetTenant(tid); return next(ctx); } } ``` -------------------------------- ### Implement MQTT Authentication Provider Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Example implementation of `IMqttAuthenticationProvider` for handling username and password authentication. It returns `MqttAuthenticationResult.Authenticated()` on success and `MqttAuthenticationResult.Failed()` on failure. ```csharp public class MyAuthProvider : IMqttAuthenticationProvider { public ValueTask AuthenticateAsync( string username, string password, CancellationToken ct) { var ok = username == "device" && password == "secret"; return ValueTask.FromResult( ok ? MqttAuthenticationResult.Authenticated() : MqttAuthenticationResult.Failed("Invalid credentials")); } } ``` -------------------------------- ### Minimal Role-Based Authorization Provider Example Source: https://github.com/thnak/mqttcontrollerframework/wiki/Authorization A basic implementation of IMqttAuthorizationProvider that restricts publishing to 'sensors/' topics based on user roles. It allows all subscriptions. ```csharp public class RoleBasedAuthzProvider(IUserRoleStore roles) : IMqttAuthorizationProvider { public async ValueTask AuthorizePublishAsync( string userName, string topic, int qos, bool retain, CancellationToken ct) { var userRoles = await roles.GetAsync(userName, ct); // Only "device" role may publish to "sensors/#" if (topic.StartsWith("sensors/") && !userRoles.Contains("device")) return MqttAuthorizationResult.Deny("Insufficient role to publish sensor data"); return MqttAuthorizationResult.Allow(); } public ValueTask AuthorizeSubscribeAsync(string userName, string topicFilter, int requestedQos, CancellationToken ct) => ValueTask.FromResult(MqttAuthorizationResult.Allow()); // open subscriptions } ``` -------------------------------- ### Publishing from a Minimal API Endpoint Source: https://github.com/thnak/mqttcontrollerframework/wiki/Server-Actions Example of how to use IMqttClientActionService within a Minimal API endpoint to publish a device command. ```APIDOC ## Endpoint: POST /api/devices/{deviceId}/command ### Description Publishes a device command to a specific device's command topic via MQTT. ### Method POST ### Endpoint `/api/devices/{deviceId}/command` ### Parameters #### Path Parameters - **deviceId** (string) - Required - The ID of the device. #### Request Body - **command** (DeviceCommand) - Required - The command to send to the device. ### Request Example ```json { "command": "some_command", "payload": "some_payload" } ``` ### Response #### Success Response (202 Accepted) Indicates that the command has been accepted for processing. ### Usage ```csharp app.MapPost("/api/devices/{deviceId}/command", async ( string deviceId, DeviceCommand command, IMqttClientActionService mqtt, CancellationToken ct) => { var payload = JsonSerializer.SerializeToUtf8Bytes(command); await mqtt.SendMessageAsync( $"devices/{deviceId}/commands", new ReadOnlySequence(payload), ct); return Results.Accepted(); }); ``` ``` -------------------------------- ### Configure MQTT Settings Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Example configuration for MQTT settings in appsettings.json. This enables non-SSL connections on port 1883. ```json { "MqttSettings": { "EnableNonSsl": true, "NonSslPort": 1883 } } ``` -------------------------------- ### Example: Seeding SessionItems with Tenant Information Source: https://github.com/thnak/mqttcontrollerframework/wiki/Connection-Validation Implement IMqttConnectionValidator to pre-populate SessionItems with data like tenant ID and plan. This data can then be accessed by subsequent middleware without re-querying. ```csharp public class TenantConnectionValidator(ITenantStore tenants) : IMqttConnectionValidator { public async ValueTask ValidateAsync( ValidatingConnectionEventArgs ctx, CancellationToken ct) { var tenant = await tenants.FindByUsernameAsync(ctx.UserName, ct); if (tenant is null) return MqttConnectionValidationResult.Reject("Tenant not found"); // Stored for the lifetime of the TCP connection ctx.SessionItems["tenantId"] = tenant.Id; ctx.SessionItems["tenantPlan"] = tenant.Plan; return MqttConnectionValidationResult.Accept(); } } ``` -------------------------------- ### Example: Presence Disconnected Handler Source: https://github.com/thnak/mqttcontrollerframework/wiki/Events An example implementation of IMqttClientDisconnectedEvent to clean up resources or update presence status when a client disconnects. Requires an IPresenceService. ```csharp public class PresenceDisconnectedHandler(IPresenceService presence) : IMqttClientDisconnectedEvent { public Task OnClientDisconnectedAsync(string clientId) => presence.SetOfflineAsync(clientId); } ``` -------------------------------- ### Multi-Tenancy Example: Connection Validation Source: https://github.com/thnak/mqttcontrollerframework/wiki/Middleware Seeds tenant information into SessionItems during connection validation. Requires an ITenantStore. ```csharp // 1. Seed the tenant into SessionItems at connection time public class ConnectionValidator(ITenantStore store) : IMqttConnectionValidator { public async ValueTask ValidateAsync( ValidatingConnectionEventArgs ctx, CancellationToken ct) { var tenant = await store.FindByUsernameAsync(ctx.UserName, ct); if (tenant is null) return MqttConnectionValidationResult.Reject("Unknown tenant"); ctx.SessionItems["tenantId"] = tenant.Id; return MqttConnectionValidationResult.Accept(); } } ``` -------------------------------- ### Registering Authentication Provider Source: https://github.com/thnak/mqttcontrollerframework/wiki/Authentication Example of how to register a custom authentication provider with the MQTT server builder. This is typically done during application startup. ```csharp builder.Services .AddMqttServer(configuration) .WithControllers() .WithAuthentication(); ``` -------------------------------- ### Database-Backed Authentication Provider Source: https://github.com/thnak/mqttcontrollerframework/wiki/Authentication An example of an `IMqttAuthenticationProvider` that authenticates users against a database. It includes checks for user existence, account lock status, and password verification. ```csharp public class DbAuthProvider(IUserRepository users) : IMqttAuthenticationProvider { public async ValueTask AuthenticateAsync( string username, string password, CancellationToken ct) { var user = await users.FindAsync(username, ct); if (user is null) return MqttAuthenticationResult.Failed("User not found"); if (user.IsLocked) return MqttAuthenticationResult.Locked(user.LockTimeRemaining); return PasswordHasher.Verify(password, user.PasswordHash) ? MqttAuthenticationResult.Authenticated() : MqttAuthenticationResult.Failed("Invalid password"); } } ``` -------------------------------- ### Docker Compose MqttSettings Configuration Source: https://github.com/thnak/mqttcontrollerframework/wiki/Configuration-Reference Example of configuring MqttSettings within a Docker Compose file using environment variables. ```yaml services: mqtt-broker: image: myapp:latest environment: - MqttSettings__EnableNonSsl=false - MqttSettings__EnableSsl=true - MqttSettings__SslPort=8883 - MqttSettings__PkcsPath=/certs/broker.pfx - MqttSettings__PkcsPassword=changeme ports: - "8883:8883" volumes: - ./certs:/certs:ro ``` -------------------------------- ### Example: IP Address Allowlist Validation Source: https://github.com/thnak/mqttcontrollerframework/wiki/Connection-Validation Implement IMqttConnectionValidator to restrict connections to a specific list of IP addresses. This example checks if the remote IP is on an allowlist. ```csharp public class IpAllowlistValidator(IIpAllowlist allowlist) : IMqttConnectionValidator { public async ValueTask ValidateAsync( ValidatingConnectionEventArgs ctx, CancellationToken ct) { var ip = (ctx.RemoteEndPoint as System.Net.IPEndPoint)?.Address; if (ip is not null && !await allowlist.IsAllowedAsync(ip, ct)) return MqttConnectionValidationResult.Reject( $"IP {ip} is not on the allowlist", MqttConnectReasonCode.Banned); return MqttConnectionValidationResult.Accept(); } } ``` -------------------------------- ### Request-Response with SendMessageAsync Source: https://github.com/thnak/mqttcontrollerframework/wiki/Server-Actions Example of publishing a request and awaiting a typed response using the SendMessageAsync overload. ```csharp var request = JsonSerializer.SerializeToUtf8Bytes(new StatusRequest { Verbose = true }); var status = await mqtt.SendMessageAsync( $"devices/{deviceId}/status/request", new ReadOnlySequence(request), ct); if (status is not null) Console.WriteLine($"Device status: {status.State}"); ``` -------------------------------- ### Publishing Alerts from a Background Service Source: https://github.com/thnak/mqttcontrollerframework/wiki/Server-Actions Example of publishing alert messages periodically from a .NET BackgroundService using IMqttClientActionService. ```csharp public class AlertPublisher(IMqttClientActionService mqtt) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken ct) { while (!ct.IsCancellationRequested) { await Task.Delay(TimeSpan.FromMinutes(1), ct); var alert = new AlertMessage { Level = "warning", Text = "High temperature" }; var bytes = JsonSerializer.SerializeToUtf8Bytes(alert); await mqtt.SendMessageAsync("alerts/temperature", new ReadOnlySequence(bytes), ct); } } } ``` -------------------------------- ### Get All Broker Metrics Source: https://github.com/thnak/mqttcontrollerframework/wiki/Broker-Stats Exposes all available MQTT broker statistics via a single HTTP GET endpoint. This endpoint provides a snapshot of current broker performance and status. ```APIDOC ## GET /metrics/mqtt ### Description Retrieves a comprehensive set of real-time statistics for the MQTT broker, including message counts, connected clients, uptime, data transfer, and topic summaries. ### Method GET ### Endpoint /metrics/mqtt ### Parameters None ### Request Body None ### Response #### Success Response (200) - **messages** (ulong) - Total messages received by the broker. - **clients** (ulong) - The total number of currently connected clients. - **uptime** (string) - The duration since the broker started, formatted as a string. - **bytesIn** (ulong) - Total bytes received by the broker. - **bytesOut** (ulong) - Total bytes sent by the broker. - **topics** (object) - A summary of statistics per topic, where keys are topic names and values are objects containing `MessageCount` (ulong) and `ByteCount` (ulong). ### Response Example { "messages": 123456, "clients": 100, "uptime": "1.23:45:01", "bytesIn": 1073741824, "bytesOut": 536870912, "topics": { "sensor/data": { "MessageCount": 50000, "ByteCount": 256000000 }, "alerts": { "MessageCount": 1000, "ByteCount": 100000 } } } ``` -------------------------------- ### Define an MQTT Controller Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Example of a C# controller class with an MQTT topic handler. The `[MqttController]` attribute marks the class, and `[MqttTopic]` defines the topic pattern. Parameters can be bound from the topic using `[FromMqttTopic]`. ```csharp using MqttControllerFramework.Attributes; [MqttController] public class SensorsController { [MqttTopic("sensors/+/temperature")] public async Task OnTemperature( [FromMqttTopic(1)] string deviceId, TemperaturePayload payload, CancellationToken ct) { Console.WriteLine($"Device {deviceId}: {payload.Value}°C"); } } public record TemperaturePayload(double Value, string Unit); ``` -------------------------------- ### Client Certificate Management Source: https://github.com/thnak/mqttcontrollerframework/wiki/TLS-and-Security Manage client certificates for an allowlist using `IHotSwappableServerCertProvider`. This includes onboarding new devices with their certificates, revoking access by thumbprint, and listing installed certificates. ```csharp public class DeviceOnboardingService(IHotSwappableServerCertProvider certProvider) { public void OnboardDevice(byte[] certBytes) { var cert = new X509Certificate2(certBytes); certProvider.InstallNewClientCert(cert); } public void RevokeDevice(string thumbprint) { certProvider.RemoveClientCert(thumbprint); } public List ListDevices() => certProvider.GetInstalledClientCertThumbprints(); } ``` -------------------------------- ### Registering Authorization Provider in Dependency Injection Source: https://github.com/thnak/mqttcontrollerframework/wiki/Authorization Shows how to register a custom authorization provider with the MQTT server builder. This example registers the RoleBasedAuthzProvider. ```csharp builder.Services .AddMqttServer(configuration) .WithControllers() .WithAuthentication() .WithAuthorization(); ``` -------------------------------- ### Request-Response with GetDataFromTopicAsync Source: https://github.com/thnak/mqttcontrollerframework/wiki/Server-Actions Example of making a request without a payload and awaiting a typed response using GetDataFromTopicAsync. ```csharp var latest = await mqtt.GetDataFromTopicAsync("sensors/room1/temperature/latest", ct); ``` -------------------------------- ### Accessing Services in MQTT Middleware Source: https://github.com/thnak/mqttcontrollerframework/wiki/Middleware Example of an AuditMiddleware that resolves an IAuditService from the per-message DI scope to log message details before proceeding. ```csharp public class AuditMiddleware : IMqttMiddleware { public async Task InvokeAsync(MqttMessageContext ctx, MqttRequestDelegate next) { var audit = ctx.Services.GetRequiredService(); await audit.LogAsync(ctx.ClientId, ctx.Topic); await next(ctx); } } ``` -------------------------------- ### Zero-Reflection Dispatch Example Source: https://github.com/thnak/mqttcontrollerframework/wiki/Performance Demonstrates a generated dispatcher method that avoids reflection for invoking controller actions, including topic segment extraction and JSON deserialization. ```csharp // Generated — no MethodInfo.Invoke, no expression trees public async ValueTask Dispatch_OnTemperature( InterceptingPublishEventArgs args, string topic, CancellationToken ct) { var topicSegments = topic.Split('/'); var deviceId = (string)Convert.ChangeType(topicSegments[1], typeof(string)); var payload = default(TemperaturePayload); if (args.ApplicationMessage.Payload.Length > 0) { var ti = _typeInfoCache.TryGet(typeof(TemperaturePayload)) ?? _jsonOptions.GetTypeInfo(typeof(TemperaturePayload)); if (ti != null) { var r = new Utf8JsonReader(args.ApplicationMessage.Payload); payload = (TemperaturePayload)JsonSerializer.Deserialize(ref r, ti)!; } } await _controller.OnTemperature(deviceId, payload!, ct); } ``` -------------------------------- ### Persist Client Certificates Source: https://github.com/thnak/mqttcontrollerframework/wiki/TLS-and-Security Configure the MQTT settings to persist installed client certificates to a specified directory. Certificates are saved as .cer files and automatically reloaded on broker startup. ```json "MqttSettings": { "ClientCertStorage": "/var/mqtt/client-certs" } ``` -------------------------------- ### Example: 10 Messages/Second with Burst Source: https://github.com/thnak/mqttcontrollerframework/wiki/Rate-Limiting Applies a rate limit allowing up to 10 messages per second with a burst capacity of 10. This is suitable for telemetry data where sustained throughput is important. ```csharp [MqttController] public class TelemetryController { [MqttTopic("telemetry/+/data")] [TokenBucketRateLimit(capacity: 10, refillRate: 10, refillIntervalMs: 1000)] public Task OnData([FromMqttTopic(1)] string deviceId, SensorData data) { ... } } ``` -------------------------------- ### Register Rate Limiting Service Source: https://github.com/thnak/mqttcontrollerframework/wiki/Rate-Limiting Registers the rate limiting service during MQTT server setup. This is a required step before applying rate limit attributes. ```csharp builder.Services .AddMqttServer(configuration) .WithControllers() .WithAuthentication() .WithRateLimiting(); // ← registers the rate limit service ``` -------------------------------- ### SqlRetainStorage Implementation for SQL Server Source: https://github.com/thnak/mqttcontrollerframework/wiki/Retained-Messages An example implementation of IRetainStorage for persisting retained messages in a SQL Server database. It handles loading, saving, and clearing messages. ```csharp public class SqlRetainStorage(IDbConnectionFactory db) : IRetainStorage { public async Task> LoadAsync(CancellationToken ct = default) { await using var conn = await db.OpenAsync(ct); var rows = await conn.QueryAsync("SELECT Topic, Payload, ContentType FROM RetainedMessages"); return rows.Select(r => new MqttApplicationMessageBuilder() .WithTopic(r.Topic) .WithPayload(r.Payload) .WithContentType(r.ContentType) .WithRetainFlag(true) .Build()).ToList(); } public async Task SaveAsync(IReadOnlyList messages, CancellationToken ct = default) { await using var conn = await db.OpenAsync(ct); await conn.ExecuteAsync("DELETE FROM RetainedMessages"); foreach (var m in messages) await conn.ExecuteAsync( "INSERT INTO RetainedMessages(Topic, Payload, ContentType) VALUES(@Topic, @Payload, @ContentType)", new { m.Topic, Payload = m.Payload.ToArray(), m.ContentType }); } public async Task ClearAsync(CancellationToken ct = default) { await using var conn = await db.OpenAsync(ct); await conn.ExecuteAsync("DELETE FROM RetainedMessages"); } } ``` -------------------------------- ### Example: High-Cost Operation Rate Limit Source: https://github.com/thnak/mqttcontrollerframework/wiki/Rate-Limiting Configures a rate limit for a high-cost operation, consuming 5 tokens per request with a burst capacity of 50. This ensures that infrequent but resource-intensive operations do not overwhelm the system. ```csharp [MqttTopic("bulk/+/import")] [TokenBucketRateLimit(capacity: 50, refillRate: 10, refillIntervalMs: 1000, TokensPerRequest = 5)] public Task OnBulkImport([FromMqttTopic(1)] string deviceId, BulkPayload payload) { ... } ``` -------------------------------- ### Example: Stacking Multiple Rate Limits Source: https://github.com/thnak/mqttcontrollerframework/wiki/Rate-Limiting Applies both a short-term burst limit and a long-term sustained limit to a single method. This provides granular control over message flow for commands that require both immediate responsiveness and overall throughput management. ```csharp [MqttTopic("commands/+/execute")] [TokenBucketRateLimit(capacity: 5, refillRate: 5, refillIntervalMs: 1000, Name = "burst")] [TokenBucketRateLimit(capacity: 60, refillRate: 60, refillIntervalMs: 60000, Name = "sustained")] public Task OnExecute([FromMqttTopic(1)] string deviceId, Command cmd) { ... } ``` -------------------------------- ### Publishing Commands from a Minimal API Endpoint Source: https://github.com/thnak/mqttcontrollerframework/wiki/Server-Actions Demonstrates publishing device commands from a .NET Minimal API endpoint using IMqttClientActionService. ```csharp app.MapPost("/api/devices/{deviceId}/command", async ( string deviceId, DeviceCommand command, IMqttClientActionService mqtt, CancellationToken ct) => { var payload = JsonSerializer.SerializeToUtf8Bytes(command); await mqtt.SendMessageAsync( $"devices/{deviceId}/commands", new ReadOnlySequence(payload), ct); return Results.Accepted(); }); ``` -------------------------------- ### Add MqttControllerFramework Services Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Registers MQTT server services and controllers with dependency injection. Use this in your Program.cs file. ```csharp builder.Services .AddMqttServer(builder.Configuration.GetSection("MqttSettings")) .WithControllers() .WithAuthentication(); ``` -------------------------------- ### Environment Variables for MqttSettings Source: https://github.com/thnak/mqttcontrollerframework/wiki/Configuration-Reference Setting MqttSettings via environment variables, recommended for Docker and Kubernetes. ```bash MqttSettings__EnableSsl=true MqttSettings__SslPort=8883 MqttSettings__PkcsPath=/etc/certs/broker.pfx MqttSettings__PkcsPassword=changeme ``` -------------------------------- ### Registering MQTT Controllers with Generated Code Source: https://github.com/thnak/mqttcontrollerframework/wiki/Controllers-and-Routing Demonstrates how to reference the generated controller registration class and add it to the service collection. Ensure the generated namespace is included if necessary. ```csharp using MyApp.Mqtt.Generated; builder.Services .AddMqttServer(configuration) .WithControllers(); ``` -------------------------------- ### Expose MQTT Metrics via Minimal API Source: https://github.com/thnak/mqttcontrollerframework/wiki/Broker-Stats Shows how to expose MQTT broker statistics like message counts, client counts, and uptime through a minimal API endpoint. ```csharp app.MapGet("/metrics/mqtt", (IMqttBrokerStatsService stats) => new { messages = stats.GetTotalMessageCount(), clients = stats.GetTotalConnectedClientCount(), uptime = stats.GetUptime().ToString(), bytesIn = stats.GetTotalBytesReceivedByBroker(), bytesOut = stats.GetTotalBytesSentByBroker(), topics = stats.GetTopicSummary(), }); ``` -------------------------------- ### InMemoryRetainStorage Implementation Source: https://github.com/thnak/mqttcontrollerframework/wiki/Retained-Messages An example of an in-memory retained message storage that does not persist data across restarts. It implements the IRetainStorage interface. ```csharp public class InMemoryRetainStorage : IRetainStorage { private IReadOnlyList _messages = []; public Task> LoadAsync(CancellationToken ct = default) => Task.FromResult(_messages); public Task SaveAsync(IReadOnlyList messages, CancellationToken ct = default) { _messages = messages; return Task.CompletedTask; } public Task ClearAsync(CancellationToken ct = default) { _messages = []; return Task.CompletedTask; } } ``` -------------------------------- ### Add MqttControllerFramework Package Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Command to add the MqttControllerFramework NuGet package to your .NET project. ```bash dotnet add package MqttControllerFramework ``` -------------------------------- ### Define MQTT Controllers with Prefixes Source: https://github.com/thnak/mqttcontrollerframework/wiki/Controllers-and-Routing Decorate C# classes with `[MqttController]` to define MQTT controllers. Use the optional `prefix` argument to prepend a base topic to all routes within the controller. ```csharp [MqttController] public class SensorsController { } [MqttController("devices")] public class DeviceController { } ``` -------------------------------- ### Full MQTT Server Builder Configuration Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Configures the MQTT server with various services including controllers, authentication, authorization, connection validation, network tracking, retain storage, middleware, rate limiting, and event handlers. ```csharp builder.Services .AddMqttServer(configuration) // registers the broker, reads MqttSettings section .WithControllers() // source-generated registration class .WithAuthentication() // IMqttAuthenticationProvider (required) .WithAuthorization() // IMqttAuthorizationProvider (optional) .WithConnectionValidator() // IMqttConnectionValidator — pre-auth hook .WithNetworkTracker() // replace built-in in-memory tracker .WithRetainStorage() // IRetainStorage — custom retain message backend .UseMiddleware() // IMqttMiddleware (ordered, chainable) .WithRateLimiting() // enable token-bucket rate limiting .OnClientConnected() // IMqttClientConnectedEvent .OnClientDisconnected() // IMqttClientDisconnectedEvent .OnClientSubscribedTopic() // IMqttClientSubscribedTopicEvent .OnClientUnsubscribedTopic(); // IMqttClientUnsubscribedTopicEvent ``` -------------------------------- ### Add MqttControllerFramework Project Reference Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md XML snippet to add the MqttControllerFramework NuGet package directly to your project file. ```xml ``` -------------------------------- ### Registering MQTT Lifecycle Event Handlers Source: https://github.com/thnak/mqttcontrollerframework/wiki/Events Demonstrates how to chain multiple event handlers for various MQTT client lifecycle events during service registration. Handlers are registered as scoped services. ```csharp builder.Services .AddMqttServer(configuration) .WithControllers() .WithAuthentication() .OnClientConnected() .OnClientConnected() // two handlers for the same event .OnClientDisconnected() .OnClientSubscribedTopic() .OnClientUnsubscribedTopic(); ``` -------------------------------- ### Minimal TLS Only Configuration Source: https://github.com/thnak/mqttcontrollerframework/wiki/Configuration-Reference Configuration for TLS only, recommended for production environments. Requires certificate and password. ```json "MqttSettings": { "EnableNonSsl": false, "EnableSsl": true, "SslPort": 8883, "PkcsPath": "/etc/certs/broker.pfx", "PkcsPassword": "changeme" } ``` -------------------------------- ### Minimal Both Ports Configuration Source: https://github.com/thnak/mqttcontrollerframework/wiki/Configuration-Reference Configuration enabling both plain TCP and TLS ports, useful for migration or development/production parity. ```json "MqttSettings": { "EnableNonSsl": true, "NonSslPort": 1883, "EnableSsl": true, "SslPort": 8883, "PkcsPath": "/etc/certs/broker.pfx", "PkcsPassword": "changeme" } ``` -------------------------------- ### Route Matching Strategy Overview Source: https://github.com/thnak/mqttcontrollerframework/wiki/Performance Illustrates the two-stage route matching process involving an LRU string cache and a DFA pattern match for performance. ```text topic string → cache lookup → route id (if hit) ``` -------------------------------- ### Production Logging Configuration Source: https://github.com/thnak/mqttcontrollerframework/wiki/Performance Configure the minimum log level for the MqttControllerFramework namespace to 'Warning' or higher in production environments to eliminate trace overhead. ```json { "Logging": { "LogLevel": { "MqttControllerFramework": "Warning" } } } ``` -------------------------------- ### Request-Response via MQTT Source: https://github.com/thnak/mqttcontrollerframework/wiki/Server-Actions Demonstrates using `SendMessageAsync` and `GetDataFromTopicAsync` for request-response patterns over MQTT. ```APIDOC ## Request-Response Patterns ### Using `SendMessageAsync` Publish a request and await a typed response. #### Example: ```csharp var request = JsonSerializer.SerializeToUtf8Bytes(new StatusRequest { Verbose = true }); var status = await mqtt.SendMessageAsync( $"devices/{deviceId}/status/request", new ReadOnlySequence(request), ct); if (status is not null) Console.WriteLine($"Device status: {status.State}"); ``` ### Using `GetDataFromTopicAsync` Send a request without a payload and await a typed response. #### Example: ```csharp var latest = await mqtt.GetDataFromTopicAsync("sensors/room1/temperature/latest", ct); ``` ``` -------------------------------- ### Implement Request-Response Pattern Source: https://github.com/thnak/mqttcontrollerframework/wiki/Controllers-and-Routing Handlers can return `Task`. If the incoming message includes a `ResponseTopic`, the framework automatically serializes the return value and publishes it to that topic. ```csharp [MqttTopic("queries/+/status")] public async Task GetStatus([FromMqttTopic(1)] string deviceId) { return await _repository.GetStatusAsync(deviceId); } ``` -------------------------------- ### Defining MQTT Topic Handlers Source: https://github.com/thnak/mqttcontrollerframework/blob/master/docs/wiki/Controllers-and-Routing.md Decorate public methods with [MqttTopic] to create MQTT topic handlers. Supports exact matches, single-level wildcards (+), and multi-level wildcards (#). ```csharp [MqttTopic("sensors/+/temperature")] // single-level wildcard [MqttTopic("logs/#")] // multi-level wildcard (must be last segment) [MqttTopic("system/status")] // exact match ``` -------------------------------- ### Minimal Plain TCP Configuration Source: https://github.com/thnak/mqttcontrollerframework/wiki/Configuration-Reference Configuration for plain TCP only, suitable for development environments. ```json "MqttSettings": { "EnableNonSsl": true, "NonSslPort": 1883 } ``` -------------------------------- ### Minimal PEM Certificate and Private Key Configuration Source: https://github.com/thnak/mqttcontrollerframework/wiki/Configuration-Reference Configuration for TLS using PEM certificate and private key files. ```json "MqttSettings": { "EnableSsl": true, "SslPort": 8883, "PkcsPath": "/etc/certs/broker.crt", "PkcsKeyPath": "/etc/certs/broker.key" } ``` -------------------------------- ### Configuring Server Origin Property for Self-Loop Prevention Source: https://github.com/thnak/mqttcontrollerframework/wiki/Server-Actions Illustrates how to configure the MQTT user property used to prevent infinite loops for server-originated messages. ```json "MqttSettings": { "ServerOriginPropertyName": "x-mqtt-origin", "ServerOriginPropertyValue": "server" } ``` -------------------------------- ### Accepting a Connection Source: https://github.com/thnak/mqttcontrollerframework/wiki/Connection-Validation Use MqttConnectionValidationResult.Accept() to allow the connection to proceed without any special conditions. ```csharp MqttConnectionValidationResult.Accept() ``` -------------------------------- ### Minimal API Key Authentication Provider Source: https://github.com/thnak/mqttcontrollerframework/wiki/Authentication A basic implementation of `IMqttAuthenticationProvider` using a static dictionary for API key validation. Suitable for simple scenarios with predefined keys. ```csharp public class ApiKeyAuthProvider : IMqttAuthenticationProvider { private static readonly Dictionary _keys = new() { ["device-001"] = "key-abc123", ["device-002"] = "key-def456", }; public ValueTask AuthenticateAsync( string username, string password, CancellationToken ct) { var result = _keys.TryGetValue(username, out var key) && key == password ? MqttAuthenticationResult.Authenticated() : MqttAuthenticationResult.Failed("Unknown device or invalid key"); return ValueTask.FromResult(result); } } ``` -------------------------------- ### TLS Configuration with PKCS Certificate Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Configures the MQTT server to enable SSL/TLS using a PKCS certificate file with a specified password. ```json "MqttSettings": { "EnableSsl": true, "SslPort": 8883, "PkcsPath": "/etc/certs/broker.pfx", "PkcsPassword": "changeme" } ``` -------------------------------- ### Applying [MqttAuthorize] Attribute to Controller and Method Source: https://github.com/thnak/mqttcontrollerframework/wiki/Authorization Demonstrates using the [MqttAuthorize] attribute to enforce authorization on all handlers within a controller, and how [MqttAllowAnonymous] can override this for specific methods. ```csharp [MqttController] [MqttAuthorize] // all handlers in this controller require authorization public class AdminController { [MqttTopic("admin/+/command")] public Task OnCommand([FromMqttTopic(1)] string deviceId, AdminCommand cmd) { ... } [MqttTopic("admin/ping")] [MqttAllowAnonymous] // override class-level — no auth required public Task OnPing() { ... } } ``` -------------------------------- ### Runtime Certificate Hot-Swap Implementation Source: https://github.com/thnak/mqttcontrollerframework/wiki/TLS-and-Security Implement a background service to periodically load and update the server certificate using `IHotSwappableServerCertProvider`. The certificate swap is atomic, affecting new TLS handshakes immediately. ```csharp public class CertRotationJob(IHotSwappableServerCertProvider certProvider) : IHostedService { public async Task StartAsync(CancellationToken ct) { while (!ct.IsCancellationRequested) { await Task.Delay(TimeSpan.FromHours(24), ct); var newCert = LoadLatestCertificate(); certProvider.UpdateCertificate(newCert); } } public Task StopAsync(CancellationToken ct) => Task.CompletedTask; } ``` -------------------------------- ### Bind MQTT Topic Segments to Parameters Source: https://github.com/thnak/mqttcontrollerframework/wiki/Controllers-and-Routing Use the `[FromMqttTopic(n)]` attribute to extract values from specific wildcard segments (`+`) in the MQTT topic and bind them to method parameters. The index `n` corresponds to the position of the wildcard. ```csharp [MqttTopic("devices/+/sensors/+/data")] public Task OnData( [FromMqttTopic(1)] string deviceId, [FromMqttTopic(2)] string sensorId, SensorReading payload) { } ``` -------------------------------- ### Accepting and Bypassing Authentication Source: https://github.com/thnak/mqttcontrollerframework/wiki/Connection-Validation Use MqttConnectionValidationResult.AcceptAndBypassAuth() to allow the connection and skip the IMqttAuthenticationProvider step. ```csharp MqttConnectionValidationResult.AcceptAndBypassAuth() ``` -------------------------------- ### Registering Middleware with UseMiddleware Source: https://github.com/thnak/mqttcontrollerframework/wiki/Middleware Adds middleware to the pipeline using the builder. Middleware is resolved from the per-message DI scope. ```csharp builder.Services .AddMqttServer(configuration) .WithControllers() .UseMiddleware() // runs first .UseMiddleware() // runs second .UseMiddleware() // runs third .WithAuthentication(); ``` -------------------------------- ### Register MQTT Server Services Source: https://github.com/thnak/mqttcontrollerframework/wiki/Connection-Validation Configure the MQTT server services, including the connection validator, authentication provider, and controller registration. The validator is registered as scoped. ```csharp builder.Services .AddMqttServer(configuration) .WithConnectionValidator() .WithAuthentication() .WithControllers(); ``` -------------------------------- ### MqttSettings for Retained Messages File Path Source: https://github.com/thnak/mqttcontrollerframework/wiki/Retained-Messages Configure the file path for storing retained messages. The file is created automatically if it doesn't exist. ```json "MqttSettings": { "RetainedMessagesFilePath": "RetainedMessages.json" } ``` -------------------------------- ### Inject IMqttBrokerStatsService in IHostedService Source: https://github.com/thnak/mqttcontrollerframework/wiki/Broker-Stats Demonstrates injecting the IMqttBrokerStatsService into an IHostedService for accessing broker statistics. ```csharp public class MetricsEndpoints(IMqttBrokerStatsService stats) : IHostedService { public Task StartAsync(CancellationToken ct) => Task.CompletedTask; public Task StopAsync(CancellationToken ct) => Task.CompletedTask; } ``` -------------------------------- ### IMqttMiddleware Interface Definition Source: https://github.com/thnak/mqttcontrollerframework/wiki/Middleware Defines the contract for middleware components. Implement this interface to create custom middleware. ```csharp public interface IMqttMiddleware { Task InvokeAsync(MqttMessageContext ctx, MqttRequestDelegate next); } ``` -------------------------------- ### Registering Source-Generated JSON Context Source: https://github.com/thnak/mqttcontrollerframework/wiki/Performance Shows how to register a custom source-generated JSON context with the MQTT server options to enable AOT-safe and allocation-minimal JSON serialization. ```csharp [JsonSerializable(typeof(TemperaturePayload))] [JsonSerializable(typeof(DeviceCommand))] public partial class AppJsonContext : JsonSerializerContext { } builder.Services .AddMqttServer(configuration) .WithControllers(options => { options.TypeInfoResolverChain.Insert(0, AppJsonContext.Default); }); ``` -------------------------------- ### Custom Sliding Window Rate Limit Attribute Source: https://github.com/thnak/mqttcontrollerframework/wiki/Rate-Limiting Demonstrates creating a custom rate limit attribute by inheriting from `RateLimitAttribute`. This allows for implementing alternative strategies like sliding window limits. ```csharp public sealed class SlidingWindowRateLimitAttribute(int windowMs, int maxRequests) : RateLimitAttribute { public override string StrategyType => "SlidingWindow"; public override bool ValidateConfiguration() => windowMs > 0 && maxRequests > 0; public override Dictionary GetConfiguration() => new() { ["WindowMs"] = windowMs, ["MaxRequests"] = maxRequests }; } ``` -------------------------------- ### Rate Limiting Configuration for Controller Source: https://github.com/thnak/mqttcontrollerframework/blob/master/README.md Applies token bucket rate limiting to an MQTT controller endpoint, allowing a burst of messages and defining refill rates. ```csharp [MqttController] public class TelemetryController { // Allow burst of 10, refill 5 tokens every 1 second, consume 1 per message [MqttTopic("telemetry/+/data")] [TokenBucketRateLimit(capacity: 10, refillRate: 5, refillIntervalMs: 1000)] public Task OnData([FromMqttTopic(1)] string deviceId, SensorData payload) { ... } } ``` ```csharp .WithRateLimiting() ```