新增Controller全局拦截支持
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.base;
|
||||
|
||||
import fun.asgc.neutrino.core.util.ObjectUtil;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public class OrderComparator implements Comparator<Object> {
|
||||
|
||||
/**
|
||||
* Shared default instance of {@code OrderComparator}.
|
||||
*/
|
||||
public static final OrderComparator INSTANCE = new OrderComparator();
|
||||
|
||||
|
||||
/**
|
||||
* Build an adapted order comparator with the given source provider.
|
||||
* @param sourceProvider the order source provider to use
|
||||
* @return the adapted comparator
|
||||
* @since 4.1
|
||||
*/
|
||||
public Comparator<Object> withSourceProvider(final OrderSourceProvider sourceProvider) {
|
||||
return new Comparator<Object>() {
|
||||
@Override
|
||||
public int compare(Object o1, Object o2) {
|
||||
return doCompare(o1, o2, sourceProvider);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Object o1, Object o2) {
|
||||
return doCompare(o1, o2, null);
|
||||
}
|
||||
|
||||
private int doCompare(Object o1, Object o2, OrderSourceProvider sourceProvider) {
|
||||
boolean p1 = (o1 instanceof PriorityOrdered);
|
||||
boolean p2 = (o2 instanceof PriorityOrdered);
|
||||
if (p1 && !p2) {
|
||||
return -1;
|
||||
}
|
||||
else if (p2 && !p1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Direct evaluation instead of Integer.compareTo to avoid unnecessary object creation.
|
||||
int i1 = getOrder(o1, sourceProvider);
|
||||
int i2 = getOrder(o2, sourceProvider);
|
||||
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the order value for the given object.
|
||||
* <p>The default implementation checks against the given {@link OrderSourceProvider}
|
||||
* using {@link #findOrder} and falls back to a regular {@link #getOrder(Object)} call.
|
||||
* @param obj the object to check
|
||||
* @return the order value, or {@code Ordered.LOWEST_PRECEDENCE} as fallback
|
||||
*/
|
||||
private int getOrder(Object obj, OrderSourceProvider sourceProvider) {
|
||||
Integer order = null;
|
||||
if (sourceProvider != null) {
|
||||
Object orderSource = sourceProvider.getOrderSource(obj);
|
||||
if (orderSource != null && orderSource.getClass().isArray()) {
|
||||
Object[] sources = ObjectUtil.toObjectArray(orderSource);
|
||||
for (Object source : sources) {
|
||||
order = findOrder(source);
|
||||
if (order != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
order = findOrder(orderSource);
|
||||
}
|
||||
}
|
||||
return (order != null ? order : getOrder(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the order value for the given object.
|
||||
* <p>The default implementation checks against the {@link Ordered} interface
|
||||
* through delegating to {@link #findOrder}. Can be overridden in subclasses.
|
||||
* @param obj the object to check
|
||||
* @return the order value, or {@code Ordered.LOWEST_PRECEDENCE} as fallback
|
||||
*/
|
||||
protected int getOrder(Object obj) {
|
||||
Integer order = findOrder(obj);
|
||||
return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an order value indicated by the given object.
|
||||
* <p>The default implementation checks against the {@link Ordered} interface.
|
||||
* Can be overridden in subclasses.
|
||||
* @param obj the object to check
|
||||
* @return the order value, or {@code null} if none found
|
||||
*/
|
||||
protected Integer findOrder(Object obj) {
|
||||
return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a priority value for the given object, if any.
|
||||
* <p>The default implementation always returns {@code null}.
|
||||
* Subclasses may override this to give specific kinds of values a
|
||||
* 'priority' characteristic, in addition to their 'order' semantics.
|
||||
* A priority indicates that it may be used for selecting one object over
|
||||
* another, in addition to serving for ordering purposes in a list/array.
|
||||
* @param obj the object to check
|
||||
* @return the priority value, or {@code null} if none
|
||||
* @since 4.1
|
||||
*/
|
||||
public Integer getPriority(Object obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort the given List with a default OrderComparator.
|
||||
* <p>Optimized to skip sorting for lists with size 0 or 1,
|
||||
* in order to avoid unnecessary array extraction.
|
||||
* @param list the List to sort
|
||||
* @see java.util.Collections#sort(java.util.List, java.util.Comparator)
|
||||
*/
|
||||
public static void sort(List<?> list) {
|
||||
if (list.size() > 1) {
|
||||
Collections.sort(list, INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the given array with a default OrderComparator.
|
||||
* <p>Optimized to skip sorting for lists with size 0 or 1,
|
||||
* in order to avoid unnecessary array extraction.
|
||||
* @param array the array to sort
|
||||
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
|
||||
*/
|
||||
public static void sort(Object[] array) {
|
||||
if (array.length > 1) {
|
||||
Arrays.sort(array, INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the given array or List with a default OrderComparator,
|
||||
* if necessary. Simply skips sorting when given any other value.
|
||||
* <p>Optimized to skip sorting for lists with size 0 or 1,
|
||||
* in order to avoid unnecessary array extraction.
|
||||
* @param value the array or List to sort
|
||||
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
|
||||
*/
|
||||
public static void sortIfNecessary(Object value) {
|
||||
if (value instanceof Object[]) {
|
||||
sort((Object[]) value);
|
||||
}
|
||||
else if (value instanceof List) {
|
||||
sort((List<?>) value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Strategy interface to provide an order source for a given object.
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface OrderSourceProvider {
|
||||
|
||||
/**
|
||||
* Return an order source for the specified object, i.e. an object that
|
||||
* should be checked for an order value as a replacement to the given object.
|
||||
* <p>Can also be an array of order source objects.
|
||||
* <p>If the returned object does not indicate any order, the comparator
|
||||
* will fall back to checking the original object.
|
||||
* @param obj the object to find an order source for
|
||||
* @return the order source for that object, or {@code null} if none found
|
||||
*/
|
||||
Object getOrderSource(Object obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.base;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public interface Ordered {
|
||||
/**
|
||||
* Useful constant for the highest precedence value.
|
||||
* @see java.lang.Integer#MIN_VALUE
|
||||
*/
|
||||
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* Useful constant for the lowest precedence value.
|
||||
* @see java.lang.Integer#MAX_VALUE
|
||||
*/
|
||||
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
/**
|
||||
* Get the order value of this object.
|
||||
* <p>Higher values are interpreted as lower priority. As a consequence,
|
||||
* the object with the lowest value has the highest priority (somewhat
|
||||
* analogous to Servlet {@code load-on-startup} values).
|
||||
* <p>Same order values will result in arbitrary sort positions for the
|
||||
* affected objects.
|
||||
* @return the order value
|
||||
* @see #HIGHEST_PRECEDENCE
|
||||
* @see #LOWEST_PRECEDENCE
|
||||
*/
|
||||
int getOrder();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.base;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public interface PriorityOrdered extends Ordered {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,874 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web;
|
||||
|
||||
import fun.asgc.neutrino.core.util.Assert;
|
||||
import fun.asgc.neutrino.core.util.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public class AntPathMatcher implements PathMatcher {
|
||||
|
||||
/** Default path separator: "/" */
|
||||
public static final String DEFAULT_PATH_SEPARATOR = "/";
|
||||
|
||||
private static final int CACHE_TURNOFF_THRESHOLD = 65536;
|
||||
|
||||
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}");
|
||||
|
||||
private static final char[] WILDCARD_CHARS = { '*', '?', '{' };
|
||||
|
||||
|
||||
private String pathSeparator;
|
||||
|
||||
private PathSeparatorPatternCache pathSeparatorPatternCache;
|
||||
|
||||
private boolean caseSensitive = true;
|
||||
|
||||
private boolean trimTokens = false;
|
||||
|
||||
private volatile Boolean cachePatterns;
|
||||
|
||||
private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<String, String[]>(256);
|
||||
|
||||
final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<String, AntPathStringMatcher>(256);
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance with the {@link #DEFAULT_PATH_SEPARATOR}.
|
||||
*/
|
||||
public AntPathMatcher() {
|
||||
this.pathSeparator = DEFAULT_PATH_SEPARATOR;
|
||||
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(DEFAULT_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient, alternative constructor to use with a custom path separator.
|
||||
* @param pathSeparator the path separator to use, must not be {@code null}.
|
||||
* @since 4.1
|
||||
*/
|
||||
public AntPathMatcher(String pathSeparator) {
|
||||
Assert.notNull(pathSeparator, "'pathSeparator' is required");
|
||||
this.pathSeparator = pathSeparator;
|
||||
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the path separator to use for pattern parsing.
|
||||
* <p>Default is "/", as in Ant.
|
||||
*/
|
||||
public void setPathSeparator(String pathSeparator) {
|
||||
this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);
|
||||
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to perform pattern matching in a case-sensitive fashion.
|
||||
* <p>Default is {@code true}. Switch this to {@code false} for case-insensitive matching.
|
||||
* @since 4.2
|
||||
*/
|
||||
public void setCaseSensitive(boolean caseSensitive) {
|
||||
this.caseSensitive = caseSensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to trim tokenized paths and patterns.
|
||||
* <p>Default is {@code false}.
|
||||
*/
|
||||
public void setTrimTokens(boolean trimTokens) {
|
||||
this.trimTokens = trimTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to cache parsed pattern metadata for patterns passed
|
||||
* into this matcher's {@link #match} method. A value of {@code true}
|
||||
* activates an unlimited pattern cache; a value of {@code false} turns
|
||||
* the pattern cache off completely.
|
||||
* <p>Default is for the cache to be on, but with the variant to automatically
|
||||
* turn it off when encountering too many patterns to cache at runtime
|
||||
* (the threshold is 65536), assuming that arbitrary permutations of patterns
|
||||
* are coming in, with little chance for encountering a recurring pattern.
|
||||
* @since 4.0.1
|
||||
* @see #getStringMatcher(String)
|
||||
*/
|
||||
public void setCachePatterns(boolean cachePatterns) {
|
||||
this.cachePatterns = cachePatterns;
|
||||
}
|
||||
|
||||
private void deactivatePatternCache() {
|
||||
this.cachePatterns = false;
|
||||
this.tokenizedPatternCache.clear();
|
||||
this.stringMatcherCache.clear();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPattern(String path) {
|
||||
return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(String pattern, String path) {
|
||||
return doMatch(pattern, path, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchStart(String pattern, String path) {
|
||||
return doMatch(pattern, path, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually match the given {@code path} against the given {@code 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 {@code true} if the supplied {@code path} matched, {@code false} if it didn't
|
||||
*/
|
||||
protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
|
||||
if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] pattDirs = tokenizePattern(pattern);
|
||||
if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] pathDirs = tokenizePath(path);
|
||||
|
||||
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 pattDir = pattDirs[pattIdxStart];
|
||||
if ("**".equals(pattDir)) {
|
||||
break;
|
||||
}
|
||||
if (!matchStrings(pattDir, 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));
|
||||
}
|
||||
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 pattDir = pattDirs[pattIdxEnd];
|
||||
if (pattDir.equals("**")) {
|
||||
break;
|
||||
}
|
||||
if (!matchStrings(pattDir, 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;
|
||||
}
|
||||
|
||||
private boolean isPotentialMatch(String path, String[] pattDirs) {
|
||||
if (!this.trimTokens) {
|
||||
int pos = 0;
|
||||
for (String pattDir : pattDirs) {
|
||||
int skipped = skipSeparator(path, pos, this.pathSeparator);
|
||||
pos += skipped;
|
||||
skipped = skipSegment(path, pos, pattDir);
|
||||
if (skipped < pattDir.length()) {
|
||||
return (skipped > 0 || (pattDir.length() > 0 && isWildcardChar(pattDir.charAt(0))));
|
||||
}
|
||||
pos += skipped;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int skipSegment(String path, int pos, String prefix) {
|
||||
int skipped = 0;
|
||||
for (int i = 0; i < prefix.length(); i++) {
|
||||
char c = prefix.charAt(i);
|
||||
if (isWildcardChar(c)) {
|
||||
return skipped;
|
||||
}
|
||||
int currPos = pos + skipped;
|
||||
if (currPos >= path.length()) {
|
||||
return 0;
|
||||
}
|
||||
if (c == path.charAt(currPos)) {
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
return skipped;
|
||||
}
|
||||
|
||||
private int skipSeparator(String path, int pos, String separator) {
|
||||
int skipped = 0;
|
||||
while (path.startsWith(separator, pos + skipped)) {
|
||||
skipped += separator.length();
|
||||
}
|
||||
return skipped;
|
||||
}
|
||||
|
||||
private boolean isWildcardChar(char c) {
|
||||
for (char candidate : WILDCARD_CHARS) {
|
||||
if (c == candidate) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenize the given path pattern into parts, based on this matcher's settings.
|
||||
* <p>Performs caching based on {@link #setCachePatterns}, delegating to
|
||||
* {@link #tokenizePath(String)} for the actual tokenization algorithm.
|
||||
* @param pattern the pattern to tokenize
|
||||
* @return the tokenized pattern parts
|
||||
*/
|
||||
protected String[] tokenizePattern(String pattern) {
|
||||
String[] tokenized = null;
|
||||
Boolean cachePatterns = this.cachePatterns;
|
||||
if (cachePatterns == null || cachePatterns.booleanValue()) {
|
||||
tokenized = this.tokenizedPatternCache.get(pattern);
|
||||
}
|
||||
if (tokenized == null) {
|
||||
tokenized = tokenizePath(pattern);
|
||||
if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) {
|
||||
// Try to adapt to the runtime situation that we're encountering:
|
||||
// There are obviously too many different patterns coming in here...
|
||||
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
|
||||
deactivatePatternCache();
|
||||
return tokenized;
|
||||
}
|
||||
if (cachePatterns == null || cachePatterns.booleanValue()) {
|
||||
this.tokenizedPatternCache.put(pattern, tokenized);
|
||||
}
|
||||
}
|
||||
return tokenized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenize the given path String into parts, based on this matcher's settings.
|
||||
* @param path the path to tokenize
|
||||
* @return the tokenized path parts
|
||||
*/
|
||||
protected String[] tokenizePath(String path) {
|
||||
return StringUtil.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether or not a string matches against a pattern.
|
||||
* @param pattern the pattern to match against (never {@code null})
|
||||
* @param str the String which must be matched against the pattern (never {@code null})
|
||||
* @return {@code true} if the string matches against the pattern, or {@code false} otherwise
|
||||
*/
|
||||
private boolean matchStrings(String pattern, String str, Map<String, String> uriTemplateVariables) {
|
||||
return getStringMatcher(pattern).matchStrings(str, uriTemplateVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build or retrieve an {@link AntPathStringMatcher} for the given pattern.
|
||||
* <p>The default implementation checks this AntPathMatcher's internal cache
|
||||
* (see {@link #setCachePatterns}), creating a new AntPathStringMatcher instance
|
||||
* if no cached copy is found.
|
||||
* <p>When encountering too many patterns to cache at runtime (the threshold is 65536),
|
||||
* it turns the default cache off, assuming that arbitrary permutations of patterns
|
||||
* are coming in, with little chance for encountering a recurring pattern.
|
||||
* <p>This method may be overridden to implement a custom cache strategy.
|
||||
* @param pattern the pattern to match against (never {@code null})
|
||||
* @return a corresponding AntPathStringMatcher (never {@code null})
|
||||
* @see #setCachePatterns
|
||||
*/
|
||||
protected AntPathStringMatcher getStringMatcher(String pattern) {
|
||||
AntPathStringMatcher matcher = null;
|
||||
Boolean cachePatterns = this.cachePatterns;
|
||||
if (cachePatterns == null || cachePatterns.booleanValue()) {
|
||||
matcher = this.stringMatcherCache.get(pattern);
|
||||
}
|
||||
if (matcher == null) {
|
||||
matcher = new AntPathStringMatcher(pattern, this.caseSensitive);
|
||||
if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
|
||||
// Try to adapt to the runtime situation that we're encountering:
|
||||
// There are obviously too many different patterns coming in here...
|
||||
// So let's turn off the cache since the patterns are unlikely to be reoccurring.
|
||||
deactivatePatternCache();
|
||||
return matcher;
|
||||
}
|
||||
if (cachePatterns == null || cachePatterns.booleanValue()) {
|
||||
this.stringMatcherCache.put(pattern, matcher);
|
||||
}
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a pattern and a full path, determine the pattern-mapped part. <p>For example: <ul>
|
||||
* <li>'{@code /docs/cvs/commit.html}' and '{@code /docs/cvs/commit.html} -> ''</li>
|
||||
* <li>'{@code /docs/*}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'</li>
|
||||
* <li>'{@code /docs/cvs/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code commit.html}'</li>
|
||||
* <li>'{@code /docs/**}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'</li>
|
||||
* <li>'{@code /docs/**\/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code cvs/commit.html}'</li>
|
||||
* <li>'{@code /*.html}' and '{@code /docs/cvs/commit.html} -> '{@code docs/cvs/commit.html}'</li>
|
||||
* <li>'{@code *.html}' and '{@code /docs/cvs/commit.html} -> '{@code /docs/cvs/commit.html}'</li>
|
||||
* <li>'{@code *}' and '{@code /docs/cvs/commit.html} -> '{@code /docs/cvs/commit.html}'</li> </ul>
|
||||
* <p>Assumes that {@link #match} returns {@code true} for '{@code pattern}' and '{@code path}', but
|
||||
* does <strong>not</strong> enforce this.
|
||||
*/
|
||||
@Override
|
||||
public String extractPathWithinPattern(String pattern, String path) {
|
||||
String[] patternParts = StringUtil.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true);
|
||||
String[] pathParts = StringUtil.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean pathStarted = false;
|
||||
|
||||
for (int segment = 0; segment < patternParts.length; segment++) {
|
||||
String patternPart = patternParts[segment];
|
||||
if (patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) {
|
||||
for (; segment < pathParts.length; segment++) {
|
||||
if (pathStarted || (segment == 0 && !pattern.startsWith(this.pathSeparator))) {
|
||||
builder.append(this.pathSeparator);
|
||||
}
|
||||
builder.append(pathParts[segment]);
|
||||
pathStarted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
|
||||
Map<String, String> variables = new LinkedHashMap<String, String>();
|
||||
boolean result = doMatch(pattern, path, true, variables);
|
||||
if (!result) {
|
||||
throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine two patterns into a new pattern.
|
||||
* <p>This implementation simply concatenates the two patterns, unless
|
||||
* the first pattern contains a file extension match (e.g., {@code *.html}).
|
||||
* In that case, the second pattern will be merged into the first. Otherwise,
|
||||
* an {@code IllegalArgumentException} will be thrown.
|
||||
* <h3>Examples</h3>
|
||||
* <table border="1">
|
||||
* <tr><th>Pattern 1</th><th>Pattern 2</th><th>Result</th></tr>
|
||||
* <tr><td>{@code null}</td><td>{@code null}</td><td> </td></tr>
|
||||
* <tr><td>/hotels</td><td>{@code null}</td><td>/hotels</td></tr>
|
||||
* <tr><td>{@code null}</td><td>/hotels</td><td>/hotels</td></tr>
|
||||
* <tr><td>/hotels</td><td>/bookings</td><td>/hotels/bookings</td></tr>
|
||||
* <tr><td>/hotels</td><td>bookings</td><td>/hotels/bookings</td></tr>
|
||||
* <tr><td>/hotels/*</td><td>/bookings</td><td>/hotels/bookings</td></tr>
|
||||
* <tr><td>/hotels/**</td><td>/bookings</td><td>/hotels/**/bookings</td></tr>
|
||||
* <tr><td>/hotels</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
|
||||
* <tr><td>/hotels/*</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
|
||||
* <tr><td>/hotels/**</td><td>{hotel}</td><td>/hotels/**/{hotel}</td></tr>
|
||||
* <tr><td>/*.html</td><td>/hotels.html</td><td>/hotels.html</td></tr>
|
||||
* <tr><td>/*.html</td><td>/hotels</td><td>/hotels.html</td></tr>
|
||||
* <tr><td>/*.html</td><td>/*.txt</td><td>{@code IllegalArgumentException}</td></tr>
|
||||
* </table>
|
||||
* @param pattern1 the first pattern
|
||||
* @param pattern2 the second pattern
|
||||
* @return the combination of the two patterns
|
||||
* @throws IllegalArgumentException if the two patterns cannot be combined
|
||||
*/
|
||||
@Override
|
||||
public String combine(String pattern1, String pattern2) {
|
||||
if (!StringUtil.hasText(pattern1) && !StringUtil.hasText(pattern2)) {
|
||||
return "";
|
||||
}
|
||||
if (!StringUtil.hasText(pattern1)) {
|
||||
return pattern2;
|
||||
}
|
||||
if (!StringUtil.hasText(pattern2)) {
|
||||
return pattern1;
|
||||
}
|
||||
|
||||
boolean pattern1ContainsUriVar = (pattern1.indexOf('{') != -1);
|
||||
if (!pattern1.equals(pattern2) && !pattern1ContainsUriVar && match(pattern1, pattern2)) {
|
||||
// /* + /hotel -> /hotel ; "/*.*" + "/*.html" -> /*.html
|
||||
// However /user + /user -> /usr/user ; /{foo} + /bar -> /{foo}/bar
|
||||
return pattern2;
|
||||
}
|
||||
|
||||
// /hotels/* + /booking -> /hotels/booking
|
||||
// /hotels/* + booking -> /hotels/booking
|
||||
if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnWildCard())) {
|
||||
return concat(pattern1.substring(0, pattern1.length() - 2), pattern2);
|
||||
}
|
||||
|
||||
// /hotels/** + /booking -> /hotels/**/booking
|
||||
// /hotels/** + booking -> /hotels/**/booking
|
||||
if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnDoubleWildCard())) {
|
||||
return concat(pattern1, pattern2);
|
||||
}
|
||||
|
||||
int starDotPos1 = pattern1.indexOf("*.");
|
||||
if (pattern1ContainsUriVar || starDotPos1 == -1 || this.pathSeparator.equals(".")) {
|
||||
// simply concatenate the two patterns
|
||||
return concat(pattern1, pattern2);
|
||||
}
|
||||
|
||||
String ext1 = pattern1.substring(starDotPos1 + 1);
|
||||
int dotPos2 = pattern2.indexOf('.');
|
||||
String file2 = (dotPos2 == -1 ? pattern2 : pattern2.substring(0, dotPos2));
|
||||
String ext2 = (dotPos2 == -1 ? "" : pattern2.substring(dotPos2));
|
||||
boolean ext1All = (ext1.equals(".*") || ext1.equals(""));
|
||||
boolean ext2All = (ext2.equals(".*") || ext2.equals(""));
|
||||
if (!ext1All && !ext2All) {
|
||||
throw new IllegalArgumentException("Cannot combine patterns: " + pattern1 + " vs " + pattern2);
|
||||
}
|
||||
String ext = (ext1All ? ext2 : ext1);
|
||||
return file2 + ext;
|
||||
}
|
||||
|
||||
private String concat(String path1, String path2) {
|
||||
boolean path1EndsWithSeparator = path1.endsWith(this.pathSeparator);
|
||||
boolean path2StartsWithSeparator = path2.startsWith(this.pathSeparator);
|
||||
|
||||
if (path1EndsWithSeparator && path2StartsWithSeparator) {
|
||||
return path1 + path2.substring(1);
|
||||
}
|
||||
else if (path1EndsWithSeparator || path2StartsWithSeparator) {
|
||||
return path1 + path2;
|
||||
}
|
||||
else {
|
||||
return path1 + this.pathSeparator + path2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a full path, returns a {@link Comparator} suitable for sorting patterns in order of
|
||||
* explicitness.
|
||||
* <p>This{@code Comparator} will {@linkplain java.util.Collections#sort(List, 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:
|
||||
* <ol>
|
||||
* <li>{@code /hotels/new}</li>
|
||||
* <li>{@code /hotels/{hotel}}</li> <li>{@code /hotels/*}</li>
|
||||
* </ol>
|
||||
* the returned comparator will sort this list so that the order will be as indicated.
|
||||
* <p>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
|
||||
*/
|
||||
@Override
|
||||
public Comparator<String> getPatternComparator(String path) {
|
||||
return new AntPatternComparator(path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests whether or not a string matches against a pattern via a {@link Pattern}.
|
||||
* <p>The pattern may contain special characters: '*' means zero or more characters; '?' means one and
|
||||
* only one character; '{' and '}' indicate a URI template pattern. For example <tt>/users/{user}</tt>.
|
||||
*/
|
||||
protected static class AntPathStringMatcher {
|
||||
|
||||
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
|
||||
|
||||
private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
private final List<String> variableNames = new LinkedList<String>();
|
||||
|
||||
public AntPathStringMatcher(String pattern) {
|
||||
this(pattern, true);
|
||||
}
|
||||
|
||||
public AntPathStringMatcher(String pattern, boolean caseSensitive) {
|
||||
StringBuilder patternBuilder = new StringBuilder();
|
||||
Matcher matcher = GLOB_PATTERN.matcher(pattern);
|
||||
int end = 0;
|
||||
while (matcher.find()) {
|
||||
patternBuilder.append(quote(pattern, end, matcher.start()));
|
||||
String match = matcher.group();
|
||||
if ("?".equals(match)) {
|
||||
patternBuilder.append('.');
|
||||
}
|
||||
else if ("*".equals(match)) {
|
||||
patternBuilder.append(".*");
|
||||
}
|
||||
else if (match.startsWith("{") && match.endsWith("}")) {
|
||||
int colonIdx = match.indexOf(':');
|
||||
if (colonIdx == -1) {
|
||||
patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
|
||||
this.variableNames.add(matcher.group(1));
|
||||
}
|
||||
else {
|
||||
String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
|
||||
patternBuilder.append('(');
|
||||
patternBuilder.append(variablePattern);
|
||||
patternBuilder.append(')');
|
||||
String variableName = match.substring(1, colonIdx);
|
||||
this.variableNames.add(variableName);
|
||||
}
|
||||
}
|
||||
end = matcher.end();
|
||||
}
|
||||
patternBuilder.append(quote(pattern, end, pattern.length()));
|
||||
this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
|
||||
Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
|
||||
private String quote(String s, int start, int end) {
|
||||
if (start == end) {
|
||||
return "";
|
||||
}
|
||||
return Pattern.quote(s.substring(start, end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point.
|
||||
* @return {@code true} if the string matches against the pattern, or {@code false} otherwise.
|
||||
*/
|
||||
public boolean matchStrings(String str, Map<String, String> uriTemplateVariables) {
|
||||
Matcher matcher = this.pattern.matcher(str);
|
||||
if (matcher.matches()) {
|
||||
if (uriTemplateVariables != null) {
|
||||
// SPR-8455
|
||||
if (this.variableNames.size() != matcher.groupCount()) {
|
||||
throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
|
||||
this.pattern + " does not match the number of URI template variables it defines, " +
|
||||
"which can occur if capturing groups are used in a URI template regex. " +
|
||||
"Use non-capturing groups instead.");
|
||||
}
|
||||
for (int i = 1; i <= matcher.groupCount(); i++) {
|
||||
String name = this.variableNames.get(i - 1);
|
||||
String value = matcher.group(i);
|
||||
uriTemplateVariables.put(name, value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The default {@link Comparator} implementation returned by
|
||||
* {@link #getPatternComparator(String)}.
|
||||
* <p>In order, the most "generic" pattern is determined by the following:
|
||||
* <ul>
|
||||
* <li>if it's null or a capture all pattern (i.e. it is equal to "/**")</li>
|
||||
* <li>if the other pattern is an actual match</li>
|
||||
* <li>if it's a catch-all pattern (i.e. it ends with "**"</li>
|
||||
* <li>if it's got more "*" than the other pattern</li>
|
||||
* <li>if it's got more "{foo}" than the other pattern</li>
|
||||
* <li>if it's shorter than the other pattern</li>
|
||||
* </ul>
|
||||
*/
|
||||
protected static class AntPatternComparator implements Comparator<String> {
|
||||
|
||||
private final String path;
|
||||
|
||||
public AntPatternComparator(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two patterns to determine which should match first, i.e. which
|
||||
* is the most specific regarding the current path.
|
||||
* @return a negative integer, zero, or a positive integer as pattern1 is
|
||||
* more specific, equally specific, or less specific than pattern2.
|
||||
*/
|
||||
@Override
|
||||
public int compare(String pattern1, String pattern2) {
|
||||
PatternInfo info1 = new PatternInfo(pattern1);
|
||||
PatternInfo info2 = new PatternInfo(pattern2);
|
||||
|
||||
if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
|
||||
return 0;
|
||||
}
|
||||
else if (info1.isLeastSpecific()) {
|
||||
return 1;
|
||||
}
|
||||
else if (info2.isLeastSpecific()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean pattern1EqualsPath = pattern1.equals(path);
|
||||
boolean pattern2EqualsPath = pattern2.equals(path);
|
||||
if (pattern1EqualsPath && pattern2EqualsPath) {
|
||||
return 0;
|
||||
}
|
||||
else if (pattern1EqualsPath) {
|
||||
return -1;
|
||||
}
|
||||
else if (pattern2EqualsPath) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
|
||||
return 1;
|
||||
}
|
||||
else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (info1.getTotalCount() != info2.getTotalCount()) {
|
||||
return info1.getTotalCount() - info2.getTotalCount();
|
||||
}
|
||||
|
||||
if (info1.getLength() != info2.getLength()) {
|
||||
return info2.getLength() - info1.getLength();
|
||||
}
|
||||
|
||||
if (info1.getSingleWildcards() < info2.getSingleWildcards()) {
|
||||
return -1;
|
||||
}
|
||||
else if (info2.getSingleWildcards() < info1.getSingleWildcards()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (info1.getUriVars() < info2.getUriVars()) {
|
||||
return -1;
|
||||
}
|
||||
else if (info2.getUriVars() < info1.getUriVars()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Value class that holds information about the pattern, e.g. number of
|
||||
* occurrences of "*", "**", and "{" pattern elements.
|
||||
*/
|
||||
private static class PatternInfo {
|
||||
|
||||
private final String pattern;
|
||||
|
||||
private int uriVars;
|
||||
|
||||
private int singleWildcards;
|
||||
|
||||
private int doubleWildcards;
|
||||
|
||||
private boolean catchAllPattern;
|
||||
|
||||
private boolean prefixPattern;
|
||||
|
||||
private Integer length;
|
||||
|
||||
public PatternInfo(String pattern) {
|
||||
this.pattern = pattern;
|
||||
if (this.pattern != null) {
|
||||
initCounters();
|
||||
this.catchAllPattern = this.pattern.equals("/**");
|
||||
this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**");
|
||||
}
|
||||
if (this.uriVars == 0) {
|
||||
this.length = (this.pattern != null ? this.pattern.length() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initCounters() {
|
||||
int pos = 0;
|
||||
while (pos < this.pattern.length()) {
|
||||
if (this.pattern.charAt(pos) == '{') {
|
||||
this.uriVars++;
|
||||
pos++;
|
||||
}
|
||||
else if (this.pattern.charAt(pos) == '*') {
|
||||
if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
|
||||
this.doubleWildcards++;
|
||||
pos += 2;
|
||||
}
|
||||
else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) {
|
||||
this.singleWildcards++;
|
||||
pos++;
|
||||
}
|
||||
else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getUriVars() {
|
||||
return this.uriVars;
|
||||
}
|
||||
|
||||
public int getSingleWildcards() {
|
||||
return this.singleWildcards;
|
||||
}
|
||||
|
||||
public int getDoubleWildcards() {
|
||||
return this.doubleWildcards;
|
||||
}
|
||||
|
||||
public boolean isLeastSpecific() {
|
||||
return (this.pattern == null || this.catchAllPattern);
|
||||
}
|
||||
|
||||
public boolean isPrefixPattern() {
|
||||
return this.prefixPattern;
|
||||
}
|
||||
|
||||
public int getTotalCount() {
|
||||
return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the given pattern, where template variables are considered to be 1 long.
|
||||
*/
|
||||
public int getLength() {
|
||||
if (this.length == null) {
|
||||
this.length = VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length();
|
||||
}
|
||||
return this.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A simple cache for patterns that depend on the configured path separator.
|
||||
*/
|
||||
private static class PathSeparatorPatternCache {
|
||||
|
||||
private final String endsOnWildCard;
|
||||
|
||||
private final String endsOnDoubleWildCard;
|
||||
|
||||
public PathSeparatorPatternCache(String pathSeparator) {
|
||||
this.endsOnWildCard = pathSeparator + "*";
|
||||
this.endsOnDoubleWildCard = pathSeparator + "**";
|
||||
}
|
||||
|
||||
public String getEndsOnWildCard() {
|
||||
return this.endsOnWildCard;
|
||||
}
|
||||
|
||||
public String getEndsOnDoubleWildCard() {
|
||||
return this.endsOnDoubleWildCard;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,11 @@ import fun.asgc.neutrino.core.web.annotation.RequestBody;
|
||||
import fun.asgc.neutrino.core.web.annotation.RequestParam;
|
||||
import fun.asgc.neutrino.core.web.context.HttpContextHolder;
|
||||
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
|
||||
import fun.asgc.neutrino.core.web.context.WebContextHolder;
|
||||
import fun.asgc.neutrino.core.web.interceptor.HandlerInterceptor;
|
||||
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistration;
|
||||
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
|
||||
import fun.asgc.neutrino.core.web.interceptor.MappedInterceptor;
|
||||
import fun.asgc.neutrino.core.web.router.DefaultHttpRouter;
|
||||
import fun.asgc.neutrino.core.web.router.HttpRouteParam;
|
||||
import fun.asgc.neutrino.core.web.router.HttpRouteResult;
|
||||
@@ -46,7 +51,9 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -62,6 +69,7 @@ public class HttpRequestHandler {
|
||||
private ApplicationConfig applicationConfig;
|
||||
@Autowired
|
||||
private DefaultHttpRouter defaultHttpRouter;
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
public void handle() {
|
||||
ChannelHandlerContext context = HttpContextHolder.getChannelHandlerContext();
|
||||
@@ -77,6 +85,10 @@ public class HttpRequestHandler {
|
||||
return;
|
||||
}
|
||||
if (HttpRouterType.METHOD == httpRouteResult.getType()) {
|
||||
if (!preHandle(context, requestParser, httpRouteResult.getPageRoute(), httpRouteResult.getMethod())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object invokeResult = invoke(httpRouteResult.getInstance(), httpRouteResult.getMethod());
|
||||
String res = String.valueOf(invokeResult);
|
||||
if (null != invokeResult && !TypeUtil.isNormalBasicType(invokeResult.getClass())) {
|
||||
@@ -87,6 +99,10 @@ public class HttpRequestHandler {
|
||||
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
} else if(HttpRouterType.PAGE == httpRouteResult.getType()) {
|
||||
if (!preHandle(context, requestParser, httpRouteResult.getPageRoute(), null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 前端页面
|
||||
String mimeType = MimeType.getMimeType(MimeType.parseSuffix(httpRouteResult.getPageLocation()));
|
||||
if (mimeType.startsWith("text/")) {
|
||||
@@ -110,6 +126,37 @@ public class HttpRequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean preHandle(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod) throws Exception {
|
||||
List<HandlerInterceptor> list = getInterceptorsForPath(route);
|
||||
if (!CollectionUtil.isEmpty(list)) {
|
||||
for (HandlerInterceptor handlerInterceptor : list) {
|
||||
if (!handlerInterceptor.preHandle(context, requestParser, route, targetMethod)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
private List<HandlerInterceptor> getInterceptorsForPath(String lookupPath) {
|
||||
List<HandlerInterceptor> result = new ArrayList<HandlerInterceptor>();
|
||||
InterceptorRegistry interceptorRegistry = WebContextHolder.getInterceptorRegistry();
|
||||
if (CollectionUtil.isEmpty(interceptorRegistry.getInterceptors())) {
|
||||
return result;
|
||||
}
|
||||
for (Object interceptor : interceptorRegistry.getInterceptors()) {
|
||||
if (interceptor instanceof HandlerInterceptor) {
|
||||
result.add((HandlerInterceptor) interceptor);
|
||||
} else if(interceptor instanceof MappedInterceptor) {
|
||||
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
|
||||
if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
|
||||
result.add(mappedInterceptor.getInterceptor());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void exceptionHandler(Throwable e) {
|
||||
log.error("Http处理异常", e);
|
||||
HttpServerUtil.send500Response(HttpContextHolder.getChannelHandlerContext(), e);
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public interface PathMatcher {
|
||||
/**
|
||||
* Does the given {@code path} represent a pattern that can be matched
|
||||
* by an implementation of this interface?
|
||||
* <p>If the return value is {@code false}, then the {@link #match}
|
||||
* method does not have to be used because direct equality comparisons
|
||||
* on the static path Strings will lead to the same result.
|
||||
* @param path the path String to check
|
||||
* @return {@code true} if the given {@code path} represents a pattern
|
||||
*/
|
||||
boolean isPattern(String path);
|
||||
|
||||
/**
|
||||
* Match the given {@code path} against the given {@code pattern},
|
||||
* according to this PathMatcher's matching strategy.
|
||||
* @param pattern the pattern to match against
|
||||
* @param path the path String to test
|
||||
* @return {@code true} if the supplied {@code path} matched,
|
||||
* {@code false} if it didn't
|
||||
*/
|
||||
boolean match(String pattern, String path);
|
||||
|
||||
/**
|
||||
* Match the given {@code path} against the corresponding part of the given
|
||||
* {@code pattern}, according to this PathMatcher's matching strategy.
|
||||
* <p>Determines whether the pattern at least matches as far as the given base
|
||||
* path goes, assuming that a full path may then match as well.
|
||||
* @param pattern the pattern to match against
|
||||
* @param path the path String to test
|
||||
* @return {@code true} if the supplied {@code path} matched,
|
||||
* {@code false} if it didn't
|
||||
*/
|
||||
boolean matchStart(String pattern, String path);
|
||||
|
||||
/**
|
||||
* Given a pattern and a full path, determine the pattern-mapped part.
|
||||
* <p>This method is supposed to find out which part of the path is matched
|
||||
* dynamically through an actual pattern, that is, it strips off a statically
|
||||
* defined leading path from the given full path, returning only the actually
|
||||
* pattern-matched part of the path.
|
||||
* <p>For example: For "myroot/*.html" as pattern and "myroot/myfile.html"
|
||||
* as full path, this method should return "myfile.html". The detailed
|
||||
* determination rules are specified to this PathMatcher's matching strategy.
|
||||
* <p>A simple implementation may return the given full path as-is in case
|
||||
* of an actual pattern, and the empty String in case of the pattern not
|
||||
* containing any dynamic parts (i.e. the {@code pattern} parameter being
|
||||
* a static path that wouldn't qualify as an actual {@link #isPattern pattern}).
|
||||
* A sophisticated implementation will differentiate between the static parts
|
||||
* and the dynamic parts of the given path pattern.
|
||||
* @param pattern the path pattern
|
||||
* @param path the full path to introspect
|
||||
* @return the pattern-mapped part of the given {@code path}
|
||||
* (never {@code null})
|
||||
*/
|
||||
String extractPathWithinPattern(String pattern, String path);
|
||||
|
||||
/**
|
||||
* Given a pattern and a full path, extract the URI template variables. URI template
|
||||
* variables are expressed through curly brackets ('{' and '}').
|
||||
* <p>For example: For pattern "/hotels/{hotel}" and path "/hotels/1", this method will
|
||||
* return a map containing "hotel"->"1".
|
||||
* @param pattern the path pattern, possibly containing URI templates
|
||||
* @param path the full path to extract template variables from
|
||||
* @return a map, containing variable names as keys; variables values as values
|
||||
*/
|
||||
Map<String, String> extractUriTemplateVariables(String pattern, String path);
|
||||
|
||||
/**
|
||||
* Given a full path, returns a {@link Comparator} suitable for sorting patterns
|
||||
* in order of explicitness for that path.
|
||||
* <p>The full algorithm used depends on the underlying implementation, but generally,
|
||||
* the returned {@code Comparator} will
|
||||
* {@linkplain java.util.Collections#sort(java.util.List, java.util.Comparator) sort}
|
||||
* a list so that more specific patterns come before generic patterns.
|
||||
* @param path the full path to use for comparison
|
||||
* @return a comparator capable of sorting patterns in order of explicitness
|
||||
*/
|
||||
Comparator<String> getPatternComparator(String path);
|
||||
|
||||
/**
|
||||
* Combines two patterns into a new pattern that is returned.
|
||||
* <p>The full algorithm used for combining the two pattern depends on the underlying implementation.
|
||||
* @param pattern1 the first pattern
|
||||
* @param pattern2 the second pattern
|
||||
* @return the combination of the two patterns
|
||||
* @throws IllegalArgumentException when the two patterns cannot be combined
|
||||
*/
|
||||
String combine(String pattern1, String pattern2);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.config;
|
||||
|
||||
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public interface WebMvcConfigurer {
|
||||
|
||||
default void addInterceptors(InterceptorRegistry registry) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.context;
|
||||
|
||||
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
|
||||
|
||||
@@ -172,6 +172,10 @@ public class HttpRequestParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
public FullHttpRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public String getHeaderValue(String name) {
|
||||
return request.headers().get(name);
|
||||
}
|
||||
@@ -203,6 +207,9 @@ public class HttpRequestParser {
|
||||
|
||||
private String getRoutePath(String url) {
|
||||
String httpContextPath = WebContextHolder.getHttpContextPath();
|
||||
if (null == url || url.length() < httpContextPath.length()) {
|
||||
return url;
|
||||
}
|
||||
if (StringUtil.isEmpty(httpContextPath)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import fun.asgc.neutrino.core.bean.SimpleBeanFactory;
|
||||
import fun.asgc.neutrino.core.context.ApplicationConfig;
|
||||
import fun.asgc.neutrino.core.util.*;
|
||||
import fun.asgc.neutrino.core.web.annotation.RestController;
|
||||
import fun.asgc.neutrino.core.web.config.WebMvcConfigurer;
|
||||
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -48,6 +50,7 @@ public class WebContextHolder {
|
||||
private static volatile Long maxContentLength;
|
||||
private static volatile Integer port;
|
||||
private static List<BeanWrapper> controllerBeanWrapperList;
|
||||
private static volatile InterceptorRegistry interceptorRegistry;
|
||||
@Autowired
|
||||
private ApplicationConfig applicationConfig;
|
||||
@Autowired
|
||||
@@ -60,6 +63,7 @@ public class WebContextHolder {
|
||||
initFavicon();
|
||||
initMaxContentLength();
|
||||
initControllerBeanWrapperList();
|
||||
initInterceptorRegistry();
|
||||
port = http.getPort();
|
||||
}
|
||||
|
||||
@@ -120,7 +124,24 @@ public class WebContextHolder {
|
||||
.filter(beanWrapper -> beanWrapper.getType().isAnnotationPresent(RestController.class)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void initInterceptorRegistry() {
|
||||
interceptorRegistry = new InterceptorRegistry();
|
||||
List<BeanWrapper> beanWrapperList = applicationBeanFactory.beanWrapperList();
|
||||
if (CollectionUtil.isEmpty(beanWrapperList)) {
|
||||
return;
|
||||
}
|
||||
List<WebMvcConfigurer> webMvcConfigurerList = applicationBeanFactory.getBeanList(WebMvcConfigurer.class);
|
||||
if (CollectionUtil.isEmpty(webMvcConfigurerList)) {
|
||||
return;
|
||||
}
|
||||
webMvcConfigurerList.forEach(webMvcConfigurer -> webMvcConfigurer.addInterceptors(interceptorRegistry));
|
||||
}
|
||||
|
||||
public static List<BeanWrapper> getControllerBeanWrapperList() {
|
||||
return controllerBeanWrapperList;
|
||||
}
|
||||
|
||||
public static InterceptorRegistry getInterceptorRegistry() {
|
||||
return interceptorRegistry;
|
||||
}
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.interceptor;
|
||||
|
||||
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public interface HandlerInterceptor {
|
||||
default boolean preHandle(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod) throws Exception {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void postHandle(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod) throws Exception {
|
||||
}
|
||||
|
||||
default void afterCompletion(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod, Throwable e) throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.interceptor;
|
||||
|
||||
import fun.asgc.neutrino.core.base.Ordered;
|
||||
import fun.asgc.neutrino.core.util.Assert;
|
||||
import fun.asgc.neutrino.core.util.StringUtil;
|
||||
import fun.asgc.neutrino.core.web.PathMatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public class InterceptorRegistration {
|
||||
private final HandlerInterceptor interceptor;
|
||||
private final List<String> includePatterns = new ArrayList<>();
|
||||
private final List<String> excludePatterns = new ArrayList<>();
|
||||
private PathMatcher pathMatcher;
|
||||
private int order = 0;
|
||||
|
||||
public InterceptorRegistration(HandlerInterceptor interceptor) {
|
||||
Assert.notNull(interceptor, "Interceptor is required");
|
||||
this.interceptor = interceptor;
|
||||
}
|
||||
|
||||
public InterceptorRegistration addPathPatterns(String... patterns) {
|
||||
return this.addPathPatterns(Arrays.asList(patterns));
|
||||
}
|
||||
|
||||
public InterceptorRegistration addPathPatterns(List<String> patterns) {
|
||||
this.includePatterns.addAll(patterns);
|
||||
return this;
|
||||
}
|
||||
|
||||
public InterceptorRegistration excludePathPatterns(String... patterns) {
|
||||
return this.excludePathPatterns(Arrays.asList(patterns));
|
||||
}
|
||||
|
||||
public InterceptorRegistration excludePathPatterns(List<String> patterns) {
|
||||
this.excludePatterns.addAll(patterns);
|
||||
return this;
|
||||
}
|
||||
|
||||
public InterceptorRegistration pathMatcher(PathMatcher pathMatcher) {
|
||||
this.pathMatcher = pathMatcher;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InterceptorRegistration order(int order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public Object getInterceptor() {
|
||||
if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) {
|
||||
return this.interceptor;
|
||||
} else {
|
||||
String[] include = StringUtil.toStringArray(this.includePatterns);
|
||||
String[] exclude = StringUtil.toStringArray(this.excludePatterns);
|
||||
MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor);
|
||||
if (this.pathMatcher != null) {
|
||||
mappedInterceptor.setPathMatcher(this.pathMatcher);
|
||||
}
|
||||
|
||||
return mappedInterceptor;
|
||||
}
|
||||
}
|
||||
|
||||
Ordered toOrdered() {
|
||||
return new Ordered() {
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.interceptor;
|
||||
|
||||
import fun.asgc.neutrino.core.base.OrderComparator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public class InterceptorRegistry {
|
||||
|
||||
private final List<InterceptorRegistration> registrations = new ArrayList<InterceptorRegistration>();
|
||||
|
||||
public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
|
||||
InterceptorRegistration registration = new InterceptorRegistration(interceptor);
|
||||
this.registrations.add(registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all registered interceptors.
|
||||
*/
|
||||
public List<Object> getInterceptors() {
|
||||
Collections.sort(this.registrations, INTERCEPTOR_ORDER_COMPARATOR);
|
||||
List<Object> result = new ArrayList<Object>(this.registrations.size());
|
||||
for (InterceptorRegistration registration : this.registrations) {
|
||||
result.add(registration.getInterceptor());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static final Comparator<Object> INTERCEPTOR_ORDER_COMPARATOR =
|
||||
OrderComparator.INSTANCE.withSourceProvider(new OrderComparator.OrderSourceProvider() {
|
||||
@Override
|
||||
public Object getOrderSource(final Object object) {
|
||||
if (object instanceof InterceptorRegistration) {
|
||||
return ((InterceptorRegistration) object).toOrdered();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.interceptor;
|
||||
|
||||
import fun.asgc.neutrino.core.web.PathMatcher;
|
||||
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/27
|
||||
*/
|
||||
public class MappedInterceptor {
|
||||
private final String[] includePatterns;
|
||||
private final String[] excludePatterns;
|
||||
private final HandlerInterceptor interceptor;
|
||||
private PathMatcher pathMatcher;
|
||||
|
||||
public MappedInterceptor(String[] includePatterns, HandlerInterceptor interceptor) {
|
||||
this(includePatterns, null, (HandlerInterceptor)interceptor);
|
||||
}
|
||||
|
||||
public MappedInterceptor(String[] includePatterns, String[] excludePatterns, HandlerInterceptor interceptor) {
|
||||
this.includePatterns = includePatterns;
|
||||
this.excludePatterns = excludePatterns;
|
||||
this.interceptor = interceptor;
|
||||
}
|
||||
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
this.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
public PathMatcher getPathMatcher() {
|
||||
return this.pathMatcher;
|
||||
}
|
||||
|
||||
public String[] getPathPatterns() {
|
||||
return this.includePatterns;
|
||||
}
|
||||
|
||||
public HandlerInterceptor getInterceptor() {
|
||||
return this.interceptor;
|
||||
}
|
||||
|
||||
public boolean matches(String lookupPath, PathMatcher pathMatcher) {
|
||||
PathMatcher pathMatcherToUse = this.pathMatcher != null ? this.pathMatcher : pathMatcher;
|
||||
String[] var4;
|
||||
int var5;
|
||||
int var6;
|
||||
String pattern;
|
||||
if (!ObjectUtils.isEmpty(this.excludePatterns)) {
|
||||
var4 = this.excludePatterns;
|
||||
var5 = var4.length;
|
||||
|
||||
for(var6 = 0; var6 < var5; ++var6) {
|
||||
pattern = var4[var6];
|
||||
if (pathMatcherToUse.match(pattern, lookupPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(this.includePatterns)) {
|
||||
return true;
|
||||
} else {
|
||||
var4 = this.includePatterns;
|
||||
var5 = var4.length;
|
||||
|
||||
for(var6 = 0; var6 < var5; ++var6) {
|
||||
pattern = var4[var6];
|
||||
if (pathMatcherToUse.match(pattern, lookupPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean preHandle(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod) throws Exception {
|
||||
return this.interceptor.preHandle(context, requestParser, route, targetMethod);
|
||||
}
|
||||
|
||||
public void postHandle(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod) throws Exception {
|
||||
this.interceptor.postHandle(context, requestParser, route, targetMethod);
|
||||
}
|
||||
|
||||
public void afterCompletion(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod, Throwable e) throws Exception {
|
||||
this.interceptor.afterCompletion(context, requestParser, route, targetMethod, e);
|
||||
}
|
||||
}
|
||||
+11
-3
@@ -200,18 +200,24 @@ public class DefaultHttpRouter implements HttpRouter {
|
||||
.setType(HttpRouterType.METHOD)
|
||||
.setMethod(method)
|
||||
.setBeanIdentity(beanWrapper.getIdentity())
|
||||
.setPageRoute(path)
|
||||
);
|
||||
}
|
||||
|
||||
private synchronized void addRoute(String path, String pageLocation) {
|
||||
private synchronized void addRoute(String path, String pageLocation, String pageRoute) {
|
||||
log.info("addRoute path:{} file:{}", path, pageLocation);
|
||||
HttpRouteIdentity identity = new HttpRouteIdentity(HttpMethod.GET, path);
|
||||
if (routeCache.containsKey(identity)) {
|
||||
return;
|
||||
}
|
||||
pageRoute = pageRoute.replaceAll("\\\\\\\\", "\\\\");
|
||||
if (pageRoute.equals("/")) {
|
||||
pageRoute += "index.html";
|
||||
}
|
||||
routeCache.put(identity, new HttpRouteInfo()
|
||||
.setType(HttpRouterType.PAGE)
|
||||
.setPageLocation(pageLocation)
|
||||
.setPageRoute(pageRoute)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -230,7 +236,7 @@ public class DefaultHttpRouter implements HttpRouter {
|
||||
}
|
||||
try (InputStream in = FileUtil.getInputStream(path)){
|
||||
if (null != in) {
|
||||
addRoute(httpRouteParam.getUrl(), path);
|
||||
addRoute(httpRouteParam.getUrl(), path, httpRouteParam.getUrl());
|
||||
httpRouteInfo = routeCache.get(identity);
|
||||
break;
|
||||
}
|
||||
@@ -247,11 +253,13 @@ public class DefaultHttpRouter implements HttpRouter {
|
||||
if (HttpRouterType.PAGE == httpRouteInfo.getType()) {
|
||||
return new HttpRouteResult()
|
||||
.setType(httpRouteInfo.getType())
|
||||
.setPageLocation(httpRouteInfo.getPageLocation());
|
||||
.setPageLocation(httpRouteInfo.getPageLocation())
|
||||
.setPageRoute(httpRouteInfo.getPageRoute());
|
||||
}
|
||||
return new HttpRouteResult()
|
||||
.setType(httpRouteInfo.getType())
|
||||
.setMethod(httpRouteInfo.getMethod())
|
||||
.setPageRoute(httpRouteInfo.getPageRoute())
|
||||
.setInstance(BeanManager.getBean(httpRouteInfo.getBeanIdentity()));
|
||||
}
|
||||
|
||||
|
||||
@@ -51,4 +51,8 @@ public class HttpRouteInfo {
|
||||
* 页面位置
|
||||
*/
|
||||
private String pageLocation;
|
||||
/**
|
||||
* 页面路由
|
||||
*/
|
||||
private String pageRoute;
|
||||
}
|
||||
|
||||
@@ -50,4 +50,8 @@ public class HttpRouteResult {
|
||||
* 页面位置
|
||||
*/
|
||||
private String pageLocation;
|
||||
/**
|
||||
* 页面路由
|
||||
*/
|
||||
private String pageRoute;
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.interceptor;
|
||||
|
||||
import fun.asgc.neutrino.core.web.AntPathMatcher;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/28
|
||||
*/
|
||||
public class InterceptorRegistryTest {
|
||||
|
||||
@Test
|
||||
public void test1() {
|
||||
AntPathMatcher antPathMatcher = new AntPathMatcher();
|
||||
System.out.println(antPathMatcher.match("/a/*", "/a/11"));
|
||||
System.out.println(antPathMatcher.match("/a/*", "/a/11/22"));
|
||||
System.out.println(antPathMatcher.match("/a/**", "/a/11/22"));
|
||||
System.out.println(antPathMatcher.match("/a/*.html", "/a/11/22"));
|
||||
System.out.println(antPathMatcher.match("/a/*.html", "/a/11.html"));
|
||||
System.out.println(antPathMatcher.match("/a/*.html", "/a/11/a.html"));
|
||||
System.out.println(antPathMatcher.match("/a/**/*.html", "/a/11/a.html"));
|
||||
System.out.println(antPathMatcher.match("/a/**/*.html", "/a/11/22/33/a.html"));
|
||||
System.out.println(antPathMatcher.match("/", "/a/11/22/33/a.html"));
|
||||
System.out.println(antPathMatcher.match("/**", "/a/11/22/33/a.html"));
|
||||
System.out.println(antPathMatcher.match("*.html", "/a/11/22/33/a.html"));
|
||||
System.out.println(antPathMatcher.match("**.html", "/a/11/22/33/a.html"));
|
||||
System.out.println(antPathMatcher.match("**/**.html", "/a/11/22/33/a.html"));
|
||||
System.out.println(antPathMatcher.match("/**/*.html", "/a/11/22/33/a.html"));
|
||||
System.out.println(antPathMatcher.match("/**/*.html", "/a.html"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.test1;
|
||||
|
||||
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
|
||||
import fun.asgc.neutrino.core.web.interceptor.HandlerInterceptor;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/28
|
||||
*/
|
||||
@Slf4j
|
||||
public class BaseAuthInterceptor implements HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* 登录验证
|
||||
* @param context
|
||||
* @param requestParser
|
||||
* @param route
|
||||
* @param targetMethod
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod) throws Exception {
|
||||
// 登录验证
|
||||
String authorize = requestParser.getHeaderValue("Authorize");
|
||||
if (!"123abc".equals(authorize)) {
|
||||
throw new UserNotLoginException();
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod, Throwable e) throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.test1;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/28
|
||||
*/
|
||||
public class UserNotLoginException extends Exception {
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2022 aoshiguchen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fun.asgc.neutrino.core.web.test1;
|
||||
|
||||
import fun.asgc.neutrino.core.annotation.Configuration;
|
||||
import fun.asgc.neutrino.core.web.config.WebMvcConfigurer;
|
||||
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/28
|
||||
*/
|
||||
@Configuration
|
||||
public class WebConfigurerAdapter implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new BaseAuthInterceptor())
|
||||
.addPathPatterns("/**").excludePathPatterns("/**/*.html", "/**/*.js", "/**/*.ico");
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ neutrino:
|
||||
application:
|
||||
name: neutrino-proxy-server
|
||||
http:
|
||||
enable: false
|
||||
enable: true
|
||||
port: 8080
|
||||
context-path: /test
|
||||
max-content-length-desc: 128K
|
||||
|
||||
Reference in New Issue
Block a user