복붙노트

[SPRING] "파일 이름"부분에 여러 점이있는 REST-ful URL 만들기 - Spring 3.0 MVC

SPRING

"파일 이름"부분에 여러 점이있는 REST-ful URL 만들기 - Spring 3.0 MVC

주석 기반 컨트롤러와 함께 Spring MVC (3.0)를 사용하고 있습니다. 리소스에 대해 REST-ful URL을 만들고 URL 끝 부분에 파일 확장명을 요구할 수는 없지만 확장명이없는 경우 HTML 콘텐츠 형식으로 간주 할 수는 있습니다. 이것은 파일 이름 부분에 점 (마침표 / 전체 멈춤)이없는 한 Spring MVC와 함께 즉시 사용할 수 있습니다.

그러나 일부 URL에는 이름에 점이있는 식별자가 필요합니다. 예 : 이렇게 :

http://company.com/widgets/123.456.789.500

이 경우 Spring은 확장자 .500에 대한 컨텐츠 유형을 찾고 오류가없는 것을 찾습니다. .html을 끝에 추가하거나 식별자를 인코딩하거나 후행 슬래시를 추가하는 등의 해결 방법을 사용할 수 있습니다. 나는 이것들이 만족스럽지 않지만 .html을 추가하면서 살 수있을 것이다.

나는 실패한 봄에 기본 파일 확장명 검색을 무시하는 방법을 찾았습니다.

주어진 컨트롤러 메소드 또는 URL 패턴 등에 대한 파일 확장자 검색을 사용자 정의하거나 사용 불가능하게 할 수 있습니까?

