Spring MVC @PathVariable bị cắt ngắn


141

Tôi có một bộ điều khiển cung cấp quyền truy cập RESTful vào thông tin:

@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}")
public ModelAndView getBlah(@PathVariable String blahName, HttpServletRequest request,
                            HttpServletResponse response) {

Vấn đề tôi gặp phải là nếu tôi nhấn máy chủ với một biến đường dẫn có các ký tự đặc biệt thì nó sẽ bị cắt ngắn. Ví dụ: http: // localhost: 8080 / blah-server / blah / get / blah2010.08.19-02: 25: 47

Tham số blahName sẽ là blah2010.08

Tuy nhiên, lệnh gọi request.getRequestURI () chứa tất cả thông tin được truyền vào.

Bạn có biết làm thế nào để ngăn chặn Spring cắt xén @PathVariable không?


Có vẻ như điều này đã được giải quyết trong Spring 3.2-M2: xem Cho phép đường dẫn mở rộng tệp hợp lệ để đàm phán nội dung được chỉ địnhtài liệu của nó .
Arjan

Câu trả lời:


149

Hãy thử một biểu thức chính quy cho @RequestMappingđối số:

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}")

1
Cảm ơn câu trả lời, điều này đã giúp tôi giải quyết trường hợp tên người dùng bị cắt xén bằng cách nào đó .. (-: Tùy chọn khác với 'useDefaultSuffixPotype' không phải là một tùy chọn vì chúng tôi đang sử dụng các lớp mùa xuân @Configuration thay vì XML.
ev Shandongen

3
Điều này hoạt động, nhưng ý nghĩa của dấu hai chấm trong regex là gì?

6
Nô-ê, tôi đã không sử dụng điều này trong một thời gian dài, nhưng tôi nghĩ rằng dấu hai chấm tách biểu thức chính quy khỏi tên đối số để ràng buộc nó.
Earldoureb

3
chúng tôi đã có một problam tương tự /item/user@abc.com, bất cứ điều gì sau @ đã được cắt bớt, điều này đã được giải quyết bằng cách thêm một dấu gạch chéo /item/user@abc.com/
Titi Wangsa bin Damhore

58

Điều này có lẽ liên quan chặt chẽ với XUÂN-6164 . Tóm lại, khung công tác cố gắng áp dụng một số thông minh cho việc giải thích URI, loại bỏ những gì nó nghĩ là phần mở rộng tệp. Điều này sẽ có tác dụng chuyển blah2010.08.19-02:25:47thành blah2010.08, vì nó nghĩ rằng đó .19-02:25:47là một phần mở rộng tập tin.

Như được mô tả trong vấn đề được liên kết, bạn có thể vô hiệu hóa hành vi này bằng cách khai báo DefaultAnnotationHandlerMappingbean của riêng bạn trong ngữ cảnh ứng dụng và đặt thuộc tính của nó useDefaultSuffixPatternthành false. Điều này sẽ ghi đè hành vi mặc định và ngăn chặn hành vi lạm dụng dữ liệu của bạn.


3
Bật đàm phán nội dung mở rộng dựa trên mặc định có vẻ như là một lựa chọn kỳ lạ. Có bao nhiêu hệ thống thực sự phơi bày cùng một tài nguyên ở các định dạng khác nhau trong thực tế?
Affe

Tôi đã thử điều này vào buổi sáng và vẫn có các biến đường dẫn bị cắt ngắn.
phogel

30
+1 cho câu trả lời tuyệt vời và cũng để sử dụng cụm từ "lạm dụng dữ liệu của bạn"
Chris Thompson

11
Đối với người dùng Spring 3.1 - nếu bạn đang sử dụng cái mới RequestMappingHandlerMappingthay thế, thuộc tính cần đặt là useSuffixPatternMatch(cũng là false). @Ted: vấn đề được liên kết đề cập rằng trong 3.2 họ hy vọng sẽ thêm một chút kiểm soát để nó không phải là tất cả hoặc không có gì.
Nick

1
Trong Spring 4.2, điều này dễ dàng hơn một chút để cấu hình. Chúng tôi sử dụng các lớp cấu hình Java và mở rộng WebMvcConfigurationSupportcung cấp một hook đơn giản: public void configurePathMatch(PathMatchConfigurer configurer)- chỉ cần ghi đè lên đó và thiết lập đường dẫn khớp với cách bạn muốn.
pmckeown 28/03/2016

31

Spring xem xét rằng bất cứ điều gì đằng sau dấu chấm cuối cùng là một phần mở rộng tệp như .jsonhoặc .xmlcắt bớt nó để lấy tham số của bạn.

Vì vậy, nếu bạn có /{blahName}:

  • /param, /param.json, /param.xmlHoặc /param.anythingsẽ dẫn đến một param với giá trịparam
  • /param.value.json, /param.value.xmlHoặc /param.value.anythingsẽ dẫn đến một param với giá trịparam.value

Nếu bạn thay đổi ánh xạ của mình thành /{blahName:.+}như được đề xuất, bất kỳ dấu chấm nào, kể cả dấu chấm cuối cùng, sẽ được coi là một phần của tham số của bạn:

  • /param sẽ dẫn đến một param có giá trị param
  • /param.json sẽ dẫn đến một param có giá trị param.json
  • /param.xml sẽ dẫn đến một param có giá trị param.xml
  • /param.anything sẽ dẫn đến một param có giá trị param.anything
  • /param.value.json sẽ dẫn đến một param có giá trị param.value.json
  • ...

Nếu bạn không quan tâm đến nhận dạng tiện ích mở rộng, bạn có thể vô hiệu hóa nó bằng cách ghi đè mvc:annotation-driventự động:

<bean id="handlerMapping"
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
    <property name="useSuffixPatternMatch" value="false"/>
</bean>

Vì vậy, một lần nữa, nếu bạn có /{blahName}:

  • /param, /param.json, /param.xmlHoặc /param.anythingsẽ dẫn đến một param với giá trịparam
  • /param.value.json, /param.value.xmlHoặc /param.value.anythingsẽ dẫn đến một param với giá trịparam.value

Lưu ý: sự khác biệt so với cấu hình mặc định chỉ hiển thị nếu bạn có ánh xạ như /something.{blahName} . Xem vấn đề dự án Resthub .

Nếu bạn muốn duy trì quản lý tiện ích mở rộng, kể từ Spring 3.2, bạn cũng có thể đặt thuộc tính useRegisteredSuffixPotypeMatch của bean RequestMappingHandlerMapping để giữ cho nhận dạng hậu tố được kích hoạt nhưng giới hạn ở tiện ích mở rộng đã đăng ký.

Ở đây bạn chỉ xác định các phần mở rộng json và xml:

<bean id="handlerMapping"
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
    <property name="useRegisteredSuffixPatternMatch" value="true"/>
</bean>

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="false"/>
    <property name="favorParameter" value="true"/>
    <property name="mediaTypes">
        <value>
            json=application/json
            xml=application/xml
        </value>
    </property>
</bean>

Lưu ý rằng mvc: điều khiển chú thích chấp nhận ngay bây giờ tùy chọn contentNegotiation để cung cấp một bean tùy chỉnh nhưng thuộc tính của RequestMappingHandlerMapping phải được thay đổi thành true (false default) (xem https://jira.springsource.org/browse/SPR-7632 ).

Vì lý do đó, bạn vẫn phải ghi đè tất cả cấu hình điều khiển mvc: annotation. Tôi đã mở một vé đến Spring để yêu cầu một RequestMappingHandlerMapping tùy chỉnh: https://jira.springsource.org/browse/SPR-11253 . Hãy bỏ phiếu nếu bạn quan tâm.

Trong khi ghi đè, hãy cẩn thận để xem xét ghi đè quản lý Thực thi tùy chỉnh. Nếu không, tất cả các ánh xạ Ngoại lệ tùy chỉnh của bạn sẽ thất bại. Bạn sẽ phải sử dụng lại MessageCoverters với danh sách bean:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />

<util:list id="messageConverters">
    <bean class="your.custom.message.converter.IfAny"></bean>
    <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</util:list>

<bean name="exceptionHandlerExceptionResolver"
      class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
    <property name="order" value="0"/>
    <property name="messageConverters" ref="messageConverters"/>
</bean>

<bean name="handlerAdapter"
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
            <property name="conversionService" ref="conversionService" />
            <property name="validator" ref="validator" />
        </bean>
    </property>
    <property name="messageConverters" ref="messageConverters"/>
</bean>

<bean id="handlerMapping"
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>

Tôi đã triển khai, trong dự án mã nguồn mở Resthub mà tôi là một phần của một tập hợp các bài kiểm tra về các chủ đề này: xem https://github.com/resthub/resthub-spring-stack/pull/219/fileshttps: // github.com/resthub/resthub-spring-stack/issues/217


16

Mọi thứ sau dấu chấm cuối cùng được hiểu là phần mở rộng tập tin và bị cắt theo mặc định.
Trong xml cấu hình mùa xuân của bạn, bạn có thể thêm DefaultAnnotationHandlerMappingvà đặt useDefaultSuffixPatternthành false(mặc định là true).

Vì vậy, mở spring xml của bạn mvc-config.xml(hoặc tuy nhiên nó được gọi) và thêm

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="useDefaultSuffixPattern" value="false" />
</bean>

Bây giờ @PathVariable blahName(và tất cả các thứ khác cũng vậy) nên chứa tên đầy đủ bao gồm tất cả các dấu chấm.

EDIT: Đây là một liên kết đến api mùa xuân


Tôi chưa thử, nhưng những người khác cho rằng bạn cũng cần phải xóa <mvc:annotation-driven />nếu có.
Arjan

7

Tôi cũng gặp vấn đề tương tự và đặt tài sản thành sai cũng không giúp tôi. Tuy nhiên, API cho biết :

Lưu ý rằng các đường dẫn có hậu tố ".xxx" hoặc kết thúc bằng "/" sẽ không được chuyển đổi bằng cách sử dụng mẫu hậu tố mặc định trong mọi trường hợp.

Tôi đã thử thêm "/ end" vào URL RESTful của mình và vấn đề đã biến mất. Tôi không hài lòng với giải pháp, nhưng nó đã làm việc.

BTW, tôi không biết các nhà thiết kế Spring đã nghĩ gì khi họ thêm "tính năng" này và sau đó bật nó theo mặc định. IMHO, nó nên được gỡ bỏ.


Tôi đồng ý. Tôi gần đây đã từng chút một.
llambda

7

Sử dụng lớp cấu hình Java chính xác:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter
{

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
    {
        configurer.favorPathExtension(false);
    }

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

Nó hiệu quả tuyệt vời đối với tôi. Chạy trên Tomcat Spring phiên bản 4.3,14
Dave


3

Tôi mới thực hiện điều này và các giải pháp ở đây thường không hoạt động như tôi mong đợi.

Tôi đề nghị sử dụng biểu thức SpEL và nhiều ánh xạ, ví dụ:

@RequestMapping(method = RequestMethod.GET, 
    value = {Routes.BLAH_GET + "/{blahName:.+}", 
             Routes.BLAH_GET + "/{blahName}/"})

3

Sự cố mở rộng tệp chỉ tồn tại nếu tham số nằm ở phần cuối của URL. Thay đổi

@RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}")

đến

@RequestMapping(
   method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/safe")

và tất cả sẽ tốt trở lại-


3

Nếu bạn có thể chỉnh sửa địa chỉ mà các yêu cầu được gửi đến, cách khắc phục đơn giản sẽ là thêm dấu gạch chéo vào chúng (và cả trong @RequestMappinggiá trị):

/path/{variable}/

vì vậy ánh xạ sẽ trông như sau:

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/")

Xem thêm Spring MVC @PathVariable với dấu chấm (.) Đang bị cắt ngắn .


3
//in your xml dispatcher  add this property to your default annotation mapper bean as follow
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="alwaysUseFullPath" value="true"></property>
</bean>       

3

thêm ":. +" làm việc cho tôi, nhưng cho đến khi tôi loại bỏ dấu ngoặc nhọn bên ngoài.

value = {"/username/{id:.+}"} không làm việc

value = "/username/{id:.+}" làm

Hy vọng tôi đã giúp được ai đó:]


2

Giải pháp cấu hình dựa trên Java để ngăn chặn cắt ngắn (sử dụng lớp không bị phản đối):

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Configuration
public class PolRepWebConfig extends WebMvcConfigurationSupport {

    @Override
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        final RequestMappingHandlerMapping handlerMapping = super
                .requestMappingHandlerMapping();
        // disable the truncation after .
        handlerMapping.setUseSuffixPatternMatch(false);
        // disable the truncation after ;
        handlerMapping.setRemoveSemicolonContent(false);
        return handlerMapping;
    }
}

Nguồn: http://www.javacodegeek.com/2013/01/spring-mvc-customizing-requestmappinghandlermicking.html

CẬP NHẬT:

Tôi nhận thấy có một số vấn đề với cấu hình tự động Spring Boot khi tôi sử dụng cách tiếp cận ở trên (một số cấu hình tự động không có hiệu quả).

Thay vào đó, tôi bắt đầu sử dụng BeanPostProcessorcách tiếp cận. Nó dường như được làm việc tốt hơn.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    private static final Logger logger = LoggerFactory
            .getLogger(MyBeanPostProcessor.class);

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

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof RequestMappingHandlerMapping) {
            setRemoveSemicolonContent((RequestMappingHandlerMapping) bean,
                    beanName);
            setUseSuffixPatternMatch((RequestMappingHandlerMapping) bean,
                    beanName);
        }
        return bean;
    }

    private void setRemoveSemicolonContent(
            RequestMappingHandlerMapping requestMappingHandlerMapping,
            String beanName) {
        logger.info(
                "Setting 'RemoveSemicolonContent' on 'RequestMappingHandlerMapping'-bean to false. Bean name: {}",
                beanName);
        requestMappingHandlerMapping.setRemoveSemicolonContent(false);
    }

    private void setUseSuffixPatternMatch(
            RequestMappingHandlerMapping requestMappingHandlerMapping,
            String beanName) {
        logger.info(
                "Setting 'UseSuffixPatternMatch' on 'RequestMappingHandlerMapping'-bean to false. Bean name: {}",
                beanName);
        requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
    }
}

