2010-11-10 14 views
22

Tengo asignaciones anotadas que funcionan estupendamente 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 hacerlos insensibles a mayúsculas y minúsculas. (Me encantaría hacer que esto suceda dentro de Spring MVC, en lugar de redirigir el tráfico de alguna manera)¿Cómo puedo tener URLs insensibles a mayúsculas y minúsculas en Spring MVC con asignaciones anotadas?

+0

También, agregar la etiqueta 'Java' se le dió mucha más páginas vistas que por lo general significa más respuestas. –

+0

pregunta similar con respuesta detallada sobre este problema que he preguntado después de ver esta pregunta. http://stackoverflow.com/questions/12684183/case-insensitive-mapping-for-spring-mvc-requestmapping-annotations/12732550 –

Respuesta

0

Bueno, no puedo responder a su pregunta (lo intenté, pensé que podría resolverlo). Pero ya que usted no ha recibido ninguna respuesta en 2 días, aquí hay algunas pistas al menos:

Este ejemplo parece sugerir que es posible:

http://webcache.googleusercontent.com/search?q=cache:ELj-ZQ8G4z0J:www.springbyexample.org/examples/sdms-simple-spring-mvc-web-module.html+case+insensitive+requestmapping+spring&cd=3&hl=en&ct=clnk&client=firefox-a

que hace referencia esta clase en la primavera

http://static.springsource.org/spring/docs/3.0.4.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/support/ControllerClassNameHandlerMapping.html

Mi conjetura (y es sólo eso, una suposición), es que se necesita para expandir <mvc:annotation-driven/> y poner en práctica los granos individuales con los parámetros correctos para mak Es insensible a mayúsculas y minúsculas. Ver:

http://rapid-web.tumblr.com/post/296916668/what-does-annotation-driven-do

Una última nota, me di cuenta de algún otro lugar de la lectura que dice que todos los caminos por defecto a minúsculas, ¿ha verificado que /MyPath no es manejado por @RequestMapping("/mypath")?

Nuevamente, sólo pienso lo mejor que puedo hacer. Tal vez te lleve lo suficientemente lejos como para hacer una pregunta más específica que te lleve a la respuesta: así es como funcionan estas cosas a veces. ¡Buena suerte!

12

De acuerdo con this webpost necesita agregar un HandlerMapping y un HandlerAdapter en Spring MVC. El mapeo asigna la solicitud a un controlador correspondiente, y el adaptador es responsable de ejecutar la solicitud utilizando el controlador.

Por lo tanto, debe reemplazar el PathMatcher tanto para el asignador como para el adaptador.

Ex (hará que todos @Controllers mayúsculas y minúsculas):

Nueva Matcher:

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 aproximadamente la misma que < mvc:annotation-driven> haría hacer. (Gracias a David Parks para el enlace)

+0

Trabajó para mí ... ¡gracias! – Rahul

+3

para Spring 3.1 replade AnnotationMethodHandlerAdapter con RequestMappingHandlerAdapter. Están usando esta nueva clase en 3.1. – smat

1

Problema informe para solution by smat


En solution by smat, hay un pequeño efecto secundario (que se lo culpa de primavera-mvc para eso).

Al principio, AntPathMatcher.doMatch() parece devolver verdadero/falso en función de URL solicitada y la cadena de mapeo de solicitudes del método del controlador (Esto 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 documentation!). Otro objetivo es recopilar los valores correspondientes para @PathVariable en el método del controlador. Estos valores se recopilan en Map<String, String> uriTemplateVariables (último parámetro). Y estos valores recopilados se utilizan para pasar al método del controlador como valor del parámetro.

Por ejemplo, tenemos controlador-método 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 solution by smatAntPathMatcher.doMatch() a recoger @PathVariable valor en Map<String, String> uriTemplateVariables como userCode->abd. Como estamos realizando un encadenamiento inferior de la cadena de ruta, los valores recopilados también tienen una capa inferior. Y este valor de userCode de carcasa inferior se pasa a nuestro controlador.

Pero, estoy agradecido con solution by smat que me ha servido bien hasta ahora sin ningún otro problema.


Solución


resuelto este problema mediante el trabajo en torno solution by smat. Sin cadena de patrón inferior o cadena de patrón en la clase extendida AntPathMatcher, obligué a mi AntPathMatcher extendido a usar mi AntPathStringMatcher personalizado. mi AntPathStringMatcher personalizado hace la coincidencia case-insesitive sin cambiar el caso de la secuencia 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).

personalizada AntPathMatcher,

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); 
} 

}

personalizada AntPathStringMatcher,

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; 
    } 
} 
0

En primavera 3.2+/Primavera de arranque, ahora se puede configurar caso de coincidencia insensible URL usando la configuración de Java simplificada.

Lo primero que necesita para crear la clase CaseInsensitivePathMatcher.groovy o Java:

import org.springframework.util.AntPathMatcher 

class CaseInsensitivePathMatcher extends AntPathMatcher{ 

    @Override 
    protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) { 
     super.doMatch(pattern.toLowerCase(), path.toLowerCase(), fullMatch, uriTemplateVariables) 
    } 
} 

A continuación, para que esto suceda, usted debe tener una clase anotada con muelles @Configuration que amplía la clase WebMvcConfigurerAdapter como se muestra a continuación (Tenga en cuenta que mi código está dentro de.clases maravilloso):

@Configuration 
public class ApplicationConfig extends WebMvcConfigurerAdapter 

a continuación, añadir los 2 siguientes métodos para la clase:

/** 
* Creates a patchMatcher bean that matches case insensitively 
* @return PathMatcher 
*/ 
@Bean 
public PathMatcher pathMatcher() { 
    new CaseInsensitivePathMatcher() 
} 

/** 
* Overrides the configurePathMatch() method in WebMvcConfigurerAdapter 
* <br/>Allows us to set a custom path matcher, used by the MVC for @RequestMapping's 
    * @param configurer 
    */ 
    @Override 
    public void configurePathMatch(PathMatchConfigurer configurer) { 
     configurer.pathMatcher = pathMatcher() 
    } 
} 


Eso es todo, ahora debería ser toda la disposición para el caso de URL insensibles con una configuración mínima

+0

Dos años después, pero aún ... 'WebMvcConfigurerAdapter' en Spring 3.2 no tiene el método' configurePathMatch'. La versión mínima de Spring no es correcta, probablemente 4.2 como se indica en las respuestas anteriores. Más información en http://docs.spring.io/spring/docs/3.2.13.RELEASE/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.html – Alf

12

Spring 4.2 will support case-insensitive path matching. Puede 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); 
    } 
} 
+0

¿Conoces alguno? configuraciones para los parámetros de consulta? – M22an

+0

@ M22an Lo siento, no sé. Creo que puede solicitar la función en https://jira.spring.io – npcode

1

Ejemplo de un archivo de frijol en la primavera de 4,2 y esto sólo 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> 
Cuestiones relacionadas