2010-01-16 22 views
14

Tôi đang sử dụng Spring MVC (3.0) với các bộ điều khiển theo chú thích. Tôi muốn tạo các URL REST-ful cho các tài nguyên và có thể yêu cầu không yêu cầu (nhưng vẫn tùy chọn cho phép) phần mở rộng tệp ở cuối URL (nhưng giả sử loại nội dung HTML nếu không có phần mở rộng). Tính năng này hoạt động hoàn toàn với Spring MVC miễn là không có dấu chấm (dấu chấm/toàn bộ) trong phần tên tệp.Cố gắng tạo các URL REST-ful có nhiều dấu chấm trong phần "tên tệp" - Spring 3.0 MVC

Tuy nhiên, một số URL của tôi yêu cầu số nhận dạng có dấu chấm trong tên. Ví dụ. như thế này:

http://company.com/widgets/123.456.789.500 

Trong trường hợp này, Spring tìm loại nội dung cho tiện ích .500 và không tìm thấy lỗi nào. Tôi có thể sử dụng công việc xung quanh như thêm .html vào cuối, mã hóa số nhận dạng hoặc thêm dấu gạch chéo. Tôi không hài lòng với bất kỳ điều gì nếu có nhưng có thể sống với việc thêm .html.

Tôi đã không tìm cách ghi đè cách phát hiện tiện ích mở rộng tệp mặc định trong Spring.

Có thể tùy chỉnh hoặc vô hiệu hóa phát hiện tiện ích mở rộng tệp cho một phương thức điều khiển hoặc mẫu URL nhất định không?

Trả lời

10

Kết hợp mẫu @PathVariable hơi bị giật khi nói đến dấu chấm trong URL (xem SPR-5778). Bạn có thể làm cho nó ít co giật (nhưng cầu kỳ hơn) và kiểm soát tốt hơn các URL có nhiều chấm, bằng cách đặt thuộc tính useDefaultSuffixPattern trên DefaultAnnotationHandlerMapping thành false.

Nếu bạn chưa khai báo rõ ràng DefaultAnnotationHandlerMapping trong ngữ cảnh của mình (và hầu hết mọi người không vì nó được khai báo hoàn toàn cho bạn), thì bạn có thể thêm rõ ràng và đặt thuộc tính đó.

+0

Điều đó đã làm điều đó, nhờ Skaffman. Nhưng nó đã phá vỡ một phần khác của ứng dụng ... contentNegotiatingViewResolver mà ánh xạ phần mở rộng ".json" của Jackson không còn hoạt động nữa. có ý tưởng nào để sửa cái này không? – nickdos

+0

Sắp xếp sự cố với nội dungNegotiatingViewResolver bằng cách liên kết rõ ràng phương pháp điều khiển của tôi với URL bao gồm ".json". Ví dụ. bằng cách sử dụng chú thích: @RequestMapping (value = "/search.json", method = RequestMethod.GET). Có một giải pháp đẹp hơn cho điều này? – nickdos

+0

Bạn đã thử '@RequestMapping (giá trị ="/tìm kiếm * ")'? – skaffman

12

Có lẽ, đó là một bản hack xấu xí, tôi chỉ muốn khám phá khả năng mở rộng của Spring @MVC. Đây là một tùy chỉnh PathMatcher. Nó sử dụng $ trong mẫu làm điểm đánh dấu kết thúc - nếu mẫu kết thúc bằng điểm đánh dấu, điểm đánh dấu bị xóa và mẫu được khớp với đối sánh mặc định, nhưng nếu mẫu có $ ở giữa (ví dụ: ...$.*), mẫu đó không khớp.

public class CustomPathMatcher implements PathMatcher { 
    private PathMatcher target; 

    public CustomPathMatcher() { 
     target = new AntPathMatcher(); 
    } 

    public String combine(String pattern1, String pattern2) { 
     return target.combine(pattern1, pattern2); 
    } 

    public String extractPathWithinPattern(String pattern, String path) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return ""; 
     } 
     return target.extractPathWithinPattern(pattern, path); 
    } 

    public Map<String, String> extractUriTemplateVariables(String pattern, 
      String path) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return Collections.emptyMap(); 
     } 
     return target.extractUriTemplateVariables(pattern, path); 
    } 

    public Comparator<String> getPatternComparator(String pattern) { 
     final Comparator<String> targetComparator = target.getPatternComparator(pattern); 
     return new Comparator<String>() { 
      public int compare(String o1, String o2) { 
       if (isEncoded(o1)) { 
        if (isEncoded(o2)) { 
         return 0; 
        } else { 
         return -1; 
        } 
       } else if (isEncoded(o2)) { 
        return 1; 
       } 
       return targetComparator.compare(o1, o2); 
      }   
     }; 
    } 

    public boolean isPattern(String pattern) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return true; 
     } 
     return target.isPattern(pattern); 
    } 

    public boolean match(String pattern, String path) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return false; 
     } 
     return target.match(pattern, path); 
    } 

    public boolean matchStart(String pattern, String path) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return false; 
     } 
     return target.match(pattern, path); 
    } 

    private boolean isEncoded(String pattern) { 
     return pattern != null && pattern.contains("$"); 
    } 

    private String resolvePattern(String pattern) { 
     int i = pattern.indexOf('$'); 
     if (i < 0) return pattern; 
     else if (i == pattern.length() - 1) { 
      return pattern.substring(0, i); 
     } else { 
      String tail = pattern.substring(i + 1); 
      if (tail.startsWith(".")) return null; 
      else return pattern.substring(0, i) + tail; 
     } 
    } 
} 