해결법

  1. ==============================

    1.URL의 점들에 관해서는 @PathVariable 패턴 매칭이 약간 짜증납니다 (SPR-5778 참고). DefaultAnnotationHandlerMapping의 useDefaultSuffixPattern 속성을 false로 설정하면 점잖은 (하지만 까다 롭다) 문제가없고 점이 많이 드는 URL을 효과적으로 제어 할 수 있습니다.

    URL의 점들에 관해서는 @PathVariable 패턴 매칭이 약간 짜증납니다 (SPR-5778 참고). DefaultAnnotationHandlerMapping의 useDefaultSuffixPattern 속성을 false로 설정하면 점잖은 (하지만 까다 롭다) 문제가없고 점이 많이 드는 URL을 효과적으로 제어 할 수 있습니다.

    컨텍스트에서 DefaultAnnotationHandlerMapping을 명시 적으로 선언하지 않은 경우 (대다수의 사용자는 암시 적으로 선언 된 이후로) 명시 적으로 추가하고 해당 속성을 설정할 수 있습니다.

  2. ==============================

    2.아마 그것은 못생긴 해킹 일뿐입니다. Spring @MVC의 확장 성을 탐구하고 싶었을 것입니다. 다음은 사용자 정의 된 PathMatcher입니다. 패턴에서 $를 끝 마커로 사용합니다. 패턴이 끝나면 마커가 제거되고 패턴은 기본 정규 일치 자와 일치하지만 패턴에 $가 중간에 있으면 (예 : ... $. *), 그런 패턴 일치하지 않습니다.

    아마 그것은 못생긴 해킹 일뿐입니다. Spring @MVC의 확장 성을 탐구하고 싶었을 것입니다. 다음은 사용자 정의 된 PathMatcher입니다. 패턴에서 $를 끝 마커로 사용합니다. 패턴이 끝나면 마커가 제거되고 패턴은 기본 정규 일치 자와 일치하지만 패턴에 $가 중간에 있으면 (예 : ... $. *), 그런 패턴 일치하지 않습니다.

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

    구성 :

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

    그리고 사용법 (주어진 "/hello/1.2.3", 값은 "1.2.3") :

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

    편집 : 지금은 "후행 슬래시 상관 없어"규칙을 위반하지 않습니다

  3. ==============================

    3.

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

    @제어 장치 공용 클래스 MyController {  @RequestMapping (value = "/ widgets / {preDot}. {postDot}")  공공 무효 getResource (@PathVariable 문자열 preDot, @PathVariable 문자열 postDot) {     문자열 fullPath = preDot + "." + postDot;     // ...  } }

    // 위 코드는 /widgets/111.222.333.444와 일치해야합니다.

  4. ==============================

    4.Spring 3.2가 변경되었고 RequestMappingHandlerMapping 빈에 명시 적으로 (mvc 네임 스페이스를 사용하지 않는 경우) 또는 다음과 같은 BeanPostProcessor를 사용하여 (스캔하거나 인스턴스화해야 함) 속성을 설정하는 것이 좋습니다.

    Spring 3.2가 변경되었고 RequestMappingHandlerMapping 빈에 명시 적으로 (mvc 네임 스페이스를 사용하지 않는 경우) 또는 다음과 같은 BeanPostProcessor를 사용하여 (스캔하거나 인스턴스화해야 함) 속성을 설정하는 것이 좋습니다.

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

    @RequestMapping에. *을 추가 할 수도 있습니다. "/{documentPath:.*}"(JIRA 주석 참조)

  5. ==============================

    5.나는 같은 문제가 있었고 커스텀 패스 매처 (PathMatcher)로 해결했다. 내 솔루션은 axtavt가 제안한 것보다 다소 간단합니다. My PathMatcher는 또한 개인 최종 AntPathMatcher 대상을 가지며 match () 메서드를 제외하고 모든 호출에 변경되지 않은 모든 호출을 위임합니다.

    나는 같은 문제가 있었고 커스텀 패스 매처 (PathMatcher)로 해결했다. 내 솔루션은 axtavt가 제안한 것보다 다소 간단합니다. My PathMatcher는 또한 개인 최종 AntPathMatcher 대상을 가지며 match () 메서드를 제외하고 모든 호출에 변경되지 않은 모든 호출을 위임합니다.

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

    이것은 스프링이 컨트롤러를 "." 끝까지. 예를 들어, 경로 매핑 "/ widgets / {id}"및 URL "/widgets/1.2.3.4"를 사용하면 Spring은 첫 번째 일치 항목을 "/ widgets / {id}"로 다시 시도합니다. 그 다음에 "/ widgets / {id}". 첫 번째는 일치하지만 id에는 "1.2.3"만 남습니다.

    My PatchMatcher는 ". *"로 끝나는 패턴을 특별히 거부하므로 첫 번째 시도가 실패하고 두 번째 일치가 발생합니다.

    ContentNegotiatingViewResolver를 사용하는 경우 requestPattern "format"을 사용하여 URL에 콘텐츠 유형을 지정할 수 있습니다 (favorParameter가 true로 설정된 경우).

    -jarppe

  6. ==============================

    6.JFY : Spring 4에서이 문제는 WebMvcConfigurerAdapter를 통해 해결됩니다.

    JFY : Spring 4에서이 문제는 WebMvcConfigurerAdapter를 통해 해결됩니다.

    @Configuration 
    class MvcConfiguration extends WebMvcConfigurerAdapter {
    
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseSuffixPatternMatch(false);
    }
    }
    

    또는 여기 에서처럼 WebMvcConfigurationSupport를 통해.

    그리고 봄 5 :

    @Configuration
    public class MvcConfiguration implements WebMvcConfigurer {
    
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            configurer.setUseSuffixPatternMatch(false);
        }
    }
    
  7. ==============================

    7.skaffman의 답변에 추가하려면 을 사용하고 useDefaultSuffixPattern 값을 무시하려면 태그를 다음과 같이 바꿀 수 있습니다.

    skaffman의 답변에 추가하려면 을 사용하고 useDefaultSuffixPattern 값을 무시하려면 태그를 다음과 같이 바꿀 수 있습니다.

    <!-- 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" />
    
  8. from https://stackoverflow.com/questions/2079299/trying-to-create-rest-ful-urls-with-multiple-dots-in-the-filename-part-sprin by cc-by-sa and MIT license