dotnet / MQTTnet

MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from http://mqtt.org/.
MIT License
4.51k stars 1.07k forks source link

Plug in custom implementation of IMqttServerApplicationMessageInterceptor #947

Closed ovidiaconescu closed 4 years ago

ovidiaconescu commented 4 years ago

Hello, is it possible to plug in a custom implementation of IMqttServerApplicationMessageInterceptor via Dependency Injection? The only example with this seems to be https://github.com/rafiulgits/IotHub

Ideally, something like this would work best services.AddScoped<IMqttServerApplicationMessageInterceptor, MessageInterceptor>();

instead of

services
                .AddHostedMqttServer(
                        t => t.WithDefaultEndpointPort(1883)
                        .WithApplicationMessageInterceptor(new MessageInterceptor()))
                .AddMqttConnectionHandler()
                .AddConnections();

Scoped seems to be the most used lifecycle as it would work with Entity Framework Core DbCodex which is also Scoped by default. This way the Interceptor would be able to access the database to validate clients and store messages.

scabana commented 4 years ago

Hi, until this gets implemented, if you are in an asp.net core app:

Change IMqttServerConnectionValidator for IMqttServerApplicationMessageInterceptor

public class InitializableMqttServerConnectionValidator : IMqttServerConnectionValidator
{
    private IServiceProvider _serviceProvider;

    public void Init(IServiceProvider serviceProvider)
    {
        if (_serviceProvider != null)
        {
            throw new InvalidOperationException("Can't init a second time!");
        }

        _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
    }

    public async Task ValidateConnectionAsync(MqttConnectionValidatorContext context)
    {
        if (_serviceProvider == null)
        {
            throw new Exception("Can't validate if Init not called");
        }

        using IServiceScope scope = _serviceProvider.CreateScope();

        await scope.ServiceProvider.GetRequiredService<IMqttServerConnectionValidator>().ValidateConnectionAsync(context);
    }
}

And in ConfigureServices:

services.AddHostedMqttServer(t => t.WithDefaultEndpointPort(1883).WithConnectionValidator(new InitializableMqttServerConnectionValidator())

And in Configure

app.UseMqttServer(server =>
{
    ((InitializableMqttServerConnectionValidator)server.Options.ConnectionValidator).Init(app.ApplicationServices);
});

And don't forget to register a IMqttServerConnectionValidator in your DI container!

But this is not how it should be implemented here. Ideally, the different services in the config should be DI resovled if not specified.

BrightSoul commented 4 years ago

Yes please, I also need dependency injection for my custom application message interceptor. Right now I'm using this hack which is pretty ugly and produces a warning at compilation.

services.AddSingleton<IMqttServerApplicationMessageInterceptor, MyCustomApplicationMessageInterceptor>();
var serviceProvider = services.BuildServiceProvider();
var interceptorInstance = serviceProvider.GetService<IMqttServerApplicationMessageInterceptor>();
services
  .AddHostedMqttServer(mqttServer => {
    mqttServer.WithApplicationMessageInterceptor(interceptorInstance).WithoutDefaultEndpoint();
});

I second @ovidiaconescu's solution and this would also be ok, assuming it uses the dependency resolver to construct the application message interceptor.

services
  .AddHostedMqttServer(mqttServer => {
    mqttServer.WithApplicationMessageInterceptor<MyCustomApplicationMessageInterceptor>().WithoutDefaultEndpoint();
});
avishnyak commented 4 years ago

If you are using MQTTnet with AspNetCore, this library handles a similar use case https://github.com/Atlas-LiftTech/MQTTnet.AspNetCore.AttributeRouting. It works with the built-in dependency injection in AspNetCore and allows you to treat your MessageInterceptors as controller actions.

Just wanted to makes sure you are aware of the option.

BrightSoul commented 4 years ago

Just wanted to makes sure you are aware of the option.

@avishnyak I wasn't aware of this, thank you. It looks interesting so I'll definitely give it a look.