Lấy cảm hứng từ: http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html


2

nếu bạn chắc chắn rằng văn bản của bạn sẽ không khớp với bất kỳ tiện ích mở rộng mặc định nào bạn có thể sử dụng mã bên dưới:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseRegisteredSuffixPatternMatch(true);
    }
}

1

Giải pháp thích hợp hơn của tôi để ngăn chặn Spring MVC @PathVariable bị cắt ngắn là thêm dấu gạch chéo ở cuối biến đường dẫn.

Ví dụ:

@RequestMapping(value ="/email/{email}/")

Vì vậy, yêu cầu sẽ như sau:

http://localhost:8080/api/email/test@test.com/

1

Vấn đề mà bạn đang phải đối mặt là do mùa xuân giải thích cuối cùng một phần của uri sau các dấu chấm (.) Như một phần mở rộng tập tin như .json hoặc .xml. Vì vậy, khi mùa xuân cố gắng giải quyết biến đường dẫn, nó chỉ đơn giản là cắt bớt phần còn lại của dữ liệu sau khi nó gặp một dấu chấm (.) Ở cuối uri. Lưu ý: điều này cũng chỉ xảy ra nếu bạn giữ biến đường dẫn ở cuối uri.

Ví dụ: hãy xem xét uri: https: //localhost/example/gallery.df/link.ar

