mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-17 14:09:40 +02:00
Add FormUrlEncodedMatcher (#1147)
* FormUrlEncodedMatcher * . * Fix * new * support wildcard
This commit is contained in:
153
src/WireMock.Net/Matchers/FormUrlEncodedMatcher.cs
Normal file
153
src/WireMock.Net/Matchers/FormUrlEncodedMatcher.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// FormUrl Encoded fields Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
|
||||
public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = [];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(matchBehaviour, [pattern], ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
IgnoreCase = ignoreCase;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
|
||||
foreach (var pattern in _patterns)
|
||||
{
|
||||
if (QueryStringParser.TryParse(pattern, IgnoreCase, out var nameValueCollection))
|
||||
{
|
||||
foreach (var nameValue in nameValueCollection)
|
||||
{
|
||||
var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator);
|
||||
var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator);
|
||||
_pairs.Add((keyMatcher, valueMatcher));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
// Input is null or empty and if no patterns defined, return Perfect match.
|
||||
if (string.IsNullOrEmpty(input) && _patterns.Length == 0)
|
||||
{
|
||||
return new MatchResult(MatchScores.Perfect);
|
||||
}
|
||||
|
||||
if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection))
|
||||
{
|
||||
return new MatchResult(MatchScores.Mismatch);
|
||||
}
|
||||
|
||||
var matches = new List<bool>();
|
||||
foreach (var inputKeyValuePair in inputNameValueCollection)
|
||||
{
|
||||
var match = false;
|
||||
foreach (var pair in _pairs)
|
||||
{
|
||||
var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect();
|
||||
if (keyMatchResult)
|
||||
{
|
||||
match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false;
|
||||
if (match)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matches.Add(match);
|
||||
}
|
||||
|
||||
var score = MatchScores.ToScore(matches.ToArray(), MatchOperator);
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Name => nameof(FormUrlEncodedMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
}
|
||||
@@ -147,19 +147,22 @@ internal class MappingConverter
|
||||
{
|
||||
var firstMatcher = requestMessageBodyMatcher.Matchers.FirstOrDefault();
|
||||
|
||||
if (firstMatcher is WildcardMatcher wildcardMatcher && wildcardMatcher.GetPatterns().Any())
|
||||
switch (firstMatcher)
|
||||
{
|
||||
sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})");
|
||||
}
|
||||
case IStringMatcher stringMatcher when stringMatcher.GetPatterns().Length > 0:
|
||||
sb.AppendLine($" .WithBody({GetString(stringMatcher)})");
|
||||
break;
|
||||
|
||||
if (firstMatcher is JsonMatcher jsonMatcher)
|
||||
{
|
||||
var matcherType = jsonMatcher.GetType().Name;
|
||||
sb.AppendLine($" .WithBody(new {matcherType}(");
|
||||
sb.AppendLine($" value: {ConvertToAnonymousObjectDefinition(jsonMatcher.Value, 3)},");
|
||||
sb.AppendLine($" ignoreCase: {ToCSharpBooleanLiteral(jsonMatcher.IgnoreCase)},");
|
||||
sb.AppendLine($" regex: {ToCSharpBooleanLiteral(jsonMatcher.Regex)}");
|
||||
sb.AppendLine(@" ))");
|
||||
case JsonMatcher jsonMatcher:
|
||||
{
|
||||
var matcherType = jsonMatcher.GetType().Name;
|
||||
sb.AppendLine($" .WithBody(new {matcherType}(");
|
||||
sb.AppendLine($" value: {ConvertToAnonymousObjectDefinition(jsonMatcher.Value, 3)},");
|
||||
sb.AppendLine($" ignoreCase: {ToCSharpBooleanLiteral(jsonMatcher.IgnoreCase)},");
|
||||
sb.AppendLine($" regex: {ToCSharpBooleanLiteral(jsonMatcher.Regex)}");
|
||||
sb.AppendLine(@" ))");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,9 @@ internal class MatcherMapper
|
||||
case nameof(ContentTypeMatcher):
|
||||
return new ContentTypeMatcher(matchBehaviour, stringPatterns, ignoreCase);
|
||||
|
||||
case nameof(FormUrlEncodedMatcher):
|
||||
return new FormUrlEncodedMatcher(matchBehaviour, stringPatterns, ignoreCase);
|
||||
|
||||
case nameof(SimMetricsMatcher):
|
||||
SimMetricType type = SimMetricType.Levenstein;
|
||||
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
|
||||
@@ -224,7 +227,7 @@ internal class MatcherMapper
|
||||
{
|
||||
if (matcher.Pattern is string patternAsString)
|
||||
{
|
||||
return new[] { new AnyOf<string, StringPattern>(patternAsString) };
|
||||
return [new AnyOf<string, StringPattern>(patternAsString)];
|
||||
}
|
||||
|
||||
if (matcher.Pattern is IEnumerable<string> patternAsStringArray)
|
||||
@@ -241,7 +244,7 @@ internal class MatcherMapper
|
||||
{
|
||||
var patternAsFile = matcher.PatternAsFile!;
|
||||
var pattern = _settings.FileSystemHandler.ReadFileAsString(patternAsFile);
|
||||
return new[] { new AnyOf<string, StringPattern>(new StringPattern { Pattern = pattern, PatternAsFile = patternAsFile }) };
|
||||
return [new AnyOf<string, StringPattern>(new StringPattern { Pattern = pattern, PatternAsFile = patternAsFile })];
|
||||
}
|
||||
|
||||
return EmptyArray<AnyOf<string, StringPattern>>.Value;
|
||||
|
||||
Reference in New Issue
Block a user