cne还缺个路由器。。。
spring mvc的路径匹配实现参考
AntPathMatcher.match(String pattern, String path)
public boolean match(String pattern, String path) {return doMatch(pattern, path, true, null);}
AntPathMatcher.protected boolean doMatch(String pattern, String path, boolean fullMatch,
Map<String, String> uriTemplateVariables)/** * Actually match the givenpath
against the givenpattern
. * @param pattern the pattern to match against * @param path the path String to test * @param fullMatch whether a full pattern match is required (else a pattern match * as far as the given base path goes is sufficient) * @returntrue
if the suppliedpath
matched,false
if it didn't */ protected boolean doMatch(String pattern, String path, boolean fullMatch, MapuriTemplateVariables) { if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) { return false; } String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator); int pattIdxStart = 0; int pattIdxEnd = pattDirs.length - 1; int pathIdxStart = 0; int pathIdxEnd = pathDirs.length - 1; // Match all elements up to the first ** while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { String patDir = pattDirs[pattIdxStart]; if ("**".equals(patDir)) { break; } if (!matchStrings(patDir, pathDirs[pathIdxStart], uriTemplateVariables)) { return false; } pattIdxStart++; pathIdxStart++; } if (pathIdxStart > pathIdxEnd) { // Path is exhausted, only match if rest of pattern is * or **'s if (pattIdxStart > pattIdxEnd) { return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator)); } if (!fullMatch) { return true; } if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) { return true; } for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; } else if (pattIdxStart > pattIdxEnd) { // String not exhausted, but pattern is. Failure. return false; } else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) { // Path start definitely matches due to "**" part in pattern. return true; } // up to last '**' while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { String patDir = pattDirs[pattIdxEnd]; if (patDir.equals("**")) { break; } if (!matchStrings(patDir, pathDirs[pathIdxEnd], uriTemplateVariables)) { return false; } pattIdxEnd--; pathIdxEnd--; } if (pathIdxStart > pathIdxEnd) { // String is exhausted for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; } while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) { int patIdxTmp = -1; for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) { if (pattDirs[i].equals("**")) { patIdxTmp = i; break; } } if (patIdxTmp == pattIdxStart + 1) { // '**/**' situation, so skip one pattIdxStart++; continue; } // Find the pattern between padIdxStart & padIdxTmp in str between // strIdxStart & strIdxEnd int patLength = (patIdxTmp - pattIdxStart - 1); int strLength = (pathIdxEnd - pathIdxStart + 1); int foundIdx = -1; strLoop: for (int i = 0; i <= strLength - patLength; i++) { for (int j = 0; j < patLength; j++) { String subPat = pattDirs[pattIdxStart + j + 1]; String subStr = pathDirs[pathIdxStart + i + j]; if (!matchStrings(subPat, subStr, uriTemplateVariables)) { continue strLoop; } } foundIdx = pathIdxStart + i; break; } if (foundIdx == -1) { return false; } pattIdxStart = patIdxTmp; pathIdxStart = foundIdx + patLength; } for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; }
AntPathMatcher
/* * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.util;import java.util.Comparator;import java.util.LinkedHashMap;import java.util.Map;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * PathMatcher implementation for Ant-style path patterns. Examples are provided below. * *Part of this mapping code has been kindly borrowed from Apache Ant. * *
The mapping matches URLs using the following rules:
- ? matches one character
- * matches zero * or more characters
- ** matches zero or more 'directories' in a path
Some examples:
com/t?st.jsp
- matchescom/test.jsp
but also *com/tast.jsp
orcom/txst.jsp
com/*.jsp
- matches all *.jsp
files in thecom
directorycom/**/test.jsp
- matches all *test.jsp
files underneath thecom
pathorg/springframework/**/*.jsp
* - matches all.jsp
files underneath theorg/springframework
path * org/**/servlet/bla.jsp
- matchesorg/springframework/servlet/bla.jsp
but also *org/springframework/testing/servlet/bla.jsp
andorg/servlet/bla.jsp
path
against the given pattern
. * @param pattern the pattern to match against * @param path the path String to test * @param fullMatch whether a full pattern match is required (else a pattern match * as far as the given base path goes is sufficient) * @return true
if the supplied path
matched, false
if it didn't */ protected boolean doMatch(String pattern, String path, boolean fullMatch, Map null
. * @param str string which must be matched against the pattern. Must not be null
. * @return true
if the string matches against the pattern, or false
otherwise. */ private boolean matchStrings(String pattern, String str, Map For example:
- *
- '
/docs/cvs/commit.html
' and '/docs/cvs/commit.html
-> '' * - '
/docs/*
' and '/docs/cvs/commit
-> 'cvs/commit
' * - '
/docs/cvs/*.html
' and '/docs/cvs/commit.html
-> 'commit.html
' * - '
/docs/**
' and '/docs/cvs/commit
-> 'cvs/commit
' * - '
/docs/**\/*.html
' and '/docs/cvs/commit.html
-> 'cvs/commit.html
' * - '
/*.html
' and '/docs/cvs/commit.html
-> 'docs/cvs/commit.html
' * - '
*.html
' and '/docs/cvs/commit.html
-> '/docs/cvs/commit.html
' * - '
*
' and '/docs/cvs/commit.html
-> '/docs/cvs/commit.html
'
Assumes that {@link #match} returns true
for 'pattern
' and 'path
', but * does not enforce this. */ public String extractPathWithinPattern(String pattern, String path) { String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator); StringBuilder builder = new StringBuilder(); // Add any path parts that have a wildcarded pattern part. int puts = 0; for (int i = 0; i < patternParts.length; i++) { String patternPart = patternParts[i]; if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) { if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) { builder.append(this.pathSeparator); } builder.append(pathParts[i]); puts++; } } // Append any trailing path parts. for (int i = patternParts.length; i < pathParts.length; i++) { if (puts > 0 || i > 0) { builder.append(this.pathSeparator); } builder.append(pathParts[i]); } return builder.toString(); } public Map
This implementation simply concatenates the two patterns, unless the first pattern * contains a file extension match (such as {@code *.html}. In that case, the second pattern * should be included in the first, or an {@code IllegalArgumentException} is thrown. *
For example:
Pattern 1 | Pattern 2 | Result |
---|---|---|
/hotels | {@code * null} | /hotels |
{@code null} | /hotels | /hotels |
/hotels | /bookings | /hotels/bookings |
/hotels | bookings | /hotels/bookings |
/hotels/* | /bookings | /hotels/bookings |
/hotels/** | /bookings | /hotels/**/bookings |
/hotels | {hotel} | /hotels/{hotel} |
/hotels/* | {hotel} | /hotels/{hotel} |
/hotels/** | {hotel} | /hotels/**/{hotel} |
/*.html | /hotels.html | /hotels.html |
/*.html | /hotels | /hotels.html |
/*.html | /*.txt | IllegalArgumentException |
The returned Comparator
will {@linkplain java.util.Collections#sort(java.util.List, * java.util.Comparator) sort} a list so that more specific patterns (without uri templates or wild cards) come before * generic patterns. So given a list with the following patterns:
/hotels/new
* /hotels/{hotel}
/hotels/*
The full path given as parameter is used to test for exact matches. So when the given path is {@code /hotels/2}, * the pattern {@code /hotels/2} will be sorted before {@code /hotels/1}. * @param path the full path to use for comparison * @return a comparator capable of sorting patterns in order of explicitness */ public Comparator