Skip to content

Commit bdcca34

Browse files
committed
1. Support for intercepting method invocations #7
2. Refactor the code
1 parent cd4c094 commit bdcca34

File tree

7 files changed

+188
-24
lines changed

7 files changed

+188
-24
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using ExampleServer.MqttControllers;
2+
using Microsoft.Extensions.Logging;
3+
using MQTTnet;
4+
using MQTTnet.AspNetCore.Routing.Routing;
5+
using System;
6+
using System.Threading.Tasks;
7+
8+
namespace ExampleServer
9+
{
10+
public class DemoRouteInvocationInterceptor : IRouteInvocationInterceptor
11+
{
12+
private readonly ILogger _logger;
13+
14+
public DemoRouteInvocationInterceptor(ILogger<DemoRouteInvocationInterceptor> logger)
15+
{
16+
_logger = logger;
17+
}
18+
public Task RouteExecuted(object o, Exception ex)
19+
{
20+
_logger.LogInformation($" {ex.Message}");
21+
return Task.CompletedTask;
22+
}
23+
24+
public Task<object> RouteExecuting(string clientId, MqttApplicationMessage applicationMessage)
25+
{
26+
object obj = new { clientId, applicationMessage.Topic };
27+
_logger.LogInformation($"{clientId},{applicationMessage.Topic}");
28+
return Task.FromResult(obj);
29+
}
30+
}
31+
}

ExampleServer/Startup.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
using System.Linq;
2+
using System.Runtime.Intrinsics.X86;
3+
using System.Text;
4+
using System.Text.Encodings.Web;
25
using System.Text.Json;
6+
using System.Text.Unicode;
7+
using ExampleServer;
38
using Microsoft.AspNetCore.Builder;
49
using Microsoft.AspNetCore.Hosting;
510
using Microsoft.Extensions.Configuration;
611
using Microsoft.Extensions.DependencyInjection;
712
using Microsoft.Extensions.Hosting;
13+
using Microsoft.Extensions.Options;
814
using MQTTnet.AspNetCore;
915
using MQTTnet.AspNetCore.Routing;
16+
using MQTTnet.AspNetCore.Routing.Routing;
1017
using MQTTnet.Server;
1118

1219
namespace Example
@@ -29,21 +36,29 @@ public void ConfigureServices(IServiceCollection services)
2936
services.AddSingleton<MqttServer>();
3037

3138
// Identify and build routes for the current assembly
32-
services.AddMqttControllers();
33-
39+
services.AddMqttControllers(opt =>
40+
{
41+
opt.WithJsonSerializerOptions(new JsonSerializerOptions(JsonSerializerDefaults.Web) { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) })
42+
.WithRouteInvocationInterceptor<DemoRouteInvocationInterceptor>();
43+
});
44+
45+
3446
// Use specific deserialization option for MQTT payload deserialization
35-
services.AddMqttDefaultJsonOptions(new JsonSerializerOptions(JsonSerializerDefaults.Web));
36-
47+
//services.AddMqttDefaultJsonOptions(new JsonSerializerOptions(JsonSerializerDefaults.Web));
48+
3749
services
3850
.AddHostedMqttServerWithServices(s =>
3951
{
4052
// Optionally set server options here
4153
s.WithoutDefaultEndpoint();
54+
4255
})
4356
.AddMqttConnectionHandler()
4457
.AddConnections();
4558
}
4659

60+
61+
4762
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
4863
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
4964
{
@@ -69,8 +84,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
6984
app.UseMqttServer(server =>
7085
{
7186
// Enable Attribute routing
72-
server.WithAttributeRouting(app.ApplicationServices, true);
87+
// server.WithAttributeRouting(app.ApplicationServices, true);
7388
});
89+
app.UseAttributeRouting();
7490
}
7591
}
7692
}
Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
// Copyright (c) Atlas Lift Tech Inc. All rights reserved.
22

3+
using Microsoft.AspNetCore.Builder;
4+
using Microsoft.AspNetCore.Hosting.Server;
35
using Microsoft.Extensions.DependencyInjection;
46
using MQTTnet.AspNetCore.Routing;
7+
using MQTTnet.AspNetCore.Routing.Routing;
58
using MQTTnet.Server;
69
using System;
710
using System.Reflection;
811
using System.Runtime.CompilerServices;
12+
using System.Text.Encodings.Web;
913
using System.Text.Json;
14+
using System.Text.Unicode;
1015

