2010-11-10 15 views
22

Tôi có ánh xạ chú thích hoạt động tốt thông qua ứng dụng web mvc mùa xuân của tôi, tuy nhiên, chúng phân biệt chữ hoa chữ thường. Tôi không thể tìm cách làm cho chúng trở nên không phân biệt chữ hoa chữ thường. (Tôi muốn thực hiện điều này xảy ra trong Spring MVC, thay vì chuyển hướng lưu lượng truy cập bằng cách nào đó)Làm thế nào tôi có thể có các URL không phân biệt dạng chữ trong Spring MVC với ánh xạ có chú thích

+0

Ngoài ra, thêm thẻ 'Java' nó sẽ mang lại cho bạn nhiều lượt xem trang thường có nghĩa là nhiều câu trả lời hơn. –

+0

câu hỏi tương tự với câu trả lời chi tiết về vấn đề này tôi đã hỏi sau khi nhìn thấy câu hỏi này. http://stackoverflow.com/questions/12684183/case-insensitive-mapping-for-spring-mvc-requestmapping-annotations/12732550 –

Trả lời

0

Vâng, tôi không thể trả lời câu hỏi của bạn (tôi đã thử, tôi nghĩ mình có thể tìm ra). Nhưng nhìn thấy như bạn vẫn chưa nhận được bất kỳ phản ứng trong 2 ngày, sau đây là một số dẫn ít nhất:

Ví dụ này dường như cho thấy nó có thể:

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

Nó tham chiếu lớp này trong mùa xuân

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

Tôi đoán là bạn cần mở rộng <mvc:annotation-driven/> và triển khai từng hạt riêng với thông số chính xác e nó không phân biệt chữ hoa chữ thường. Xem:

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

Một lưu ý cuối cùng, tôi nhận thấy ở một nơi khác trong việc đọc mà nó nói rằng tất cả các đường dẫn mặc định để giảm trường hợp, có bạn xác minh rằng /MyPath không được xử lý bởi @RequestMapping("/mypath")?

Một lần nữa, chỉ cần thực phẩm cho suy nghĩ là tốt nhất tôi có thể làm. Có lẽ nó sẽ giúp bạn đủ xa để hỏi một câu hỏi cụ thể hơn dẫn bạn đến câu trả lời - đó là cách những điều này làm việc đôi khi. Chúc may mắn!

12

Theo this webpost bạn cần thêm cả HandlerMappingHandlerAdapter trong Spring MVC. Ánh xạ ánh xạ yêu cầu tới một bộ điều khiển tương ứng và bộ điều hợp có trách nhiệm thực hiện yêu cầu bằng bộ điều khiển.

Do đó, bạn cần ghi đè số PathMatcher cho cả người lập bản đồ và bộ điều hợp.

Ex (sẽ làm cho tất cả case-insensitive @Controllers):

New 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"/> 

gia tăng về tương tự mà < mvc:annotation-driven> sẽ làm. (Cám ơn David Parks cho liên kết)

+0

Làm việc cho tôi .. cảm ơn! – Rahul

+3

Cho Spring 3.1 replay AnnotationMethodHandlerAdapter với RequestMappingHandlerAdapter. Họ đang sử dụng lớp mới này trong 3.1. – smat

1

Vấn đề báo cáo cho solution by smat


Trong solution by smat, có một chút tác dụng phụ (tôi sẽ đổ lỗi cho mùa xuân-MVC cho điều đó).

Lúc đầu, AntPathMatcher.doMatch() có vẻ trả về true/false tùy thuộc vào chuỗi yêu cầu-url và chuỗi ánh xạ yêu cầu của bộ điều khiển (Đó là điều duy nhất nên được thực hiện tại đây).Nhưng, phương pháp này cũng được sử dụng cho một mục đích nữa (không được viết trong documentation!). Một mục đích khác là thu thập các giá trị tương ứng cho @PathVariable trong phương pháp điều khiển. Các giá trị này được thu thập trong Map<String, String> uriTemplateVariables (tham số cuối cùng) .Và các giá trị được thu thập này được sử dụng để chuyển sang phương thức điều khiển làm giá trị tham số.

