diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln
index 86b5368b..2fd76f42 100644
--- a/WireMock.Net Solution.sln
+++ b/WireMock.Net Solution.sln
@@ -138,6 +138,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Tests.UsingNuG
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.GraphQL", "src\WireMock.Net.GraphQL\WireMock.Net.GraphQL.csproj", "{B6269AAC-170A-4346-8B9A-444DED3D9A45}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Extensions.Routing.Tests", "test\WireMock.Net.Extensions.Routing.Tests\WireMock.Net.Extensions.Routing.Tests.csproj", "{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Extensions.Routing", "src\WireMock.Net.Extensions.Routing\WireMock.Net.Extensions.Routing.csproj", "{1E874C8F-08A2-493B-8421-619F9A6E9E77}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -332,6 +336,14 @@ Global
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -386,6 +398,8 @@ Global
{1F80A6E6-D146-4E40-9EA8-49DB8494239F} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{BBA332C6-28A9-42E7-9C4D-A0816E52A198} = {0BB8B634-407A-4610-A91F-11586990767A}
{B6269AAC-170A-4346-8B9A-444DED3D9A45} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
+ {3FCBCA9C-9DB0-4A96-B47E-30470764CC9C} = {0BB8B634-407A-4610-A91F-11586990767A}
+ {1E874C8F-08A2-493B-8421-619F9A6E9E77} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
diff --git a/src/WireMock.Net.Extensions.Routing/Delegates/WireMockHttpRequestHandler.cs b/src/WireMock.Net.Extensions.Routing/Delegates/WireMockHttpRequestHandler.cs
new file mode 100644
index 00000000..d094aac8
--- /dev/null
+++ b/src/WireMock.Net.Extensions.Routing/Delegates/WireMockHttpRequestHandler.cs
@@ -0,0 +1,10 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.Extensions.Routing.Delegates;
+
+///
+/// Represents a handler for processing WireMock.Net HTTP requests and returning a response asynchronously.
+///
+/// The incoming request message.
+/// A task that resolves to a .
+public delegate Task WireMockHttpRequestHandler(IRequestMessage requestMessage);
diff --git a/src/WireMock.Net.Extensions.Routing/Delegates/WireMockMiddleware.cs b/src/WireMock.Net.Extensions.Routing/Delegates/WireMockMiddleware.cs
new file mode 100644
index 00000000..2ef19d2e
--- /dev/null
+++ b/src/WireMock.Net.Extensions.Routing/Delegates/WireMockMiddleware.cs
@@ -0,0 +1,10 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.Extensions.Routing.Delegates;
+
+///
+/// Represents a middleware component for WireMock.Net HTTP request handling.
+///
+/// The next request handler in the middleware pipeline.
+/// A that processes the request.
+public delegate WireMockHttpRequestHandler WireMockMiddleware(WireMockHttpRequestHandler next);
diff --git a/src/WireMock.Net.Extensions.Routing/Extensions/DictionaryExtensions.cs b/src/WireMock.Net.Extensions.Routing/Extensions/DictionaryExtensions.cs
new file mode 100644
index 00000000..b3db898a
--- /dev/null
+++ b/src/WireMock.Net.Extensions.Routing/Extensions/DictionaryExtensions.cs
@@ -0,0 +1,19 @@
+// Copyright © WireMock.Net
+
+using System.Collections.Immutable;
+
+namespace WireMock.Net.Extensions.Routing.Extensions;
+
+internal static class DictionaryExtensions
+{
+ public static IDictionary AddIf(
+ this IDictionary source,
+ bool condition,
+ TKey key,
+ TValue value,
+ IEqualityComparer? keyComparer = null)
+ where TKey : notnull =>
+ condition
+ ? source.ToImmutableDictionary(keyComparer).Add(key, value)
+ : source;
+}
diff --git a/src/WireMock.Net.Extensions.Routing/Extensions/HttpResponseExtensions.cs b/src/WireMock.Net.Extensions.Routing/Extensions/HttpResponseExtensions.cs
new file mode 100644
index 00000000..011b1b7e
--- /dev/null
+++ b/src/WireMock.Net.Extensions.Routing/Extensions/HttpResponseExtensions.cs
@@ -0,0 +1,34 @@
+// Copyright © WireMock.Net
+
+using Microsoft.AspNetCore.Http;
+using WireMock.Types;
+using WireMock.Util;
+
+namespace WireMock.Net.Extensions.Routing.Extensions;
+
+internal static class HttpResponseExtensions
+{
+ public static async Task ToResponseMessageAsync(
+ this HttpResponse response)
+ {
+ var headers = response.Headers.ToDictionary(
+ header => header.Key, header => new WireMockList(header.Value.ToArray()));
+ return new()
+ {
+ Headers = headers!,
+ BodyData = new BodyData
+ {
+ DetectedBodyType = BodyType.String,
+ BodyAsString = await response.ReadBodyAsStringAsync(),
+ },
+ StatusCode = response.StatusCode,
+ };
+ }
+
+ public static async Task ReadBodyAsStringAsync(this HttpResponse response)
+ {
+ response.Body.Seek(0, SeekOrigin.Begin);
+ using var reader = new StreamReader(response.Body);
+ return await reader.ReadToEndAsync();
+ }
+}
diff --git a/src/WireMock.Net.Extensions.Routing/Extensions/RequestMessageExtensions.cs b/src/WireMock.Net.Extensions.Routing/Extensions/RequestMessageExtensions.cs
new file mode 100644
index 00000000..f0f13d84
--- /dev/null
+++ b/src/WireMock.Net.Extensions.Routing/Extensions/RequestMessageExtensions.cs
@@ -0,0 +1,16 @@
+// Copyright © WireMock.Net
+
+using JsonConverter.Abstractions;
+
+namespace WireMock.Net.Extensions.Routing.Extensions;
+
+internal static class RequestMessageExtensions
+{
+ public static T? GetBodyAsJson(
+ this IRequestMessage requestMessage,
+ IJsonConverter jsonConverter,
+ JsonConverterOptions? jsonOptions = null) =>
+ requestMessage.Body is not null
+ ? jsonConverter.Deserialize(requestMessage.Body, jsonOptions)
+ : default;
+}
diff --git a/src/WireMock.Net.Extensions.Routing/Extensions/StringExtensions.cs b/src/WireMock.Net.Extensions.Routing/Extensions/StringExtensions.cs
new file mode 100644
index 00000000..d55adf19
--- /dev/null
+++ b/src/WireMock.Net.Extensions.Routing/Extensions/StringExtensions.cs
@@ -0,0 +1,9 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.Extensions.Routing.Extensions;
+
+internal static class StringExtensions
+{
+ public static string ToMatchFullStringRegex(this string regex) =>
+ $"^{regex}$";
+}
diff --git a/src/WireMock.Net.Extensions.Routing/Extensions/TaskExtensions.cs b/src/WireMock.Net.Extensions.Routing/Extensions/TaskExtensions.cs
new file mode 100644
index 00000000..308fef3b
--- /dev/null
+++ b/src/WireMock.Net.Extensions.Routing/Extensions/TaskExtensions.cs
@@ -0,0 +1,39 @@
+// Copyright © WireMock.Net
+
+using System.Reflection;
+
+namespace WireMock.Net.Extensions.Routing.Extensions;
+
+internal static class TaskExtensions
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Usage",
+ "VSTHRD003:Avoid awaiting foreign Tasks",
+ Justification = "Await is required here to transform base task to generic one.")]
+ public static async Task