diff --git a/Directory.Build.props b/Directory.Build.props
index 2b63653b..9937770d 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,7 +4,7 @@
- 1.0.18
+ 1.0.19
diff --git a/GitHubReleaseNotes.txt b/GitHubReleaseNotes.txt
index 860f0b5a..5dd7de2e 100644
--- a/GitHubReleaseNotes.txt
+++ b/GitHubReleaseNotes.txt
@@ -1,3 +1,3 @@
https://github.com/StefH/GitHubReleaseNotes
-GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.18.0
\ No newline at end of file
+GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.19.0
\ No newline at end of file
diff --git a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
index 4c3ef47b..cb3d0a39 100644
--- a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
+++ b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
@@ -438,6 +438,15 @@ namespace WireMock.Net.ConsoleApplication
.WithBody("ok")
);
+ server.Given(Request.Create()
+ .WithPath("/services/query/")
+ .WithParam("q", "SELECT Id from User where username='user@gmail.com'")
+ .UsingGet())
+ .RespondWith(Response.Create()
+ .WithStatusCode(200)
+ .WithHeader("Content-Type", "application/json")
+ .WithBodyAsJson(new { Id = "5bdf076c-5654-4b3e-842c-7caf1fabf8c9" }));
+
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
diff --git a/src/WireMock.Net/RequestBuilders/Request.cs b/src/WireMock.Net/RequestBuilders/Request.cs
index 054f8d2d..3fa1b9f9 100644
--- a/src/WireMock.Net/RequestBuilders/Request.cs
+++ b/src/WireMock.Net/RequestBuilders/Request.cs
@@ -4,7 +4,6 @@ using System.Collections.ObjectModel;
using System.Linq;
using WireMock.Matchers;
using WireMock.Matchers.Request;
-using WireMock.Util;
using WireMock.Validation;
namespace WireMock.RequestBuilders
diff --git a/src/WireMock.Net/RequestMessage.cs b/src/WireMock.Net/RequestMessage.cs
index 46583b43..f89da6a5 100644
--- a/src/WireMock.Net/RequestMessage.cs
+++ b/src/WireMock.Net/RequestMessage.cs
@@ -171,40 +171,7 @@ namespace WireMock
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList(header.Value));
Cookies = cookies;
RawQuery = WebUtility.UrlDecode(urlDetails.Url.Query);
- Query = ParseQuery(RawQuery);
- }
-
- private static IDictionary> ParseQuery(string queryString)
- {
- if (string.IsNullOrEmpty(queryString))
- {
- return null;
- }
-
- if (queryString.StartsWith("?"))
- {
- queryString = queryString.Substring(1);
- }
-
- return queryString.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries)
- .Aggregate(new Dictionary>(),
- (dict, term) =>
- {
- string[] parts = term.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
- string key = parts[0];
- if (!dict.ContainsKey(key))
- {
- dict.Add(key, new WireMockList());
- }
-
- if (parts.Length == 2)
- {
- string[] values = parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
- dict[key].AddRange(values);
- }
-
- return dict;
- });
+ Query = QueryStringParser.Parse(RawQuery);
}
///
diff --git a/src/WireMock.Net/Util/QueryStringParser.cs b/src/WireMock.Net/Util/QueryStringParser.cs
new file mode 100644
index 00000000..a512a1b1
--- /dev/null
+++ b/src/WireMock.Net/Util/QueryStringParser.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace WireMock.Util
+{
+ ///
+ /// Based on https://stackoverflow.com/questions/659887/get-url-parameters-from-a-string-in-net
+ ///
+ internal static class QueryStringParser
+ {
+ public static IDictionary> Parse(string queryString)
+ {
+ if (string.IsNullOrEmpty(queryString))
+ {
+ return new Dictionary>();
+ }
+
+ string[] JoinParts(string[] parts)
+ {
+ if (parts.Length > 2)
+ {
+ return new[] { string.Join("=", parts, 1, parts.Length - 1) };
+ }
+
+ if (parts.Length > 1)
+ {
+ return parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); // support "?key=1,2"
+ }
+
+ return new string[0];
+ }
+
+ return queryString.TrimStart('?')
+ .Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries) // Support "?key=value;key=anotherValue" and "?key=value&key=anotherValue"
+ .Select(parameter => parameter.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries))
+ .GroupBy(parts => parts[0], JoinParts)
+ .ToDictionary(grouping => grouping.Key, grouping => new WireMockList(grouping.SelectMany(x => x)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Util/QueryStringParserTests.cs b/test/WireMock.Net.Tests/Util/QueryStringParserTests.cs
new file mode 100644
index 00000000..799c2940
--- /dev/null
+++ b/test/WireMock.Net.Tests/Util/QueryStringParserTests.cs
@@ -0,0 +1,210 @@
+using FluentAssertions;
+using System.Collections.Generic;
+using WireMock.Util;
+using Xunit;
+
+namespace WireMock.Net.Tests.Util
+{
+ public class QueryStringParserTests
+ {
+ [Fact]
+ public void Parse_WithNullString()
+ {
+ // Assign
+ string query = null;
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Should().Equal(new Dictionary>());
+ }
+
+ [Fact]
+ public void Parse_WithEmptyString()
+ {
+ // Assign
+ string query = "";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Should().Equal(new Dictionary>());
+ }
+
+ [Fact]
+ public void Parse_WithQuestionMark()
+ {
+ // Assign
+ string query = "?";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Should().Equal(new Dictionary>());
+ }
+
+ [Fact]
+ public void Parse_With1Param()
+ {
+ // Assign
+ string query = "?key=bla/blub.xml";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(1);
+ result["key"].Should().Equal(new WireMockList("bla/blub.xml"));
+ }
+
+ [Fact]
+ public void Parse_With2Params()
+ {
+ // Assign
+ string query = "?x=1&y=2";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(2);
+ result["x"].Should().Equal(new WireMockList("1"));
+ result["y"].Should().Equal(new WireMockList("2"));
+ }
+
+ [Fact]
+ public void Parse_With1ParamNoValue()
+ {
+ // Assign
+ string query = "?empty";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(1);
+ result["empty"].Should().Equal(new WireMockList());
+ }
+
+ [Fact]
+ public void Parse_With1ParamNoValueWithEqualSign()
+ {
+ // Assign
+ string query = "?empty=";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(1);
+ result["empty"].Should().Equal(new WireMockList());
+ }
+
+ [Fact]
+ public void Parse_With1ParamAndJustAndSign()
+ {
+ // Assign
+ string query = "?key=1&";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(1);
+ result["key"].Should().Equal(new WireMockList("1"));
+ }
+
+ [Fact]
+ public void Parse_With2ParamsAndWhereOneHasAQuestion()
+ {
+ // Assign
+ string query = "?key=value?&b=c";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(2);
+ result["key"].Should().Equal(new WireMockList("value?"));
+ result["b"].Should().Equal(new WireMockList("c"));
+ }
+
+ [Fact]
+ public void Parse_With1ParamWithEqualSign()
+ {
+ // Assign
+ string query = "?key=value=what";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(1);
+ result["key"].Should().Equal(new WireMockList("value=what"));
+ }
+
+ [Fact]
+ public void Parse_WithMultipleParamWithSameKeySeparatedBySemiColon()
+ {
+ // Assign
+ string query = "?key=value;key=anotherValue";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(1);
+ result["key"].Should().Equal(new WireMockList(new[] { "value", "anotherValue" }));
+ }
+
+ [Fact]
+ public void Parse_With1ParamContainingComma()
+ {
+ // Assign
+ string query = "?key=1,2&key=3";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(1);
+ result["key"].Should().Equal(new WireMockList(new[] { "1", "2", "3" }));
+ }
+
+ [Fact]
+ public void Parse_WithMultipleParamWithSameKey()
+ {
+ // Assign
+ string query = "?key=value&key=anotherValue";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(1);
+ result["key"].Should().Equal(new WireMockList(new[] { "value", "anotherValue" }));
+ }
+
+ [Fact]
+ public void Parse_WithComplex()
+ {
+ // Assign
+ string query = "?q=energy+edge&rls=com.microsoft:en-au&ie=UTF-8&oe=UTF-8&startIndex=&startPage=1%22";
+
+ // Act
+ var result = QueryStringParser.Parse(query);
+
+ // Assert
+ result.Count.Should().Be(6);
+ result["q"].Should().Equal(new WireMockList("energy+edge"));
+ result["rls"].Should().Equal(new WireMockList("com.microsoft:en-au"));
+ result["ie"].Should().Equal(new WireMockList("UTF-8"));
+ result["oe"].Should().Equal(new WireMockList("UTF-8"));
+ result["startIndex"].Should().Equal(new WireMockList());
+ result["startPage"].Should().Equal(new WireMockList("1%22"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
index 6936f5e1..26b90ea4 100644
--- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
+++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
@@ -34,8 +34,8 @@
all
runtime; build; native; contentfiles; analyzers
+
-