Ví dụ, chúng tôi có điều khiển-phương pháp như thế này,

@RequestMapping("/code/{userCode}") 
public String getCode(@PathVariable("userCode") String userCode) { 
    System.out.println(userCode); 
} 

và nếu chúng ta tiếp cận với URL, /code/AbD sau đó với solution by smatAntPathMatcher.doMatch() sẽ thu thập @PathVariable giá trị trong Map<String, String> uriTemplateVariables như userCode->abd. Vì chúng ta sắp xếp chuỗi đường dẫn thấp hơn, các giá trị thu thập được cũng thấp hơn. Và giá trị này giá trị userCode thấp hơn được truyền cho bộ điều khiển của chúng tôi.

Nhưng, tôi biết ơn solution by smat mà phục vụ tốt cho tôi cho đến nay không có bất kỳ vấn đề khác.


Giải pháp


Giải Quyết vấn đề này bằng cách làm việc xung quanh solution by smat. Nếu không có con đường-vỏ thấp hơn hoặc chuỗi pattern trong mở rộng lớp AntPathMatcher, tôi buộc phải mở rộng của tôi AntPathMatcher sử dụng tùy chỉnh của tôi AntPathStringMatcher. tuỳ chỉnh AntPathStringMatcher của tôi phù hợp với trường hợp không phân biệt chữ hoa chữ thường mà không thay đổi trường hợp chuỗi thực.

Trong đoạn mã sau giải pháp hầu hết các mã được sao chép từ mã lớp gốc (mã Tôi muốn tùy chỉnh được ẩn cho lớp con vì tiếp cận tư nhân).

Tuỳ chỉnh 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); 
} 

}

Tuỳ chỉnh 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

Trong mùa xuân 3.2+/Spring Boot, bây giờ bạn có thể thiết lập trường hợp phù hợp với URL không nhạy cảm bằng cách sử dụng cấu hình Java đơn giản.

Trước tiên, bạn cần phải tạo ra các CaseInsensitivePathMatcher.groovy hoặc Java lớp:

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

Tiếp theo, để thực hiện điều này, bạn nên có một lớp chú thích với Springs @Configuration mở rộng lớp WebMvcConfigurerAdapter như hình dưới đây (Lưu ý rằng mã của tôi được chứa bên trong.lớp groovy):

@Configuration 
public class ApplicationConfig extends WebMvcConfigurerAdapter 

Sau đó, thêm 2 phương pháp sau để lớp:

/** 
* 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() 
    } 
} 


Vậy là xong, bây giờ bạn sẽ có tất cả các thiết lập đối với trường hợp URL không nhạy cảm với cấu hình tối thiểu

+0

Hai năm sau, nhưng vẫn ... 'WebMvcConfigurerAdapter' trong Mùa xuân 3.2 không có phương thức' configurePathMatch'. Phiên bản Spring tối thiểu không chính xác, có thể là 4,2 như được nêu trong các câu trả lời ở trên. Thông tin thêm về 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. Bạn có thể định cấu hình như sau:

@Configuration 
public class WebConfig extends WebMvcConfigurerAdapter { 
    @Override 
    public void configurePathMatch(PathMatchConfigurer configurer) { 
     AntPathMatcher matcher = new AntPathMatcher(); 
     matcher.setCaseSensitive(false); 
     configurer.setPathMatcher(matcher); 
    } 
} 
+0

Bạn có biết bất kỳ thông tin nào như vậy không cấu hình cho tham số truy vấn? – M22an

+0

@ M22an Xin lỗi, tôi không biết. Tôi nghĩ bạn có thể yêu cầu tính năng này tại https://jira.spring.io – npcode

1

Ví dụ từ tệp bean trong Spring 4.2 và điều này CHỈ ĐƯỢC H SUP TRỢ 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> 
Các vấn đề liên quan