1116
// This is needed to make internal classes visible to UnitTesting projects
1217
[assembly: InternalsVisibleTo("MQTTnet.AspNetCore.Routing.Tests, PublicKey=00240000048000009" +
@@ -19,40 +24,123 @@ namespace MQTTnet.AspNetCore.Routing
1924
{
2025
public static class ServiceCollectionExtensions
2126
{
22-
public static IServiceCollection AddMqttControllers(this IServiceCollection services, Assembly[] fromAssemblies = null)
27+
public static IServiceCollection AddMqttControllers(this IServiceCollection services, Assembly[] fromAssemblies)
2328
{
29+
return services.AddMqttControllers(opt => opt.FromAssemblies = fromAssemblies);
30+
}
31+
public static IServiceCollection AddMqttControllers(this IServiceCollection services)
32+
{
33+
return services.AddMqttControllers(opt => { });
34+
}
35+
public static IServiceCollection AddMqttControllers(this IServiceCollection services, Action< MqttRoutingOptions> _options)
36+
{
37+
var _opt = new MqttRoutingOptions();
38+
_opt.WithJsonSerializerOptions();
39+
_opt.FromAssemblies = null;
40+
_opt.RouteInvocationInterceptor = null;
41+
_options?.Invoke( _opt);
42+
43+
services.AddSingleton(_opt);
2444
services.AddSingleton(_ =>
2545
{
46+
47+
var fromAssemblies= _opt.FromAssemblies;
2648
if (fromAssemblies != null && fromAssemblies.Length == 0)
2749
{
2850
throw new ArgumentException("'fromAssemblies' cannot be an empty array. Pass null or a collection of 1 or more assemblies.", nameof(fromAssemblies));
2951
}
30-
3152
var assemblies = fromAssemblies ?? new Assembly[] { Assembly.GetEntryAssembly() };
3253

3354
return MqttRouteTableFactory.Create(assemblies);
3455
});
3556

3657
services.AddSingleton<ITypeActivatorCache>(new TypeActivatorCache());
3758
services.AddSingleton<MqttRouter>();
38-
59+
if (_opt.RouteInvocationInterceptor == null)
60+
{
61+
services.AddSingleton(typeof( IRouteInvocationInterceptor), _opt.RouteInvocationInterceptor);
62+
}
3963
return services;
4064
}
65+
public static void WithRouteInvocationInterceptor<T>(this MqttRoutingOptions opt) where T : IRouteInvocationInterceptor
66+
{
67+
opt.RouteInvocationInterceptor = typeof(T);
68+
}
69+
public static MqttRoutingOptions WithJsonSerializerOptions(this MqttRoutingOptions opt)
70+
{
71+
#if NET5_0_OR_GREATER
72+
var jopt = new JsonSerializerOptions(JsonSerializerDefaults.Web);
73+
#else
74+
var jopt = new JsonSerializerOptions();
75+
jopt.PropertyNameCaseInsensitive = true;
76+
#endif
77+
jopt.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
78+
opt.SerializerOptions = jopt;
79+
return opt;
80+
}
81+
82+
public static MqttRoutingOptions WithJsonSerializerOptions(this MqttRoutingOptions opt, JsonSerializerOptions options)
83+
{
84+
opt.SerializerOptions = options;
85+
return opt;
86+
}
4187

88+
89+
[Obsolete("Use 'services.AddMqttControllers(opt => opt.SerializerOptions= new JsonSerializerOptions());' instead ")]
4290
public static IServiceCollection AddMqttDefaultJsonOptions(this IServiceCollection services,
4391
JsonSerializerOptions options)
4492
{
45-
services.AddSingleton(new MqttDefaultJsonOptions(options));
93+
services.AddSingleton(new MqttRoutingOptions() { SerializerOptions = options });
4694
return services;
4795
}
4896

97+
public static IApplicationBuilder UseAttributeRouting(this IApplicationBuilder app, bool allowUnmatchedRoutes = false)
98+
{
99+
var router = app.ApplicationServices.GetRequiredService<MqttRouter>();
100+
var server = app.ApplicationServices.GetRequiredService<MqttServer>();
101+
var interceptor = app.ApplicationServices.GetService<IRouteInvocationInterceptor>();
102+
server.InterceptingPublishAsync += async (args) =>
103+
{
104+
await interceptor?.RouteExecuting(args.ClientId, args.ApplicationMessage);
105+
try
106+
{
107+
await router.OnIncomingApplicationMessage(app.ApplicationServices, args, allowUnmatchedRoutes);
108+
}
109+
catch (Exception ex)
110+
{
111+
await interceptor?.RouteExecuted(args, ex);
112+
if (interceptor == null)
113+
{
114+
throw;
115+
}
116+
}
117+
};
118+
return app;
119+
}
120+
121+
[Obsolete("Use UseAttributeRouting instead")]
49122
public static void WithAttributeRouting(this MqttServer server, IServiceProvider svcProvider, bool allowUnmatchedRoutes = false)
50123
{
51124
var router = svcProvider.GetRequiredService<MqttRouter>();
125+
var interceptor = svcProvider.GetRequiredService<IRouteInvocationInterceptor>();
52126
server.InterceptingPublishAsync += async (args) =>
53127
{
54-
await router.OnIncomingApplicationMessage(svcProvider, args, allowUnmatchedRoutes);
128+
await interceptor?.RouteExecuting(args.ClientId, args.ApplicationMessage);
129+
try
130+
{
131+
await router.OnIncomingApplicationMessage(svcProvider, args, allowUnmatchedRoutes);
132+
}
133+
catch (Exception ex)
134+
{
135+
await interceptor?.RouteExecuted(args, ex);
136+
if (interceptor == null)
137+
{
138+
throw;
139+
}
140+
}
55141
};
56142
}
143+
144+
57145
}
58146
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
6+
namespace MQTTnet.AspNetCore.Routing.Routing
7+
{
8+
public interface IRouteInvocationInterceptor
9+
{
10+
/// <summary>
11+
/// Executed before the route handler is executed
12+
/// </summary>
13+
/// <param name="clientId">The identifier of sender of the message</param>
14+
/// <param name="applicationMessage">The message being handled</param>
15+
/// <returns>Returns an opague object that may be used to correlate before- and after route execution. May be null</returns>
16+
Task<object> RouteExecuting(string clientId, MqttApplicationMessage applicationMessage);
17+
18+
/// <summary>
19+
/// Executed after the route handler has been executed.
20+
/// </summary>
21+
/// <param name="o">Set to the the response of <see cref="RouteExecuting(string, MqttApplicationMessage)"/>. May be null.</param>
22+
/// <param name="ex">An exception if the route handler failed. Otherwise null.</param>
23+
/// <returns></returns>
24+
Task RouteExecuted(object o, Exception ex);
25+
}
26+
}

Source/Routing/MqttDefaultJsonOptions.cs

Lines changed: 0 additions & 13 deletions
This file was deleted.

Source/Routing/MqttRouter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ private static Task HandlerInvoker(MethodInfo method, object instance, object?[]
185185
if (param.IsDefined(typeof(FromPayloadAttribute), false))
186186
{
187187
JsonSerializerOptions? defaultOptions =
188-
serviceProvider.GetService<MqttDefaultJsonOptions>()?.SerializerOptions;
188+
serviceProvider.GetService<MqttRoutingOptions>()?.SerializerOptions;
189189
return JsonSerializer.Deserialize(controllerContext.MqttContext.ApplicationMessage.Payload,
190190
param.ParameterType,
191191
defaultOptions
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using MQTTnet.AspNetCore.Routing.Routing;
2+
using System;
3+
using System.Reflection;
4+
using System.Text.Json;
5+
6+
namespace MQTTnet.AspNetCore.Routing;
7+
8+
public class MqttRoutingOptions
9+
10+
{
11+
public JsonSerializerOptions SerializerOptions { get;internal set; }
12+
public Assembly[] FromAssemblies { get; internal set; }
13+
public Type RouteInvocationInterceptor { get; internal set; }
14+
15+
16+
}

0 commit comments

Comments
 (0)