Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISSUE-1106 Add middleware examples #1137

Merged
merged 4 commits into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions JustSaying.sln
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JustSaying.Extensions.Depen
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JustSaying.Benchmark", "tests\JustSaying.Benchmark\JustSaying.Benchmark.csproj", "{83B43FC1-1F1C-4838-B67B-CE70D16870AF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JustSaying.Sample.Middleware", "samples\src\JustSaying.Sample.Middleware\JustSaying.Sample.Middleware.csproj", "{38DAC394-0A6E-4BB6-BCFC-8C21D2C64B3A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -133,6 +135,10 @@ Global
{83B43FC1-1F1C-4838-B67B-CE70D16870AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83B43FC1-1F1C-4838-B67B-CE70D16870AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83B43FC1-1F1C-4838-B67B-CE70D16870AF}.Release|Any CPU.Build.0 = Release|Any CPU
{38DAC394-0A6E-4BB6-BCFC-8C21D2C64B3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{38DAC394-0A6E-4BB6-BCFC-8C21D2C64B3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{38DAC394-0A6E-4BB6-BCFC-8C21D2C64B3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38DAC394-0A6E-4BB6-BCFC-8C21D2C64B3A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -155,6 +161,7 @@ Global
{A4D0E032-3AF1-437F-AF6B-4B602B8D4C49} = {77C93C37-DE5B-448F-9A23-6C9D0C8465CA}
{6AF4E086-6784-489C-9AB1-36F637A30094} = {E22A50F2-9952-4483-8AD1-09BE354FB3E4}
{83B43FC1-1F1C-4838-B67B-CE70D16870AF} = {E22A50F2-9952-4483-8AD1-09BE354FB3E4}
{38DAC394-0A6E-4BB6-BCFC-8C21D2C64B3A} = {77C93C37-DE5B-448F-9A23-6C9D0C8465CA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {18FBDF85-C124-4444-9F03-D0D4F2B3A612}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace JustSaying.Sample.Middleware.Exceptions;

public class BusinessException : Exception
{
public string MessageId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Amazon;
using Microsoft.Extensions.Configuration;

namespace JustSaying.Sample.Middleware.Extensions;

internal static class ConfigurationExtensions
{
private const string AWSServiceUrlKey = "AWSServiceUrl";

private const string AWSRegionKey = "AWSRegion";

internal static bool HasAWSServiceUrl(this IConfiguration configuration) => !string.IsNullOrWhiteSpace(configuration[AWSServiceUrlKey]);

internal static Uri GetAWSServiceUri(this IConfiguration configuration) => new(configuration[AWSServiceUrlKey]);

internal static RegionEndpoint GetAWSRegion(this IConfiguration configuration) => RegionEndpoint.GetBySystemName(configuration[AWSRegionKey]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using JustSaying.Messaging.MessageHandling;
using JustSaying.Sample.Middleware.Messages;

namespace JustSaying.Sample.Middleware.Handlers;

/// <summary>
/// A message handler that will always return true.
/// </summary>
public class SampleMessageHandler : IHandlerAsync<SampleMessage>
{
public async Task<bool> Handle(SampleMessage message)
{
await Task.Delay(1000);
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using JustSaying.Messaging.MessageHandling;
using JustSaying.Sample.Middleware.Exceptions;
using JustSaying.Sample.Middleware.Messages;
using Serilog;

namespace JustSaying.Sample.Middleware.Handlers;

/// <summary>
/// A message handler that will randomly throw an exception, mimicking a transient error.
/// </summary>
public class UnreliableMessageHandler : IHandlerAsync<UnreliableMessage>
{
public async Task<bool> Handle(UnreliableMessage message)
{
await Task.Delay(1000);

if (Random.Shared.NextInt64() % 2 == 0)
{
Log.Information("Throwing for message id {MessageId}", message.Id);
throw new BusinessException() { MessageId = message.Id.ToString() };
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\JustSaying.Extensions.DependencyInjection.Microsoft\JustSaying.Extensions.DependencyInjection.Microsoft.csproj" />
<ProjectReference Include="..\..\..\src\JustSaying.Models\JustSaying.Models.csproj" />
<ProjectReference Include="..\..\..\src\JustSaying\JustSaying.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="Polly" Version="7.2.3" />
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
</ItemGroup>

<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
adammorr marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>

</Project>
32 changes: 32 additions & 0 deletions samples/src/JustSaying.Sample.Middleware/JustSayingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using JustSaying.Messaging;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace JustSaying.Sample.Middleware;

/// <summary>
/// A background service responsible for starting the JustSaying message bus and publisher.
/// </summary>
public class JustSayingService : IHostedService
{
private readonly IMessagingBus _bus;
private readonly IMessagePublisher _publisher;
private readonly ILogger<JustSayingService> _logger;

public JustSayingService(IMessagingBus bus, IMessagePublisher publisher, ILogger<JustSayingService> logger)
{
_bus = bus;
_publisher = publisher;
_logger = logger;
}

public async Task StartAsync(CancellationToken cancellationToken)
{
await _bus.StartAsync(cancellationToken);
await _publisher.StartAsync(cancellationToken);

_logger.LogInformation("Message bus and publisher have started successfully");
}

public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using JustSaying.Models;

namespace JustSaying.Sample.Middleware.Messages;

public class SampleMessage : Message
{
public string SampleId { get; set; } = Guid.NewGuid().ToString();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using JustSaying.Models;

namespace JustSaying.Sample.Middleware.Messages;

public class UnreliableMessage : Message
{
public string Name { get; set; } = Guid.NewGuid().ToString();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using JustSaying.Messaging.Middleware;
using Serilog;

namespace JustSaying.Sample.Middleware.Middlewares;

/// <summary>
/// A middleware that will output a log message before and after a message has passed through it.
/// </summary>
public class EchoJustSayingMiddleware : MiddlewareBase<HandleMessageContext, bool>
{
private readonly string _name;

public EchoJustSayingMiddleware(string name)
{
_name = name;
}

protected override async Task<bool> RunInnerAsync(HandleMessageContext context, Func<CancellationToken, Task<bool>> func, CancellationToken stoppingToken)
{
Log.Information("[{MiddlewareName}] Starting {Name} for {MessageType}", nameof(EchoJustSayingMiddleware), _name, context.Message.GetType().Name);

try
{
return await func(stoppingToken);
}
finally
{
Log.Information("[{MiddlewareName}] Ending {Name} for {MessageType}", nameof(EchoJustSayingMiddleware), _name, context.Message.GetType().Name);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using JustSaying.Messaging.Middleware;
using JustSaying.Sample.Middleware.Messages;
using Serilog;

namespace JustSaying.Sample.Middleware.Middlewares;

/// <summary>
/// A middleware that will interrogate each message and output some information.
/// </summary>
public sealed class InterrogateMiddleware : MiddlewareBase<HandleMessageContext, bool>
{
protected override async Task<bool> RunInnerAsync(HandleMessageContext context, Func<CancellationToken, Task<bool>> func, CancellationToken stoppingToken)
{
if (context.Message is SampleMessage simpleMessage)
{
Log.Information("[{MiddlewareName}] Hello SampleMessage! Your SampleId is {SampleId} and Id is {Id}", nameof(InterrogateMiddleware), simpleMessage.SampleId, simpleMessage.Id);
}
else if (context.Message is UnreliableMessage unreliableMessage)
{
Log.Information("[{MiddlewareName}] Hello UnreliableMessage! Hope you work this time....your Name is {Name} and Id is {Id}", nameof(InterrogateMiddleware),unreliableMessage.Name, context.Message.Id);
}

return await func(stoppingToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using JustSaying.Messaging.Middleware;
using JustSaying.Sample.Middleware.Exceptions;
using Polly;
using Serilog;

namespace JustSaying.Sample.Middleware.Middlewares;

/// <summary>
/// A middleware that will wrap the handling of a message in the provided Polly policy.
/// </summary>
public class PollyJustSayingMiddleware : MiddlewareBase<HandleMessageContext, bool>
{
private readonly AsyncPolicy _policy;

public PollyJustSayingMiddleware()
{
_policy = CreateMessageRetryPolicy();
}

protected override async Task<bool> RunInnerAsync(HandleMessageContext context, Func<CancellationToken, Task<bool>> func, CancellationToken stoppingToken)
{
Log.Information("[{MiddlewareName}] Started {MessageType}", nameof(PollyJustSayingMiddleware), context.Message.GetType().Name);

try
{
return await _policy.ExecuteAsync(() => func(stoppingToken));
martincostello marked this conversation as resolved.
Show resolved Hide resolved
}
finally
{
Log.Information("[{MiddlewareName}] Finished {MessageType}", nameof(PollyJustSayingMiddleware), context.Message.GetType().Name);
}
}

private static AsyncPolicy CreateMessageRetryPolicy()
{
return Policy.Handle<BusinessException>()
.WaitAndRetryAsync(3, count => TimeSpan.FromMilliseconds(Math.Max(count * 100, 1000)),
onRetry: (e, ts, retryCount, ctx) => Log.Information(e, "Retrying failed operation on count {RetryCount}", retryCount));
}
}
Loading