He anotado asignaciones que funcionan muy bien a través de mi aplicación web spring mvc, sin embargo, distinguen entre mayúsculas y minúsculas. No puedo encontrar una manera de hacer que no distingan entre mayúsculas y minúsculas. (Me encantaría que esto suceda dentro de Spring MVC, en lugar de redirigir el tráfico de alguna manera)
¿Cómo puedo tener direcciones URL que no distinguen entre mayúsculas y minúsculas en Spring MVC con asignaciones anotadas?
dave
Spring 4.2 admitirá la coincidencia de rutas que no distingue entre mayúsculas y minúsculas. Puedes configurarlo de la siguiente manera:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
AntPathMatcher matcher = new AntPathMatcher();
matcher.setCaseSensitive(false);
configurer.setPathMatcher(matcher);
}
}
-
¿Conoce alguna configuración de este tipo para los parámetros de consulta?
– M22an
11 de noviembre de 2015 a las 12:16
-
@M22an Lo siento, no lo sé. Creo que puede solicitar la función en jira.spring.io
– código np
12 de noviembre de 2015 a las 3:45
-
En Spring Boot 2 o Spring 5, el
WebMvcConfigurerAdapter
es obsoleto. En su lugar, se debe implementar elWebMvcConfigurer
directamente.– Esquí alpino
30 de marzo de 2018 a las 12:37
-
Dos años después, pero aún…
WebMvcConfigurerAdapter
en Spring 3.2 no tiene el métodoconfigurePathMatch
. La versión mínima de Spring no es correcta, probablemente 4.2 como se indica en las respuestas anteriores. Más información sobre docs.spring.io/spring/docs/3.2.13.RELEASE/javadoc-api/org/…– alf
26 de abril de 2017 a las 11:40
-
Funciona bastante bien con Spring 3.2.17 y superior. Lo único que falta en el código anterior son varias declaraciones de “retorno” que se omiten por error. Puede encontrar una solución para Spring anterior a 3.2.17, en: newmiancode.blogspot.com/2010/01/…
– Gjera
3 de abril de 2019 a las 13:42
-
Gracias por el comentario @Gjera. tiene razón en que no hay declaraciones de devolución. Como mencioné en los ejemplos anteriores, mi código está escrito usando Groovy, lo que le permite omitir el ‘retorno’ y por defecto la última línea de código ejecutada en un método como declaración de retorno. Editaré la publicación para ponerla en ‘negrita’ para que se destaque más.
– pczeus
25/04/2019 a las 15:00
De acuerdo a esta publicación web necesita agregar ambos a Mapeo de controlador y un Adaptador de controlador en SpringMVC. La asignación asigna la solicitud a un controlador correspondiente y el adaptador es responsable de ejecutar la solicitud mediante el controlador.
Por lo tanto, debe anular el PathMatcher tanto para el mapeador como para el adaptador.
Ex (hará que todos los @Controllers no distingan entre mayúsculas y minúsculas):
Nuevo emparejador:
public class CaseInsenseticePathMatcher extends AntPathMatcher {
@Override
protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
System.err.println(pattern + " -- " + path);
return super.doMatch(pattern.toLowerCase(), path.toLowerCase(), fullMatch, uriTemplateVariables);
}
}
applicationContext.xml:
<bean id="matcher" class="test.CaseInsenseticePathMatcher"/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="pathMatcher" ref="matcher"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="pathMatcher" ref="matcher"/>
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"/>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="conversion-service" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"/>
Añadido casi lo mismo que <mvc:controlado por anotaciones> haría. (Gracias a David Parks por el enlace)
-
Para Spring 3.1, reemplace AnnotationMethodHandlerAdapter con RequestMappingHandlerAdapter. Están usando esta nueva clase en 3.1.
– smat
26 de septiembre de 2012 a las 12:52
Comunidad
Problema informe para solución por smat
En la solución de smat, hay un pequeño efecto secundario (culparía a spring-mvc por eso).
Primero, AntPathMatcher.doMatch()
parece devolver verdadero/falso según la URL solicitada y la cadena de asignación de solicitud del método del controlador (eso es lo único que se debe hacer aquí). Pero, este método también se usa para un propósito más (que no está escrito en documentación!). Otro propósito es recopilar los valores correspondientes para @PathVariable
en el método del controlador. Estos valores se recogen en Map<String, String> uriTemplateVariables
(último parámetro). Y estos valores recopilados se utilizan para pasar al método del controlador como valor de parámetro.
Por ejemplo, tenemos un método de controlador como este,
@RequestMapping("/code/{userCode}")
public String getCode(@PathVariable("userCode") String userCode) {
System.out.println(userCode);
}
y si accedemos con URL, /code/AbD
luego con solucion por smat AntPathMatcher.doMatch()
colectaré @PathVariable
valor en Map<String, String> uriTemplateVariables
como userCode->abd
. Como estamos en minúsculas en la cadena de ruta, los valores recopilados también están en minúsculas. Y esto el valor del código de usuario en minúsculas se pasa a nuestro controlador.
Pero estoy agradecido a la solución de smat que me sirvió bien hasta ahora sin ningún otro problema.
Solución
Resolvió este problema haciendo una solución alternativa por smat. Sin ruta de minúsculas o cadena de patrón en extendido AntPathMatcher
clase, forcé mi extendida AntPathMatcher
para usar mi costumbre AntPathStringMatcher
. mi costumbre AntPathStringMatcher
hace coincidencias entre mayúsculas y minúsculas sin cambiar el caso de la cadena real.
En el siguiente código de solución, la mayor parte del código se copia del código de clase original (el código que quería personalizar estaba oculto para la subclase debido al acceso privado).
AntPathMatcher personalizado,
public class CaseInsensitivePathMatcher extends AntPathMatcher {
private final Map<String, CaseInsensitiveAntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<String, CaseInsensitiveAntPathStringMatcher>();
/**
* Actually match the given <code>path</code> against the given
* <code>pattern</code>.
*
* @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</code> if the supplied <code>path</code> matched,
* <code>false</code> if it didn't
*/
protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
if (path.startsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR) != pattern.startsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR)) {
return false;
}
String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, AntPathMatcher.DEFAULT_PATH_SEPARATOR);
String[] pathDirs = StringUtils.tokenizeToStringArray(path, AntPathMatcher.DEFAULT_PATH_SEPARATOR);
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(AntPathMatcher.DEFAULT_PATH_SEPARATOR) ? path.endsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR) : !path
.endsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR));
}
if (!fullMatch) {
return true;
}
if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR)) {
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;
}
/**
* Tests whether or not a string matches against a pattern. The pattern may
* contain two special characters:<br>
* '*' means zero or more characters<br>
* '?' means one and only one character
*
* @param pattern
* pattern to match against. Must not be <code>null</code>.
* @param str
* string which must be matched against the pattern. Must not be
* <code>null</code>.
* @return <code>true</code> if the string matches against the pattern, or
* <code>false</code> otherwise.
*/
private boolean matchStrings(String pattern, String str, Map<String, String> uriTemplateVariables) {
CaseInsensitiveAntPathStringMatcher matcher = this.stringMatcherCache.get(pattern);
if (matcher == null) {
matcher = new CaseInsensitiveAntPathStringMatcher(pattern);
this.stringMatcherCache.put(pattern, matcher);
}
return matcher.matchStrings(str, uriTemplateVariables);
}
}
AntPathStringMatcher personalizado,
public class CaseInsensitiveAntPathStringMatcher {
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>();
/** Construct a new instance of the <code>AntPatchStringMatcher</code>. */
CaseInsensitiveAntPathStringMatcher(String pattern) {
this.pattern = createPattern(pattern);
}
private Pattern createPattern(String pattern) {
StringBuilder patternBuilder = new StringBuilder();
Matcher m = GLOB_PATTERN.matcher(pattern);
int end = 0;
while (m.find()) {
patternBuilder.append(quote(pattern, end, m.start()));
String match = m.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);
variableNames.add(m.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);
variableNames.add(variableName);
}
}
end = m.end();
}
patternBuilder.append(quote(pattern, end, pattern.length()));
return Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE); // this line is updated to create case-insensitive pattern object
}
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</code> if the string matches against the pattern, or <code>false</code> otherwise.
*/
public boolean matchStrings(String str, Map<String, String> uriTemplateVariables) {
Matcher matcher = pattern.matcher(str);
if (matcher.matches()) {
if (uriTemplateVariables != null) {
// SPR-8455
Assert.isTrue(variableNames.size() == matcher.groupCount(),
"The number of capturing groups in the pattern segment " + 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;
}
}
-
Para Spring 3.1, reemplace AnnotationMethodHandlerAdapter con RequestMappingHandlerAdapter. Están usando esta nueva clase en 3.1.
– smat
26 de septiembre de 2012 a las 12:52
shershon
Ejemplo de un archivo de bean en Spring 4.2 y esto SOLO ES COMPATIBLE con v4.2+:
<mvc:annotation-driven validator="validator">
<mvc:path-matching path-matcher="pathMatcher" />
</mvc:annotation-driven>
...
<!--Set endpoints case insensitive, spring is case-sensitive by default-->
<bean id="pathMatcher" class="org.springframework.util.AntPathMatcher">
<property name="caseSensitive" value="false" />
</bean>
Además, agregue la etiqueta ‘Java’, obtendrá muchas más visitas a la página, lo que generalmente significa más respuestas.
–David Parques
13 de noviembre de 2010 a las 9:09
pregunta similar con una respuesta detallada sobre este problema que hice después de ver esta pregunta. stackoverflow.com/questions/12684183/…
– Zahid Riaz
5 de octubre de 2012 a las 9:59