@RestController
public class CustomController {
    @GetMapping("/example/{firstValue}/{secondValue}")
    public void example(@PathVariable("firstValue") String firstValue,
      @PathVariable("secondValue") String secondValue) {
        // ...  
    }
}

Trong url trên FirstValue = "gallery.df" và secondValue = "link", bit cuối cùng sau. bị cắt ngắn khi biến đường dẫn được diễn giải.

Vì vậy, để ngăn chặn điều này, có hai cách có thể:

1.) Sử dụng ánh xạ regrec

Sử dụng biểu thức chính ở phần cuối của ánh xạ

@GetMapping("/example/{firstValue}/{secondValue:.+}")   
public void example(
  @PathVariable("firstValue") String firstValue,
  @PathVariable("secondValue") String secondValue) {
    //...
}

Bằng cách sử dụng +, chúng tôi chỉ ra bất kỳ giá trị nào sau dấu chấm cũng sẽ là một phần của biến đường dẫn.

2.) Thêm dấu gạch chéo vào cuối @PathVariable của chúng tôi

@GetMapping("/example/{firstValue}/{secondValue}/")
public void example(
  @PathVariable("firstValue") String firstValue,
  @PathVariable("secondValue") String secondValue) {
    //...
}

Điều này sẽ kèm theo biến thứ hai của chúng ta bảo vệ nó khỏi hành vi mặc định của Spring.

