1
+ // Copyright © WireMock.Net
2
+
3
+ using System . Collections . Generic ;
4
+ using AnyOfTypes ;
5
+ using Stef . Validation ;
6
+ using WireMock . Models ;
7
+ using WireMock . Util ;
8
+
9
+ namespace WireMock . Matchers ;
10
+
11
+ /// <summary>
12
+ /// FormUrl Encoded fields Matcher
13
+ /// </summary>
14
+ /// <inheritdoc cref="IStringMatcher"/>
15
+ /// <inheritdoc cref="IIgnoreCaseMatcher"/>
16
+ public class FormUrlEncodedMatcher : IStringMatcher , IIgnoreCaseMatcher
17
+ {
18
+ private readonly AnyOf < string , StringPattern > [ ] _patterns ;
19
+
20
+ /// <inheritdoc />
21
+ public MatchBehaviour MatchBehaviour { get ; }
22
+
23
+ private readonly List < ( WildcardMatcher Key , WildcardMatcher ? Value ) > _pairs = [ ] ;
24
+
25
+ /// <summary>
26
+ /// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
27
+ /// </summary>
28
+ /// <param name="pattern">The pattern.</param>
29
+ /// <param name="ignoreCase">Ignore the case from the pattern.</param>
30
+ /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
31
+ public FormUrlEncodedMatcher (
32
+ AnyOf < string , StringPattern > pattern ,
33
+ bool ignoreCase = false ,
34
+ MatchOperator matchOperator = MatchOperator . Or ) :
35
+ this ( MatchBehaviour . AcceptOnMatch , [ pattern ] , ignoreCase , matchOperator )
36
+ {
37
+ }
38
+
39
+ /// <summary>
40
+ /// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
41
+ /// </summary>
42
+ /// <param name="matchBehaviour">The match behaviour.</param>
43
+ /// <param name="pattern">The pattern.</param>
44
+ /// <param name="ignoreCase">Ignore the case from the pattern.</param>
45
+ /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
46
+ public FormUrlEncodedMatcher (
47
+ MatchBehaviour matchBehaviour ,
48
+ AnyOf < string , StringPattern > pattern ,
49
+ bool ignoreCase = false ,
50
+ MatchOperator matchOperator = MatchOperator . Or ) :
51
+ this ( matchBehaviour , [ pattern ] , ignoreCase , matchOperator )
52
+ {
53
+ }
54
+
55
+ /// <summary>
56
+ /// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
57
+ /// </summary>
58
+ /// <param name="patterns">The patterns.</param>
59
+ /// <param name="ignoreCase">Ignore the case from the pattern.</param>
60
+ /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
61
+ public FormUrlEncodedMatcher (
62
+ AnyOf < string , StringPattern > [ ] patterns ,
63
+ bool ignoreCase = false ,
64
+ MatchOperator matchOperator = MatchOperator . Or ) :
65
+ this ( MatchBehaviour . AcceptOnMatch , patterns , ignoreCase , matchOperator )
66
+ {
67
+ }
68
+
69
+ /// <summary>
70
+ /// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
71
+ /// </summary>
72
+ /// <param name="matchBehaviour">The match behaviour.</param>
73
+ /// <param name="patterns">The patterns.</param>
74
+ /// <param name="ignoreCase">Ignore the case from the pattern.</param>
75
+ /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
76
+ public FormUrlEncodedMatcher (
77
+ MatchBehaviour matchBehaviour ,
78
+ AnyOf < string , StringPattern > [ ] patterns ,
79
+ bool ignoreCase = false ,
80
+ MatchOperator matchOperator = MatchOperator . Or )
81
+ {
82
+ _patterns = Guard . NotNull ( patterns ) ;
83
+ IgnoreCase = ignoreCase ;
84
+ MatchBehaviour = matchBehaviour ;
85
+ MatchOperator = matchOperator ;
86
+
87
+ foreach ( var pattern in _patterns )
88
+ {
89
+ if ( QueryStringParser . TryParse ( pattern , IgnoreCase , out var nameValueCollection ) )
90
+ {
91
+ foreach ( var nameValue in nameValueCollection )
92
+ {
93
+ var keyMatcher = new WildcardMatcher ( MatchBehaviour . AcceptOnMatch , [ nameValue . Key ] , ignoreCase , MatchOperator ) ;
94
+ var valueMatcher = new WildcardMatcher ( MatchBehaviour . AcceptOnMatch , [ nameValue . Value ] , ignoreCase , MatchOperator ) ;
95
+ _pairs . Add ( ( keyMatcher , valueMatcher ) ) ;
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ /// <inheritdoc />
102
+ public MatchResult IsMatch ( string ? input )
103
+ {
104
+ // Input is null or empty and if no patterns defined, return Perfect match.
105
+ if ( string . IsNullOrEmpty ( input ) && _patterns . Length == 0 )
106
+ {
107
+ return new MatchResult ( MatchScores . Perfect ) ;
108
+ }
109
+
110
+ if ( ! QueryStringParser . TryParse ( input , IgnoreCase , out var inputNameValueCollection ) )
111
+ {
112
+ return new MatchResult ( MatchScores . Mismatch ) ;
113
+ }
114
+
115
+ var matches = new List < bool > ( ) ;
116
+ foreach ( var inputKeyValuePair in inputNameValueCollection )
117
+ {
118
+ var match = false ;
119
+ foreach ( var pair in _pairs )
120
+ {
121
+ var keyMatchResult = pair . Key . IsMatch ( inputKeyValuePair . Key ) . IsPerfect ( ) ;
122
+ if ( keyMatchResult )
123
+ {
124
+ match = pair . Value ? . IsMatch ( inputKeyValuePair . Value ) . IsPerfect ( ) ?? false ;
125
+ if ( match )
126
+ {
127
+ break ;
128
+ }
129
+ }
130
+ }
131
+
132
+ matches . Add ( match ) ;
133
+ }
134
+
135
+ var score = MatchScores . ToScore ( matches . ToArray ( ) , MatchOperator ) ;
136
+ return new MatchResult ( MatchBehaviourHelper . Convert ( MatchBehaviour , score ) ) ;
137
+ }
138
+
139
+ /// <inheritdoc />
140
+ public virtual AnyOf < string , StringPattern > [ ] GetPatterns ( )
141
+ {
142
+ return _patterns ;
143
+ }
144
+
145
+ /// <inheritdoc />
146
+ public virtual string Name => nameof ( FormUrlEncodedMatcher ) ;
147
+
148
+ /// <inheritdoc />
149
+ public bool IgnoreCase { get ; }
150
+
151
+ /// <inheritdoc />
152
+ public MatchOperator MatchOperator { get ; }
153
+ }
0 commit comments