Config:

<bean id = "pathMatcher" class = "sample.CustomPathMatcher" /> 

<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
    <property name = "pathMatcher" ref="pathMatcher" /> 
</bean> 

<bean class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 
    <property name = "pathMatcher" ref="pathMatcher" /> 
</bean> 

Và sử dụng (cho "/hello/1.2.3", value là "1.2.3"):

@RequestMapping(value = "/hello/{value}$", method = RequestMethod.GET) 
public String hello(@PathVariable("value") String value, ModelMap model) 

EDIT:: Bây giờ không phá vỡ "dấu gạch chéo theo sau không quan trọng" quy tắc

+0

Cảm ơn mã này - thực sự hữu ích. Suy nghĩ tôi muốn đề cập đến bất cứ ai khác bằng cách sử dụng này mà bạn nên loại bỏ từ cấu hình mvc của bạn nếu thêm cấu hình pathMatcher tùy chỉnh ở trên. –

3

Tôi đã có cùng một vấn đề và tôi cũng giải quyết nó bằng PathMatcher tùy chỉnh. Giải pháp của tôi là đơn giản hơn một chút để những gì axtavt đề xuất. PathMatcher tôi cũng có một mục tiêu AntPathMatcher thức riêng, và nó đại biểu tất cả các cuộc gọi đến nó không thay đổi, ngoại trừ trận đấu() phương pháp:

@Override 
public boolean match(String pattern, String path) { 
    return pattern.endsWith(".*") ? false : target.match(pattern, path); 
} 

này hoạt động bởi vì mùa xuân cố gắng để phù hợp với điều khiển bằng cách thêm vào cuối cùng "." . Ví dụ: với ánh xạ đường dẫn "/ widgets/{id}" và URL "/widgets/1.2.3.4", Spring sẽ thử kết quả khớp đầu tiên "/ widgets/{id}." và sau đó "/ widgets/{id} ". Đầu tiên sẽ khớp, nhưng nó chỉ để lại "1.2.3" cho id.

PatchMatcher của tôi đặc biệt từ chối các mẫu kết thúc ". *", Do đó, lần thử đầu tiên không thành công và khớp thứ hai.

Nếu bạn đang sử dụng ContentNegotiatingViewResolver, bạn vẫn có thể chỉ định loại nội dung trong URL bằng cách sử dụng tham số yêu cầu "định dạng" (nếu tham số favorParameter là true).

-jarppe

6
<!-- language: lang-java --> 

@Controller public class MyController { @RequestMapping(value="/widgets/{preDot}.{postDot}") public void getResource(@PathVariable String preDot, @PathVariable String postDot) { String fullPath = preDot + "." + postDot; //... } }

// mã trên phải phù hợp với /widgets/111.222.333.444

+0

giải pháp đơn giản, dễ dàng – Jayz

1

Để thêm vào câu trả lời skaffman, nếu bạn đang sử dụng <mvc:annotation-driven/> và bạn muốn ghi đè giá trị useDefaultSuffixPattern, bạn có thể thay thế thẻ <mvc:annotation-driven> bằng cách sau:

<!-- Maps requests to @Controllers based on @RequestMapping("path") annotation values --> 
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 
    <property name="order" value="1" /> 
    <property name="useDefaultSuffixPattern" value="false" /> 
</bean> 

<!-- Enables annotated @Controllers --> 
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> 
4

Xuân 3.2 đã thay đổi, và cho thấy rằng bạn thiết lập các thuộc tính trên đậu RequestMappingHandlerMapping, hoặc rõ ràng (nếu không sử dụng không gian tên MVC) or by using a BeanPostProcessor như sau (bạn sẽ cần phải quét hoặc khởi tạo nó):

@Component 
public class IncludeExtensionsInRequestParamPostProcessor implements BeanPostProcessor { 
    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
     if (bean instanceof RequestMappingHandlerMapping) { 
      RequestMappingHandlerMapping mapping = (RequestMappingHandlerMapping)bean; 
      mapping.setUseRegisteredSuffixPatternMatch(false); 
      mapping.setUseSuffixPatternMatch(false); 
     } 
     return bean; 
    } 

    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } 
} 

Bạn cũng có thể chỉ thêm :.* vào @RequestMapping của mình, ví dụ: "/{documentPath:.*}"(see JIRA comment)

2

JFY: trong Spring 4 vấn đề này được khắc phục qua: WebMvcConfigurerAdapter.

@Configuration 
class MvcConfiguration extends WebMvcConfigurerAdapter { 

@Override 
public void configurePathMatch(PathMatchConfigurer configurer) { 
    configurer.setUseSuffixPatternMatch(false); 
} 
} 

Hoặc qua WebMvcConfigurationHỗ trợ như here.

Các vấn đề liên quan