3) Bằng cách ghi đè cấu hình webmvc mặc định của Spring

Spring cung cấp cách để ghi đè các cấu hình mặc định mà được nhập bằng cách sử dụng các chú thích @EnableWebMvc Chúng tôi có thể tùy chỉnh cấu hình Spring MVC bằng cách tuyên bố riêng của chúng tôi DefaultAnnotationHandlerMapping đậu trong bối cảnh ứng dụng và thiết lập của nó useDefaultSuffixPattern tài sản để sai. Thí dụ:

@Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {

    @Bean
    public RequestMappingHandlerMapping 
      requestMappingHandlerMapping() {

        RequestMappingHandlerMapping handlerMapping
          = super.requestMappingHandlerMapping();
        handlerMapping.setUseSuffixPatternMatch(false);
        return handlerMapping;
    }
}

Hãy nhớ rằng ghi đè cấu hình mặc định này, ảnh hưởng đến tất cả các url.

Lưu ý: ở đây chúng tôi đang mở rộng lớp WebMvcConfigurationSupport để ghi đè các phương thức mặc định. Có một cách khác để ghi đè các cấu hình deault bằng cách triển khai giao diện WebMvcConfigurer. Để biết thêm chi tiết về điều này, hãy đọc: https://docs.spring.io/spring/docs/civerse/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.