diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln
index 6a3bdd94..ed9519f6 100644
--- a/WireMock.Net Solution.sln
+++ b/WireMock.Net Solution.sln
@@ -152,6 +152,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenTelemetry"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenTelemetryDemo", "examples\WireMock.Net.OpenTelemetryDemo\WireMock.Net.OpenTelemetryDemo.csproj", "{9957038D-F9C3-CA5D-E8AE-BE188E512635}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.MimePart", "examples\WireMock.Net.Console.MimePart\WireMock.Net.Console.MimePart.csproj", "{4005E20C-D42B-138A-79BE-B3F5420C563F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -810,6 +812,18 @@ Global
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x64.Build.0 = Release|Any CPU
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.ActiveCfg = Release|Any CPU
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.Build.0 = Release|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x64.Build.0 = Debug|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x86.Build.0 = Debug|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x64.ActiveCfg = Release|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x64.Build.0 = Release|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x86.ActiveCfg = Release|Any CPU
+ {4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -871,6 +885,7 @@ Global
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{9957038D-F9C3-CA5D-E8AE-BE188E512635} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
+ {4005E20C-D42B-138A-79BE-B3F5420C563F} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
diff --git a/examples/WireMock.Net.Console.MimePart/MainApp.cs b/examples/WireMock.Net.Console.MimePart/MainApp.cs
new file mode 100644
index 00000000..0178f2b2
--- /dev/null
+++ b/examples/WireMock.Net.Console.MimePart/MainApp.cs
@@ -0,0 +1,82 @@
+// Copyright © WireMock.Net
+
+using Newtonsoft.Json;
+using WireMock.Logging;
+using WireMock.Matchers;
+using WireMock.RequestBuilders;
+using WireMock.ResponseBuilders;
+using WireMock.Server;
+using WireMock.Settings;
+
+namespace WireMock.Net.Console.MimePart;
+
+// Test this CURL:
+// curl -X POST http://localhost:9091/multipart -F "plainText=This is some plain text;type=text/plain" -F "jsonData={ `"Key`": `"Value`" };type=application/json" -F "image=@image.png;type=image/png"
+//
+// curl -X POST http://localhost:9091/multipart2 -F "plainText=This is some plain text;type=text/plain" -F "jsonData={ `"Key`": `"Value`" };type=application/json" -F "image=@image.png;type=image/png"
+
+public static class MainApp
+{
+ public static async Task RunAsync()
+ {
+ using var server = WireMockServer.Start(new WireMockServerSettings
+ {
+ Port = 9091,
+ StartAdminInterface = true,
+
+ ReadStaticMappings = true,
+ //WatchStaticMappings = true,
+ //WatchStaticMappingsInSubdirectories = true,
+
+ Logger = new WireMockConsoleLogger()
+ });
+ System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
+
+ var textPlainContentTypeMatcher = new ContentTypeMatcher("text/plain");
+ var textPlainContentMatcher = new ExactMatcher("This is some plain text");
+ var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher);
+
+ var textJsonContentTypeMatcher = new ContentTypeMatcher("application/json");
+ var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true);
+ var textJsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher);
+
+ var imagePngContentTypeMatcher = new ContentTypeMatcher("image/png");
+ var imagePngContentDispositionMatcher = new ExactMatcher("form-data; name=\"image\"; filename=\"image.png\"");
+ var imagePngContentTransferEncodingMatcher = new ExactMatcher("default");
+ var imagePngContentMatcher = new ExactObjectMatcher(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"));
+ var imagePngMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, imagePngContentTypeMatcher, imagePngContentDispositionMatcher, imagePngContentTransferEncodingMatcher, imagePngContentMatcher);
+
+ var matchers = new IMatcher[]
+ {
+ textPlainMatcher,
+ textJsonMatcher,
+ imagePngMatcher
+ };
+
+ server
+ .Given(Request.Create()
+ .WithPath("/multipart")
+ .UsingPost()
+ .WithMultiPart(matchers)
+ )
+ .WithGuid("b9c82182-e469-41da-bcaf-b6e3157fefdb")
+ .RespondWith(Response.Create()
+ .WithBody("MultiPart is ok")
+ );
+
+ // server.SaveStaticMappings();
+
+ System.Console.WriteLine(JsonConvert.SerializeObject(server.MappingModels, Formatting.Indented));
+
+ System.Console.WriteLine("Press any key to stop the server");
+ System.Console.ReadKey();
+ server.Stop();
+
+ System.Console.WriteLine("Displaying all requests");
+ var allRequests = server.LogEntries;
+ System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
+
+ System.Console.WriteLine("Press any key to quit");
+ System.Console.ReadKey();
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMock.Net.Console.MimePart/Program.cs b/examples/WireMock.Net.Console.MimePart/Program.cs
new file mode 100644
index 00000000..85d66ba5
--- /dev/null
+++ b/examples/WireMock.Net.Console.MimePart/Program.cs
@@ -0,0 +1,23 @@
+// Copyright © WireMock.Net
+
+using System.Reflection;
+using log4net;
+using log4net.Config;
+using log4net.Repository;
+
+namespace WireMock.Net.Console.MimePart;
+
+static class Program
+{
+ private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
+ private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
+
+ static async Task Main(params string[] args)
+ {
+ Log.Info("Starting WireMock.Net.Console.MimePart...");
+
+ XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
+
+ await MainApp.RunAsync();
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMock.Net.Console.MimePart/WireMock.Net.Console.MimePart.csproj b/examples/WireMock.Net.Console.MimePart/WireMock.Net.Console.MimePart.csproj
new file mode 100644
index 00000000..537b650a
--- /dev/null
+++ b/examples/WireMock.Net.Console.MimePart/WireMock.Net.Console.MimePart.csproj
@@ -0,0 +1,29 @@
+
+
+
+ Exe
+ net8.0
+ $(DefineConstants);GRAPHQL;MIMEKIT;PROTOBUF
+ enable
+ enable
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/examples/WireMock.Net.Console.MimePart/__admin/mappings/b9c82182-e469-41da-bcaf-b6e3157fefdc.json b/examples/WireMock.Net.Console.MimePart/__admin/mappings/b9c82182-e469-41da-bcaf-b6e3157fefdc.json
new file mode 100644
index 00000000..0bc68029
--- /dev/null
+++ b/examples/WireMock.Net.Console.MimePart/__admin/mappings/b9c82182-e469-41da-bcaf-b6e3157fefdc.json
@@ -0,0 +1,79 @@
+{
+ "Guid": "b9c82182-e469-41da-bcaf-b6e3157fefdc",
+ "UpdatedAt": "2025-12-18T17:21:57.3879723Z",
+ "Request": {
+ "Path": {
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "/multipart2",
+ "IgnoreCase": false
+ }
+ ]
+ },
+ "Methods": [
+ "POST"
+ ],
+ "Body": {
+ "MatcherName": "MultiPartMatcher",
+ "Matchers": [
+ {
+ "Name": "MimePartMatcher",
+ "ContentTypeMatcher": {
+ "Name": "ContentTypeMatcher",
+ "Pattern": "text/plain",
+ "IgnoreCase": false
+ },
+ "ContentMatcher": {
+ "Name": "ExactMatcher",
+ "Pattern": "This is some plain text",
+ "IgnoreCase": false
+ }
+ },
+ {
+ "Name": "MimePartMatcher",
+ "ContentTypeMatcher": {
+ "Name": "ContentTypeMatcher",
+ "Pattern": "application/json",
+ "IgnoreCase": false
+ },
+ "ContentMatcher": {
+ "Name": "JsonMatcher",
+ "Pattern": {
+ "Key": "Value"
+ },
+ "IgnoreCase": true,
+ "Regex": false
+ }
+ },
+ {
+ "Name": "MimePartMatcher",
+ "ContentTypeMatcher": {
+ "Name": "ContentTypeMatcher",
+ "Pattern": "image/png",
+ "IgnoreCase": false
+ },
+ "ContentDispositionMatcher": {
+ "Name": "ExactMatcher",
+ "Pattern": "form-data; name=\"image\"; filename=\"image.png\"",
+ "IgnoreCase": false
+ },
+ "ContentTransferEncodingMatcher": {
+ "Name": "ExactMatcher",
+ "Pattern": "default",
+ "IgnoreCase": false
+ },
+ "ContentMatcher": {
+ "Name": "ExactObjectMatcher",
+ "Pattern": "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"
+ }
+ }
+ ],
+ "MatchOperator": "Or"
+ }
+ },
+ "Response": {
+ "BodyDestination": "SameAsSource",
+ "Body": "MultiPart2 is ok"
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMock.Net.Console.MimePart/log4net.config b/examples/WireMock.Net.Console.MimePart/log4net.config
new file mode 100644
index 00000000..feae9952
--- /dev/null
+++ b/examples/WireMock.Net.Console.MimePart/log4net.config
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/WireMock.Net.Console.NET8/MainApp.cs b/examples/WireMock.Net.Console.NET8/MainApp.cs
index fa88484c..fce4d3f7 100644
--- a/examples/WireMock.Net.Console.NET8/MainApp.cs
+++ b/examples/WireMock.Net.Console.NET8/MainApp.cs
@@ -339,7 +339,7 @@ namespace WireMock.Net.ConsoleApplication
}
}
System.Console.WriteLine("X = {0} ; default = {1} ; pX = {2:0.00} ; valueX = {3:0.00}", xCount, defaultCount, pX, 1.0 * xCount / tot);
- return;
+
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
{
HostingScheme = HostingScheme.HttpAndHttps,
@@ -556,39 +556,6 @@ namespace WireMock.Net.ConsoleApplication
);
#endif
-#if MIMEKIT
- var textPlainContentTypeMatcher = new ContentTypeMatcher("text/plain");
- var textPlainContentMatcher = new ExactMatcher("This is some plain text");
- var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher);
-
- var textJsonContentTypeMatcher = new ContentTypeMatcher("text/json");
- var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true);
- var textJsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher);
-
- var imagePngContentTypeMatcher = new ContentTypeMatcher("image/png");
- var imagePngContentDispositionMatcher = new ExactMatcher("attachment; filename=\"image.png\"");
- var imagePngContentTransferEncodingMatcher = new ExactMatcher("base64");
- var imagePngContentMatcher = new ExactObjectMatcher(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"));
- var imagePngMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, imagePngContentTypeMatcher, imagePngContentDispositionMatcher, imagePngContentTransferEncodingMatcher, imagePngContentMatcher);
-
- var matchers = new IMatcher[]
- {
- textPlainMatcher,
- textJsonMatcher,
- imagePngMatcher
- };
-
- server
- .Given(Request.Create()
- .WithPath("/multipart")
- .UsingPost()
- .WithMultiPart(matchers)
- )
- .WithGuid("b9c82182-e469-41da-bcaf-b6e3157fefdb")
- .RespondWith(Response.Create()
- .WithBody("MultiPart is ok")
- );
-#endif
// 400 ms
server
.Given(Request.Create()
diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/BodyModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/BodyModel.cs
index d958bfd9..30b8469e 100644
--- a/src/WireMock.Net.Abstractions/Admin/Mappings/BodyModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Mappings/BodyModel.cs
@@ -8,6 +8,12 @@ namespace WireMock.Admin.Mappings;
[FluentBuilder.AutoGenerateBuilder]
public class BodyModel
{
+ ///
+ /// The name of the body matcher.
+ /// Currently only "MultiPartMatcher" is supported.
+ ///
+ public string? MatcherName { get; set; }
+
///
/// Gets or sets the matcher.
///
diff --git a/src/WireMock.Net.Abstractions/Admin/Requests/LogRequestMatchModel.cs b/src/WireMock.Net.Abstractions/Admin/Requests/LogRequestMatchModel.cs
index 6a379627..4a2451f3 100644
--- a/src/WireMock.Net.Abstractions/Admin/Requests/LogRequestMatchModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Requests/LogRequestMatchModel.cs
@@ -1,6 +1,7 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
+using WireMock.Matchers.Request;
namespace WireMock.Admin.Requests;
@@ -47,5 +48,5 @@ public class LogRequestMatchModel
///
/// The match details.
///
- public IList