diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3ebd41d3..8f6695aa 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,6 +23,7 @@ jobs:
run: |
dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0
dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release --framework net8.0
+ dotnet test './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj' -c Release --framework net8.0
linux-build-and-run:
name: Run Tests on Linux
@@ -38,6 +39,7 @@ jobs:
run: |
dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0
dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release --framework net8.0
+ dotnet test './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj' -c Release --framework net8.0
- name: Install .NET Aspire workload
run: dotnet workload install aspire
diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln
index 98ae19be..ea1480ff 100644
--- a/WireMock.Net Solution.sln
+++ b/WireMock.Net Solution.sln
@@ -131,10 +131,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Aspire.TestApp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp1.AppHostOriginal", "examples-Aspire\AspireApp1.AppHostOriginal\AspireApp1.AppHostOriginal.csproj", "{C9210DA3-F390-4598-8512-349A473FE9C9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.TUnit", "src\WireMock.Net.TUnit\WireMock.Net.TUnit.csproj", "{91024A93-848F-4A02-AF53-5EBE5834E23C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.TUnit", "src\WireMock.Net.TUnit\WireMock.Net.TUnit.csproj", "{91024A93-848F-4A02-AF53-5EBE5834E23C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.TUnitTests", "test\WireMock.Net.TUnitTests\WireMock.Net.TUnitTests.csproj", "{4CD237F7-B616-46B8-872F-E49B4BBB3EAE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.WebApplication", "examples\WireMock.Net.WebApplication\WireMock.Net.WebApplication.csproj", "{E72ADFAB-4B42-439E-B1EE-C06E504B35D2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.AspNetCore.Middleware", "src\WireMock.Net.AspNetCore.Middleware\WireMock.Net.AspNetCore.Middleware.csproj", "{B6269AAC-170A-4346-8B9A-579DED3D9A13}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.TestWebApplication", "test\WireMock.Net.TestWebApplication\WireMock.Net.TestWebApplication.csproj", "{6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Middleware.Tests", "test\WireMock.Net.Middleware.Tests\WireMock.Net.Middleware.Tests.csproj", "{A5FEF4F7-7DA2-4962-89A8-16BA942886E5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -329,6 +337,22 @@ Global
{4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CD237F7-B616-46B8-872F-E49B4BBB3EAE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E72ADFAB-4B42-439E-B1EE-C06E504B35D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B6269AAC-170A-4346-8B9A-579DED3D9A13}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5FEF4F7-7DA2-4962-89A8-16BA942886E5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -383,6 +407,10 @@ Global
{C9210DA3-F390-4598-8512-349A473FE9C9} = {AD474543-0715-49F2-A284-936B060BF736}
{91024A93-848F-4A02-AF53-5EBE5834E23C} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{4CD237F7-B616-46B8-872F-E49B4BBB3EAE} = {0BB8B634-407A-4610-A91F-11586990767A}
+ {E72ADFAB-4B42-439E-B1EE-C06E504B35D2} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
+ {B6269AAC-170A-4346-8B9A-579DED3D9A13} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
+ {6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE} = {0BB8B634-407A-4610-A91F-11586990767A}
+ {A5FEF4F7-7DA2-4962-89A8-16BA942886E5} = {0BB8B634-407A-4610-A91F-11586990767A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml
index 3b2523fc..498f6bba 100644
--- a/azure-pipelines-ci.yml
+++ b/azure-pipelines-ci.yml
@@ -54,6 +54,7 @@ jobs:
script: |
dotnet-coverage collect "dotnet test ./test/WireMock.Net.Tests/WireMock.Net.Tests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-xunit.xml"
dotnet-coverage collect "dotnet test ./test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-tunit.xml"
+ dotnet-coverage collect "dotnet test ./test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-middleware.xml"
displayName: 'Execute WireMock.Net.Tests with Coverage'
- task: CmdLine@2
diff --git a/examples/WireMock.Net.WebApplication/Program.cs b/examples/WireMock.Net.WebApplication/Program.cs
new file mode 100644
index 00000000..3284d3cc
--- /dev/null
+++ b/examples/WireMock.Net.WebApplication/Program.cs
@@ -0,0 +1,56 @@
+using WireMock.Net.AspNetCore.Middleware;
+using WireMock.RequestBuilders;
+using WireMock.ResponseBuilders;
+
+var builder = WebApplication.CreateBuilder(args);
+
+if (!builder.Environment.IsProduction())
+{
+ builder.Services.AddWireMockService(server =>
+ {
+ server.Given(Request.Create()
+ .WithPath("/test1")
+ .UsingAnyMethod()
+ ).RespondWith(Response.Create()
+ .WithBody("1 : WireMock.Net !")
+ );
+
+ server.Given(Request.Create()
+ .WithPath("/test2")
+ .UsingAnyMethod()
+ ).RespondWith(Response.Create()
+ .WithBody("2 : WireMock.Net !")
+ );
+ }, true);
+}
+
+var app = builder.Build();
+
+app.MapGet("/weatherforecast", async (HttpClient client) =>
+{
+ var result = await client.GetStringAsync("https://real-api:12345/test1");
+
+ return Enumerable.Range(1, 3).Select(index =>
+ new WeatherForecast
+ (
+ DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
+ Random.Shared.Next(-20, 55),
+ result
+ ));
+});
+
+app.MapGet("/weatherforecast2", async (IHttpClientFactory factory) =>
+{
+ using var client = factory.CreateClient();
+ var result = await client.GetStringAsync("https://real-api:12345/test2");
+
+ return Enumerable.Range(1, 3).Select(index =>
+ new WeatherForecast
+ (
+ DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
+ Random.Shared.Next(-20, 55),
+ result
+ ));
+});
+
+await app.RunAsync();
\ No newline at end of file
diff --git a/examples/WireMock.Net.WebApplication/Properties/launchSettings.json b/examples/WireMock.Net.WebApplication/Properties/launchSettings.json
new file mode 100644
index 00000000..2dd72c1b
--- /dev/null
+++ b/examples/WireMock.Net.WebApplication/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:57375",
+ "sslPort": 44333
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "weatherforecast",
+ "applicationUrl": "http://localhost:5112",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "weatherforecast",
+ "applicationUrl": "https://localhost:7021;http://localhost:5112",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "weatherforecast",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/examples/WireMock.Net.WebApplication/WeatherForecast.cs b/examples/WireMock.Net.WebApplication/WeatherForecast.cs
new file mode 100644
index 00000000..9a9044a3
--- /dev/null
+++ b/examples/WireMock.Net.WebApplication/WeatherForecast.cs
@@ -0,0 +1,4 @@
+record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
+{
+ public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+}
\ No newline at end of file
diff --git a/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.csproj b/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.csproj
new file mode 100644
index 00000000..a2ac2064
--- /dev/null
+++ b/examples/WireMock.Net.WebApplication/WireMock.Net.WebApplication.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/examples/WireMock.Net.WebApplication/appsettings.Development.json b/examples/WireMock.Net.WebApplication/appsettings.Development.json
new file mode 100644
index 00000000..a783479b
--- /dev/null
+++ b/examples/WireMock.Net.WebApplication/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMock.Net.WebApplication/appsettings.json b/examples/WireMock.Net.WebApplication/appsettings.json
new file mode 100644
index 00000000..7cb1bd58
--- /dev/null
+++ b/examples/WireMock.Net.WebApplication/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs b/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs
new file mode 100644
index 00000000..954dc8b4
--- /dev/null
+++ b/src/WireMock.Net.AspNetCore.Middleware/AppConstants.cs
@@ -0,0 +1,9 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.AspNetCore.Middleware;
+
+internal static class AppConstants
+{
+ internal const string HEADER_REDIRECT = "X-WireMock-Redirect";
+ internal const string HEADER_RESPONSE_DELAY = "X-WireMock-Response-Delay";
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs
new file mode 100644
index 00000000..546cab2a
--- /dev/null
+++ b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandler.cs
@@ -0,0 +1,72 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Stef.Validation;
+using WireMock.Server;
+
+namespace WireMock.Net.AspNetCore.Middleware.HttpDelegatingHandler;
+
+///
+/// DelegatingHandler that takes requests made via the
+/// and routes them to the .
+///
+internal class WireMockDelegationHandler : DelegatingHandler
+{
+ private readonly ILogger _logger;
+ private readonly WireMockServerInstance _server;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly WireMockDelegationHandlerSettings _settings;
+
+ ///
+ /// Creates a new instance of
+ ///
+ public WireMockDelegationHandler(
+ ILogger logger,
+ WireMockServerInstance server,
+ IHttpContextAccessor httpContextAccessor,
+ WireMockDelegationHandlerSettings settings
+ )
+ {
+ _server = Guard.NotNull(server);
+ _httpContextAccessor = Guard.NotNull(httpContextAccessor);
+ _logger = Guard.NotNull(logger);
+ _settings = Guard.NotNull(settings);
+ }
+
+ ///
+ protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ Guard.NotNull(request);
+ Guard.NotNull(_httpContextAccessor.HttpContext);
+
+ if (_settings.AlwaysRedirect || IsWireMockRedirectHeaderSetToTrue())
+ {
+ _logger.LogDebug("Redirecting request to WireMock server");
+ if (_server.Instance?.Url != null)
+ {
+ request.RequestUri = new Uri(_server.Instance.Url + request.RequestUri!.PathAndQuery);
+ }
+ }
+
+ if (TryGetDelayHeaderValue(out var delayInMs))
+ {
+ await Task.Delay(delayInMs, cancellationToken);
+ }
+
+ return await base.SendAsync(request, cancellationToken);
+ }
+
+ private bool IsWireMockRedirectHeaderSetToTrue()
+ {
+ return
+ _httpContextAccessor.HttpContext!.Request.Headers.TryGetValue(AppConstants.HEADER_REDIRECT, out var values) &&
+ bool.TryParse(values.ToString(), out var shouldRedirectToWireMock) && shouldRedirectToWireMock;
+ }
+
+ private bool TryGetDelayHeaderValue(out int delayInMs)
+ {
+ delayInMs = 0;
+ return
+ _httpContextAccessor.HttpContext!.Request.Headers.TryGetValue(AppConstants.HEADER_RESPONSE_DELAY, out var values) &&
+ int.TryParse(values.ToString(), out delayInMs);
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandlerSettings.cs b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandlerSettings.cs
new file mode 100644
index 00000000..07c9ba70
--- /dev/null
+++ b/src/WireMock.Net.AspNetCore.Middleware/HttpDelegatingHandler/WireMockDelegationHandlerSettings.cs
@@ -0,0 +1,8 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.AspNetCore.Middleware.HttpDelegatingHandler;
+
+internal class WireMockDelegationHandlerSettings
+{
+ public bool AlwaysRedirect { get; set; }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs b/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs
new file mode 100644
index 00000000..8a933d03
--- /dev/null
+++ b/src/WireMock.Net.AspNetCore.Middleware/ServiceCollectionExtensions.cs
@@ -0,0 +1,52 @@
+// Copyright © WireMock.Net
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Http;
+using Stef.Validation;
+using WireMock.Net.AspNetCore.Middleware.HttpDelegatingHandler;
+using WireMock.Server;
+using WireMock.Settings;
+
+namespace WireMock.Net.AspNetCore.Middleware;
+
+///
+/// Extension methods for .
+///
+public static class ServiceCollectionExtensions
+{
+ ///
+ /// Adds all the components necessary to run WireMock.Net as a background service.
+ ///
+ public static IServiceCollection AddWireMockService(
+ this IServiceCollection services,
+ Action configure,
+ bool alwaysRedirectToWireMock = true,
+ WireMockServerSettings? settings = null
+ )
+ {
+ Guard.NotNull(services);
+ Guard.NotNull(configure);
+
+ services.AddTransient();
+
+ services.AddSingleton(new WireMockServerInstance(configure, settings));
+
+ services.AddSingleton(new WireMockDelegationHandlerSettings
+ {
+ AlwaysRedirect = alwaysRedirectToWireMock
+ });
+
+ services.AddHostedService();
+ services.AddHttpClient();
+ services.AddHttpContextAccessor();
+ services.ConfigureAll(options =>
+ {
+ options.HttpMessageHandlerBuilderActions.Add(builder =>
+ {
+ builder.AdditionalHandlers.Add(builder.Services.GetRequiredService());
+ });
+ });
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.AspNetCore.Middleware/WireMock.Net.AspNetCore.Middleware.csproj b/src/WireMock.Net.AspNetCore.Middleware/WireMock.Net.AspNetCore.Middleware.csproj
new file mode 100644
index 00000000..0025768c
--- /dev/null
+++ b/src/WireMock.Net.AspNetCore.Middleware/WireMock.Net.AspNetCore.Middleware.csproj
@@ -0,0 +1,49 @@
+
+
+
+ enable
+ Middleware which can be used to host WireMock.Net as a AspNetCore Middleware in a WebApplication
+ WireMock.Net.AspNetCore.Middleware
+ Matt Yost;Stef Heyenrath
+ net8.0
+ true
+ WireMock.Net.AspNetCore.Middleware
+ WireMock.Net.AspNetCore.Middleware
+ dotnet;middleware;wiremock;service;webapplication
+ {B6269AAC-170A-4346-8B9A-579DED3D9A13}
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+ true
+ true
+ true
+ ../WireMock.Net/WireMock.Net.ruleset
+ true
+ ../WireMock.Net/WireMock.Net.snk
+ true
+ MIT
+ WireMock.Net-LogoAspire.png
+ ../../resources/WireMock.Net-LogoAspire.ico
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
diff --git a/src/WireMock.Net.AspNetCore.Middleware/WireMockBackgroundService.cs b/src/WireMock.Net.AspNetCore.Middleware/WireMockBackgroundService.cs
new file mode 100644
index 00000000..22931968
--- /dev/null
+++ b/src/WireMock.Net.AspNetCore.Middleware/WireMockBackgroundService.cs
@@ -0,0 +1,39 @@
+// Copyright © WireMock.Net
+
+using Microsoft.Extensions.Hosting;
+using Stef.Validation;
+using WireMock.Server;
+
+namespace WireMock.Net.AspNetCore.Middleware;
+
+///
+/// A used to start/stop the
+///
+internal class WireMockBackgroundService : BackgroundService
+{
+ private readonly WireMockServerInstance _serverInstance;
+
+ ///
+ /// Creates a new using an instance
+ /// of
+ ///
+ ///
+ public WireMockBackgroundService(WireMockServerInstance serverInstance)
+ {
+ _serverInstance = Guard.NotNull(serverInstance);
+ }
+
+ ///
+ protected override Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _serverInstance.Start();
+ return Task.CompletedTask;
+ }
+
+ ///
+ public override Task StopAsync(CancellationToken cancellationToken)
+ {
+ _serverInstance.Stop();
+ return base.StopAsync(cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs b/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs
new file mode 100644
index 00000000..c3cc91f6
--- /dev/null
+++ b/src/WireMock.Net.AspNetCore.Middleware/WireMockServerInstance.cs
@@ -0,0 +1,49 @@
+// Copyright © WireMock.Net
+
+using Stef.Validation;
+using WireMock.Server;
+using WireMock.Settings;
+
+namespace WireMock.Net.AspNetCore.Middleware;
+
+///
+/// WireMockServer Instance object
+///
+internal class WireMockServerInstance
+{
+ private readonly Action _configureAction;
+ private readonly WireMockServerSettings? _settings;
+
+ ///
+ /// Creates a new instance and provides ability to add configuration
+ /// for the start method of
+ ///
+ public WireMockServerInstance(Action configure, WireMockServerSettings? settings = null)
+ {
+ _configureAction = Guard.NotNull(configure);
+ _settings = settings;
+ }
+
+ ///
+ /// Instance accessor for the
+ ///
+ public WireMockServer? Instance { get; private set; }
+
+ ///
+ /// Configures and starts instance for use.
+ ///
+ public void Start()
+ {
+ Instance = _settings != null ? WireMockServer.Start(_settings) : WireMockServer.Start();
+
+ _configureAction.Invoke(Instance);
+ }
+
+ ///
+ /// Stops the
+ ///
+ public void Stop()
+ {
+ Instance?.Stop();
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Middleware.Tests/CustomWebApplicationFactory.cs b/test/WireMock.Net.Middleware.Tests/CustomWebApplicationFactory.cs
new file mode 100644
index 00000000..00ebcd7a
--- /dev/null
+++ b/test/WireMock.Net.Middleware.Tests/CustomWebApplicationFactory.cs
@@ -0,0 +1,25 @@
+// Copyright © WireMock.Net
+
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc.Testing;
+
+namespace WireMock.Net.Middleware.Tests;
+
+internal class CustomWebApplicationFactory : WebApplicationFactory
+ where TEntryPoint : class
+{
+ private readonly List<(string Key, string Value)> _settings = new();
+
+ public CustomWebApplicationFactory(bool alwaysRedirectToWireMock = true)
+ {
+ _settings.Add(("AlwaysRedirectToWireMock", alwaysRedirectToWireMock.ToString().ToLowerInvariant()));
+ }
+
+ protected override void ConfigureWebHost(IWebHostBuilder builder)
+ {
+ foreach (var arg in _settings)
+ {
+ builder.UseSetting(arg.Key, arg.Value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs b/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs
new file mode 100644
index 00000000..00d420e0
--- /dev/null
+++ b/test/WireMock.Net.Middleware.Tests/IntegrationTests.cs
@@ -0,0 +1,49 @@
+// Copyright © WireMock.Net
+
+using FluentAssertions;
+using WireMock.Net.TestWebApplication;
+
+namespace WireMock.Net.Middleware.Tests;
+
+public class IntegrationTests
+{
+ [Theory]
+ [InlineData("/real1", "Hello 1 from WireMock.Net !")]
+ [InlineData("/real2", "Hello 2 from WireMock.Net !")]
+ public async Task CallingRealApi_WithAlwaysRedirectToWireMockIsTrue(string requestUri, string expectedResponse)
+ {
+ // Arrange
+ await using var factory = new CustomWebApplicationFactory();
+ using var client = factory.CreateClient();
+
+ // Act
+ var response = await client.GetAsync(requestUri);
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ stringResponse.Should().Be(expectedResponse);
+ }
+
+ [Theory]
+ [InlineData("/real1", "Hello 1 from WireMock.Net !")]
+ [InlineData("/real2", "Hello 2 from WireMock.Net !")]
+ public async Task CallingRealApi_WithAlwaysRedirectToWireMockIsFalse(string requestUri, string expectedResponse)
+ {
+ // Arrange
+ await using var factory = new CustomWebApplicationFactory(false);
+ using var client = factory.CreateClient();
+
+ var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
+ request.Headers.Add("X-WireMock-Redirect", "true");
+ request.Headers.Add("X-WireMock-Response-Delay", "10");
+
+ // Act
+ var response = await client.SendAsync(request);
+
+ // Assert
+ response.EnsureSuccessStatusCode();
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ stringResponse.Should().Be(expectedResponse);
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj b/test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj
new file mode 100644
index 00000000..fec8bdf6
--- /dev/null
+++ b/test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj
@@ -0,0 +1,43 @@
+
+
+
+ net8.0
+ enable
+ enable
+ false
+ true
+ true
+ ../../src/WireMock.Net/WireMock.Net.snk
+ true
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/WireMock.Net.TestWebApplication/Program.cs b/test/WireMock.Net.TestWebApplication/Program.cs
new file mode 100644
index 00000000..63320a13
--- /dev/null
+++ b/test/WireMock.Net.TestWebApplication/Program.cs
@@ -0,0 +1,49 @@
+using WireMock.Net.AspNetCore.Middleware;
+using WireMock.RequestBuilders;
+using WireMock.ResponseBuilders;
+
+namespace WireMock.Net.TestWebApplication;
+
+// Make the implicit Program class public so test projects can access it.
+public class Program
+{
+ public static async Task Main(string[] args)
+ {
+ var alwaysRedirectToWireMock = args.Contains("--AlwaysRedirectToWireMock=true");
+
+ var builder = WebApplication.CreateBuilder(args);
+
+ builder.Services.AddWireMockService(server =>
+ {
+ server.Given(Request.Create()
+ .WithPath("/test1")
+ .UsingAnyMethod()
+ ).RespondWith(Response.Create()
+ .WithBody("Hello 1 from WireMock.Net !")
+ );
+
+ server.Given(Request.Create()
+ .WithPath("/test2")
+ .UsingAnyMethod()
+ ).RespondWith(Response.Create()
+ .WithBody("Hello 2 from WireMock.Net !")
+ );
+ }, alwaysRedirectToWireMock);
+
+ var app = builder.Build();
+
+ app.MapGet("/real1", async (HttpClient client) =>
+ {
+ var result = await client.GetStringAsync("https://real-api:12345/test1");
+ return result;
+ });
+
+ app.MapGet("/real2", async (IHttpClientFactory factory) =>
+ {
+ using var client = factory.CreateClient();
+ return await client.GetStringAsync("https://real-api:12345/test2");
+ });
+
+ await app.RunAsync();
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.TestWebApplication/Properties/launchSettings.json b/test/WireMock.Net.TestWebApplication/Properties/launchSettings.json
new file mode 100644
index 00000000..a79dad85
--- /dev/null
+++ b/test/WireMock.Net.TestWebApplication/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "WireMock.Net.TestWebApplication": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:57712;http://localhost:57713"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.TestWebApplication/WireMock.Net.TestWebApplication.csproj b/test/WireMock.Net.TestWebApplication/WireMock.Net.TestWebApplication.csproj
new file mode 100644
index 00000000..acfdf66c
--- /dev/null
+++ b/test/WireMock.Net.TestWebApplication/WireMock.Net.TestWebApplication.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/test/WireMock.Net.TestWebApplication/appsettings.json b/test/WireMock.Net.TestWebApplication/appsettings.json
new file mode 100644
index 00000000..7cb1bd58
--- /dev/null
+++ b/test/WireMock.Net.TestWebApplication/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
\ No newline at end of file