34
34
import org .springframework .web .util .UrlPathHelper ;
35
35
36
36
/**
37
- * A logical disjunction (' || ') request condition that matches a request
38
- * against a set of URL path patterns.
39
- *
37
+ * A logical disjunction (' || ') request condition that matches a request
38
+ * against a set of URL path patterns.
39
+ *
40
40
* @author Rossen Stoyanchev
41
41
* @since 3.1
42
42
*/
43
43
public final class PatternsRequestCondition extends AbstractRequestCondition <PatternsRequestCondition > {
44
-
45
- private final Set <String > patterns ;
44
+
45
+ private final Set <String > patterns ;
46
46
47
47
private final UrlPathHelper urlPathHelper ;
48
-
48
+
49
49
private final PathMatcher pathMatcher ;
50
50
51
51
private final boolean useSuffixPatternMatch ;
52
52
53
53
private final boolean useTrailingSlashMatch ;
54
-
54
+
55
+ private final List <String > fileExtensions = new ArrayList <String >();
56
+
55
57
/**
56
58
* Creates a new instance with the given URL patterns.
57
- * Each pattern that is not empty and does not start with "/" is pre-pended with "/".
58
- * @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
59
+ * Each pattern that is not empty and does not start with "/" is prepended with "/".
60
+ * @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
59
61
*/
60
62
public PatternsRequestCondition (String ... patterns ) {
61
- this (asList (patterns ), null , null , true , true );
63
+ this (asList (patterns ), null , null , true , true , null );
64
+ }
65
+
66
+ /**
67
+ * Additional constructor with flags for using suffix pattern (.*) and
68
+ * trailing slash matches.
69
+ *
70
+ * @param patterns the URL patterns to use; if 0, the condition will match to every request.
71
+ * @param urlPathHelper for determining the lookup path of a request
72
+ * @param pathMatcher for path matching with patterns
73
+ * @param useSuffixPatternMatch whether to enable matching by suffix (".*")
74
+ * @param useTrailingSlashMatch whether to match irrespective of a trailing slash
75
+ */
76
+ public PatternsRequestCondition (String [] patterns , UrlPathHelper urlPathHelper , PathMatcher pathMatcher ,
77
+ boolean useSuffixPatternMatch , boolean useTrailingSlashMatch ) {
78
+
79
+ this (asList (patterns ), urlPathHelper , pathMatcher , useSuffixPatternMatch , useTrailingSlashMatch , null );
62
80
}
63
81
64
82
/**
65
83
* Creates a new instance with the given URL patterns.
66
84
* Each pattern that is not empty and does not start with "/" is pre-pended with "/".
67
- * @param patterns the URL patterns to use; if 0, the condition will match to every request.
85
+ * @param patterns the URL patterns to use; if 0, the condition will match to every request.
68
86
* @param urlPathHelper a {@link UrlPathHelper} for determining the lookup path for a request
69
87
* @param pathMatcher a {@link PathMatcher} for pattern path matching
70
88
* @param useSuffixPatternMatch whether to enable matching by suffix (".*")
71
89
* @param useTrailingSlashMatch whether to match irrespective of a trailing slash
90
+ * @param fileExtensions a list of file extensions to consider for path matching
72
91
*/
73
- public PatternsRequestCondition (String [] patterns ,
74
- UrlPathHelper urlPathHelper ,
75
- PathMatcher pathMatcher ,
76
- boolean useSuffixPatternMatch ,
77
- boolean useTrailingSlashMatch ) {
78
- this (asList (patterns ), urlPathHelper , pathMatcher , useSuffixPatternMatch , useTrailingSlashMatch );
92
+ public PatternsRequestCondition (String [] patterns , UrlPathHelper urlPathHelper ,
93
+ PathMatcher pathMatcher , boolean useSuffixPatternMatch , boolean useTrailingSlashMatch ,
94
+ List <String > fileExtensions ) {
95
+
96
+ this (asList (patterns ), urlPathHelper , pathMatcher , useSuffixPatternMatch , useTrailingSlashMatch , fileExtensions );
79
97
}
80
98
81
99
/**
82
100
* Private constructor accepting a collection of patterns.
101
+ * @param fileExtensionResolver
83
102
*/
84
- private PatternsRequestCondition (Collection <String > patterns ,
85
- UrlPathHelper urlPathHelper ,
86
- PathMatcher pathMatcher ,
87
- boolean useSuffixPatternMatch ,
88
- boolean useTrailingSlashMatch ) {
103
+ private PatternsRequestCondition (Collection <String > patterns , UrlPathHelper urlPathHelper ,
104
+ PathMatcher pathMatcher , boolean useSuffixPatternMatch , boolean useTrailingSlashMatch ,
105
+ List <String > fileExtensions ) {
106
+
89
107
this .patterns = Collections .unmodifiableSet (prependLeadingSlash (patterns ));
90
108
this .urlPathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper ();
91
109
this .pathMatcher = pathMatcher != null ? pathMatcher : new AntPathMatcher ();
92
110
this .useSuffixPatternMatch = useSuffixPatternMatch ;
93
111
this .useTrailingSlashMatch = useTrailingSlashMatch ;
112
+ if (fileExtensions != null ) {
113
+ for (String fileExtension : fileExtensions ) {
114
+ this .fileExtensions .add ("." + fileExtension );
115
+ }
116
+ }
94
117
}
95
118
96
119
private static List <String > asList (String ... patterns ) {
@@ -126,15 +149,15 @@ protected String getToStringInfix() {
126
149
}
127
150
128
151
/**
129
- * Returns a new instance with URL patterns from the current instance ("this") and
130
- * the "other" instance as follows:
152
+ * Returns a new instance with URL patterns from the current instance ("this") and
153
+ * the "other" instance as follows:
131
154
* <ul>
132
- * <li>If there are patterns in both instances, combine the patterns in "this" with
155
+ * <li>If there are patterns in both instances, combine the patterns in "this" with
133
156
* the patterns in "other" using {@link PathMatcher#combine(String, String)}.
134
157
* <li>If only one instance has patterns, use them.
135
158
* <li>If neither instance has patterns, use an empty String (i.e. "").
136
159
* </ul>
137
- */
160
+ */
138
161
public PatternsRequestCondition combine (PatternsRequestCondition other ) {
139
162
Set <String > result = new LinkedHashSet <String >();
140
163
if (!this .patterns .isEmpty () && !other .patterns .isEmpty ()) {
@@ -154,26 +177,26 @@ else if (!other.patterns.isEmpty()) {
154
177
result .add ("" );
155
178
}
156
179
return new PatternsRequestCondition (result , this .urlPathHelper , this .pathMatcher , this .useSuffixPatternMatch ,
157
- this .useTrailingSlashMatch );
180
+ this .useTrailingSlashMatch , this . fileExtensions );
158
181
}
159
182
160
183
/**
161
- * Checks if any of the patterns match the given request and returns an instance
162
- * that is guaranteed to contain matching patterns, sorted via
163
- * {@link PathMatcher#getPatternComparator(String)}.
164
- *
184
+ * Checks if any of the patterns match the given request and returns an instance
185
+ * that is guaranteed to contain matching patterns, sorted via
186
+ * {@link PathMatcher#getPatternComparator(String)}.
187
+ *
165
188
* <p>A matching pattern is obtained by making checks in the following order:
166
189
* <ul>
167
190
* <li>Direct match
168
191
* <li>Pattern match with ".*" appended if the pattern doesn't already contain a "."
169
192
* <li>Pattern match
170
193
* <li>Pattern match with "/" appended if the pattern doesn't already end in "/"
171
194
* </ul>
172
- *
195
+ *
173
196
* @param request the current request
174
- *
175
- * @return the same instance if the condition contains no patterns;
176
- * or a new condition with sorted matching patterns;
197
+ *
198
+ * @return the same instance if the condition contains no patterns;
199
+ * or a new condition with sorted matching patterns;
177
200
* or {@code null} if no patterns match.
178
201
*/
179
202
public PatternsRequestCondition getMatchingCondition (HttpServletRequest request ) {
@@ -189,19 +212,28 @@ public PatternsRequestCondition getMatchingCondition(HttpServletRequest request)
189
212
}
190
213
}
191
214
Collections .sort (matches , this .pathMatcher .getPatternComparator (lookupPath ));
192
- return matches .isEmpty () ? null :
215
+ return matches .isEmpty () ? null :
193
216
new PatternsRequestCondition (matches , this .urlPathHelper , this .pathMatcher , this .useSuffixPatternMatch ,
194
- this .useTrailingSlashMatch );
217
+ this .useTrailingSlashMatch , this . fileExtensions );
195
218
}
196
219
197
220
private String getMatchingPattern (String pattern , String lookupPath ) {
198
221
if (pattern .equals (lookupPath )) {
199
222
return pattern ;
200
223
}
201
224
if (this .useSuffixPatternMatch ) {
202
- boolean hasSuffix = pattern .indexOf ('.' ) != -1 ;
203
- if (!hasSuffix && this .pathMatcher .match (pattern + ".*" , lookupPath )) {
204
- return pattern + ".*" ;
225
+ if (useSmartSuffixPatternMatch (pattern , lookupPath )) {
226
+ for (String extension : this .fileExtensions ) {
227
+ if (this .pathMatcher .match (pattern + extension , lookupPath )) {
228
+ return pattern + extension ;
229
+ }
230
+ }
231
+ }
232
+ else {
233
+ boolean hasSuffix = pattern .indexOf ('.' ) != -1 ;
234
+ if (!hasSuffix && this .pathMatcher .match (pattern + ".*" , lookupPath )) {
235
+ return pattern + ".*" ;
236
+ }
205
237
}
206
238
}
207
239
if (this .pathMatcher .match (pattern , lookupPath )) {
@@ -217,15 +249,23 @@ private String getMatchingPattern(String pattern, String lookupPath) {
217
249
}
218
250
219
251
/**
220
- * Compare the two conditions based on the URL patterns they contain.
221
- * Patterns are compared one at a time, from top to bottom via
222
- * {@link PathMatcher#getPatternComparator(String)}. If all compared
223
- * patterns match equally, but one instance has more patterns, it is
252
+ * Whether to match by known file extensions. Return "true" if file extensions
253
+ * are configured, and the lookup path has a suffix.
254
+ */
255
+ private boolean useSmartSuffixPatternMatch (String pattern , String lookupPath ) {
256
+ return (!this .fileExtensions .isEmpty () && lookupPath .indexOf ('.' ) != -1 ) ;
257
+ }
258
+
259
+ /**
260
+ * Compare the two conditions based on the URL patterns they contain.
261
+ * Patterns are compared one at a time, from top to bottom via
262
+ * {@link PathMatcher#getPatternComparator(String)}. If all compared
263
+ * patterns match equally, but one instance has more patterns, it is
224
264
* considered a closer match.
225
- *
226
- * <p>It is assumed that both instances have been obtained via
227
- * {@link #getMatchingCondition(HttpServletRequest)} to ensure they
228
- * contain only patterns that match the request and are sorted with
265
+ *
266
+ * <p>It is assumed that both instances have been obtained via
267
+ * {@link #getMatchingCondition(HttpServletRequest)} to ensure they
268
+ * contain only patterns that match the request and are sorted with
229
269
* the best matches on top.
230
270
*/
231
271
public int compareTo (PatternsRequestCondition other , HttpServletRequest request ) {
0 commit comments