Spring MVC @PathVariable with dot (.) Đang bị cắt ngắn


361

Đây là phần tiếp theo của câu hỏi Spring MVC @PathVariable bị cắt ngắn

Diễn đàn Spring tuyên bố rằng nó đã sửa (phiên bản 3.2) như là một phần của ContentNegotiationManager. xem liên kết dưới đây.
https://jira.springsource.org/browse/SPR-6164
https://jira.springsource.org/browse/SPR-7632

Trong ứng dụng của tôi requestParameter với .com bị cắt ngắn.

Bất cứ ai có thể giải thích cho tôi làm thế nào để sử dụng tính năng mới này? Làm thế nào để cấu hình tại xml?

Lưu ý: diễn đàn mùa xuân- # 1 Spring MVC @PathVariable with dot (.) Đang bị cắt ngắn

Câu trả lời:


485

Theo như tôi biết thì vấn đề này chỉ xuất hiện đối với đường dẫn ở cuối yêu cầu.

Chúng tôi đã có thể giải quyết điều đó bằng cách xác định addon regex trong requestmapping.

 /somepath/{variable:.+}

1
Cảm ơn, tôi nghĩ rằng bản sửa lỗi này cũng có sẵn sớm hơn (trước 3.2V)?. Tuy nhiên tôi không thích cách khắc phục này; vì nó cần ở tất cả các url phải được xử lý trong ứng dụng của tôi ... và việc triển khai URL trong tương lai cũng phải được xử lý này ...
Kanagavelu Sugumar

4
đây là cách tôi giải quyết vấn đề vào mùa xuân 3.0.5<!-- Spring Configuration needed to avoid URI using dots to be truncated --> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="useDefaultSuffixPattern" value="false" /> </bean>
Farid

11
@Mariusz, cú pháp là {variable_name:regular_expression}, vì vậy ở đây chúng ta có biến được đặt tên variable, giá trị nào sẽ được khớp bằng regex .+( .có nghĩa là 'bất kỳ ký tự nào' và +có nghĩa là 'một hoặc nhiều lần').
Michał Rybak

4
@StefanHaberl nếu bạn khớp variabletheo cách thông thường, Spring sử dụng các tính năng phát hiện hậu tố của nó và cắt mọi thứ sau dấu chấm. Khi bạn sử dụng kết hợp regrec, các tính năng đó không được sử dụng - biến chỉ được khớp với regrec mà bạn cung cấp.
Michał Rybak

9
@martin "variable:.+"không hoạt động khi có nhiều hơn một dấu chấm trong biến. ví dụ: đặt email ở cuối các đường dẫn yên tĩnh như thế nào /path/abc@server.com.au. Bộ điều khiển thậm chí không được gọi, nhưng nó hoạt động khi chỉ có một dấu chấm /path/abc@server.com. Bất kỳ ý tưởng tại sao và / hoặc một cách giải quyết?
Bohemian

242

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.xml và cắt nó để lấy tham số của bạn.

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

  • /somepath/param, /somepath/param.json,/somepath/param.xml Hoặc /somepath/param.anythingsẽ dẫn đến một param với giá trịparam
  • /somepath/param.value.json, /somepath/param.value.xml Hoặc /somepath/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 /somepath/{variable:.+}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:

  • /somepath/param sẽ dẫn đến một param có giá trị param
  • /somepath/param.json sẽ dẫn đến một param có giá trị param.json
  • /somepath/param.xml sẽ dẫn đến một param có giá trị param.xml
  • /somepath/param.anything sẽ dẫn đến một param có giá trị param.anything
  • /somepath/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ó /somepath/{variable}:

  • /somepath/param, /somepath/param.json,/somepath/param.xml Hoặc /somepath/param.anythingsẽ dẫn đến một param với giá trịparam
  • /somepath/param.value.json,/somepath/param.value.xml Hoặc /somepath/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ư thế nào somepath/something.{variable}. 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 bị giới hạn đối với 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 (mặc định là sai) (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 . Vui lòng bỏ phiếu nếu bạn được xen vào.

Trong khi ghi đè, hãy cẩn thận để xem xét quản lý Thi hành tùy chỉnh ghi đè. 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 bộ thử nghiệm về các chủ đề này: xem https://github.com/resthub/resthub-spring-stack/pull/219/files & https: // github.com/resthub/resthub-spring-stack/issues/217


Hãy tha thứ cho tôi Tôi là người mới, vậy bạn đặt các cấu hình bean ở đâu? và nó áp dụng cho phiên bản mùa xuân nào?
Splash

@Splash: Bạn phải định nghĩa các bean này vào (các) tệp Spring applicationContext.xml "tiêu chuẩn" của bạn. Điều này áp dụng cho Spring 3.2 ít nhất. Có lẽ (ít nhất là một phần) trước
bmeurant

Đây là câu trả lời đúng theo ý kiến ​​của tôi. Có vẻ như tham số "useRegisteredSuffixPotypeMatch" đã được giới thiệu chính xác cho vấn đề OP.
lrxw

Đây chỉ là một nửa giải pháp cho tôi. Xem câu trả lời của @Paul Aerer's.
8bitjunkie

96

Cập nhật cho Spring 4: kể từ 4.0.1, bạn có thể sử dụng PathMatchConfigurer(thông qua của bạn WebMvcConfigurer), vd

@Configuration
protected static class AllResources extends WebMvcConfigurerAdapter {

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

}


@Configuration
public class WebConfig implements WebMvcConfigurer {

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

Trong xml, nó sẽ là ( https://jira.spring.io/browse/SPR-10163 ):

<mvc:annotation-driven>
    [...]
    <mvc:path-matching registered-suffixes-only="true"/>
</mvc:annotation-driven>

11
đây là giải pháp sạch nhất: tắt tính năng gây ra nó, thay vì hack xung quanh nó. Dù sao chúng tôi cũng không sử dụng tính năng này, nên vấn đề đã được giải quyết - hoàn hảo!
David Lavender

Lớp AllResource đi đâu?
irl_irl

1
@ste_irl Thêm một lớp java trong cùng gói với chính của bạn.
kometen

5
Sử dụng matcher.setUseSuffixPatternMatch(false)để vô hiệu hóa hoàn toàn khớp hậu tố.
Gian Marco Gherardi

Đây chỉ là một nửa giải pháp cho tôi. Xem câu trả lời của @Paul Aerer's.
8bitjunkie

87

Ngoài câu trả lời của Martin Frey, điều này cũng có thể được khắc phục bằng cách thêm dấu gạch chéo trong giá trị RequestMapping:

/path/{variable}/

Hãy nhớ rằng sửa chữa này không hỗ trợ khả năng bảo trì. Bây giờ, nó yêu cầu tất cả các URI phải có dấu gạch chéo - một điều có thể không rõ ràng đối với người dùng API / nhà phát triển mới. Bởi vì có thể không phải tất cả các tham số có thể có .trong đó, nó cũng có thể tạo ra các lỗi không liên tục


2
Đó thậm chí là một giải pháp sạch hơn. Tôi đã phải tìm ra cách khó khăn mà IE đang thiết lập các tiêu đề chấp nhận theo hậu tố. Vì vậy, tôi muốn đăng lên một số yêu cầu .doc và tôi luôn tải xuống thay vì trang html mới. Cách tiếp cận này đã khắc phục điều đó.
Martin Frey

đây là giải pháp đơn giản nhất để tôi giải quyết vấn đề của mình; regrec có vẻ hơi quá mức đối với nhiều trường hợp
Riccardo Cossu

7
nhưng nó va chạm với hành vi mặc định của AngularJS để loại bỏ các dấu gạch chéo tự động. Điều đó có thể được cấu hình trong các bản phát hành Angular mới nhất nhưng nó là thứ cần theo dõi hàng giờ nếu bạn không biết chuyện gì đang xảy ra.
dschulten

1
@dschulten Và bạn vừa tiết kiệm cho tôi hàng giờ gỡ lỗi, cảm ơn! Tuy nhiên, bạn nên đề cập trong câu trả lời rằng dấu gạch chéo sẽ được yêu cầu trong các yêu cầu HTPP.
Hoffmann

1
Điều này rất nguy hiểm! Tôi chắc chắn sẽ không đề xuất nó vì bất kỳ ai triển khai API sẽ ít mong đợi nhất. Rất không thể bảo trì.
Sparkyspider

32

Trong Spring Boot Rest Controller, tôi đã giải quyết những điều này bằng các bước sau:

Bộ điều khiển:

@GetMapping("/statusByEmail/{email:.+}/")
public String statusByEmail(@PathVariable(value = "email") String email){
  //code
}

Và từ khách hàng nghỉ ngơi:

Get http://mywebhook.com/statusByEmail/abc.test@gmail.com/

2
Câu trả lời này phụ thuộc vào dấu gạch chéo để làm việc.
8bitjunkie

2
hoạt động như một lá bùa (cũng không có dấu gạch chéo). cảm ơn bạn!
afe

27

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 / nbid:.+}" } không hoạt động

value = "/username / nbid:.+}" hoạt động

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


Đó là bởi vì dấu ngoặc nhọn đánh giá RegEx và bạn đã có một số khoảngid
8bitjunkie

15

/somepath/{variable:.+}hoạt động trong requestMappingthẻ Java .


Tôi thích câu trả lời này vì nó không hiển thị những gì không hoạt động.
johnnieb

Không hoạt động đối với các địa chỉ email có nhiều hơn một dấu chấm.
8bitjunkie

1
@ 8bitjunkie Sth thích "/{code:.+}"hoạt động cho nhiều dấu chấm chứ không phải một, tức là 61.12.7nó cũng hoạt động cho tức làk.a.p@o.i.n
thử vào

13

Đây là một cách tiếp cận hoàn toàn dựa vào cấu hình java:

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 MvcConfig extends WebMvcConfigurationSupport{

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
        handlerMapping.setUseSuffixPatternMatch(false);
        handlerMapping.setUseTrailingSlashMatch(false);
        return handlerMapping;
    }
}

Cảm ơn, giải quyết nó cho tôi. Ngoài ra, nó rất sạch sẽ và rõ ràng. +1
bkis

11

Một cách khá dễ dàng để khắc phục vấn đề này là nối thêm dấu gạch chéo ...

ví dụ:

sử dụng :

/somepath/filename.jpg/

thay vì:

/somepath/filename.jpg

11

Trong Spring Boot, biểu thức chính quy giải quyết vấn đề như

@GetMapping("/path/{param1:.+}")

Lưu ý rằng điều này chỉ hoạt động cho một dấu chấm. Nó không hoạt động cho các địa chỉ email.
8bitjunkie

1
@ 8bitjunkie Sth thích "/{code:.+}"hoạt động cho nhiều dấu chấm chứ không phải một, tức là 61.12.7nó cũng hoạt động cho tức làk.a.p@o.i.n
thử vào

1
@ 8bitjunkie Tôi đã thử nó với địa chỉ IP. Nó hoạt động rất tốt. Vì vậy, điều đó có nghĩa là nó hoạt động cho nhiều dấu chấm.
Dapper Dan

6

Giải pháp hoàn chỉnh bao gồm địa chỉ email trong tên đường dẫn cho mùa xuân 4.2 là

<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>
<mvc:annotation-driven
    content-negotiation-manager="contentNegotiationManager">
    <mvc:path-matching suffix-pattern="false" registered-suffixes-only="true" />
</mvc:annotation-driven>

Thêm phần này vào ứng dụng-xml


Upvote - đây là câu trả lời duy nhất ở đây cho thấy rõ rằng cả hai mục cấu hình ContentNegotiationManagerFactoryBean và contentNegotiationManager đều được yêu cầu
8bitjunkie 26/07/18

5

Nếu bạn đang sử dụng Spring 3.2.x và <mvc:annotation-driven />, hãy tạo một chút BeanPostProcessor:

package spring;

public final class DoNotTruncateMyUrls implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof RequestMappingHandlerMapping) {
            ((RequestMappingHandlerMapping)bean).setUseSuffixPatternMatch(false);
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

Sau đó đặt cái này vào xml cấu hình MVC của bạn:

<bean class="spring.DoNotTruncateMyUrls" />

Có liên quan đến ContentNegotiationManager không?
Kanagavelu Sugumar

Mã của tôi chỉ định cấu hình RequestMappingHandlerMapping để các URL sẽ không bị cắt ngắn. ContentNegotiationManager là một con thú khác.
Jukka

2
Điều này đã cũ, nhưng bạn thực sự không cần một thứ BeanPostProcessornày. Nếu bạn sử dụng WebMvcConfigurationSupportbạn có thể ghi đè requestMappingHandlerMapping @Beanphương thức. Nếu bạn sử dụng cấu hình XML, bạn có thể chỉ cần khai báo RequestMappingHandlerMappingbean của riêng bạn và khai báo thuộc tính đó.
Sotirios Delimanolis

Cảm ơn bạn rất nhiều, tôi đã thử một số giải pháp khác nhau cho cùng một vấn đề, chỉ có giải pháp này hiệu quả với tôi. :-)
Chúng tôi là Borg

3

Cuối cùng tôi đã tìm thấy giải pháp trong Spring Docs :

Để vô hiệu hóa hoàn toàn việc sử dụng tiện ích mở rộng tệp, bạn phải đặt cả hai điều sau đây:

 useSuffixPatternMatching(false), see PathMatchConfigurer

 favorPathExtension(false), see ContentNegotiationConfigurer

Thêm điều này vào WebMvcConfigurerAdapterviệc thực hiện của tôi đã giải quyết vấn đề:

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

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

2

Đối với tôi

@GetMapping(path = "/a/{variableName:.+}")

không hoạt động nhưng chỉ khi bạn cũng mã hóa "dấu chấm" trong url yêu cầu của bạn là "% 2E" thì nó mới hoạt động. Nhưng tất cả đều yêu cầu URL phải ... đó không phải là mã hóa "tiêu chuẩn", mặc dù hợp lệ. Cảm thấy như một cái gì đó của một lỗi: |

Cách khác, tương tự như cách "dấu gạch chéo" là di chuyển biến sẽ có dấu chấm "nội tuyến" ex:

@GetMapping (path = "/ {biếnName} / a")

bây giờ tất cả các dấu chấm sẽ được bảo tồn, không cần sửa đổi hoặc regex.


1

Kể từ Mùa xuân 5.2.4 (Spring Boot v2.2.6.RELEASE) PathMatchConfigurer.setUseSuffixPatternMatchContentNegotiationConfigurer.favorPathExtensionđã bị từ chối ( https://spring.io/blog/2020/03/24/spring-framework-5-2-5-av Available-now và https://github.com/spring-projects/spring-framework/issues/24179 ).

Vấn đề thực sự là máy khách yêu cầu một loại phương tiện cụ thể (như .com) và Spring đã thêm tất cả các loại phương tiện đó theo mặc định. Trong hầu hết các trường hợp, bộ điều khiển REST của bạn sẽ chỉ tạo JSON nên nó sẽ không hỗ trợ định dạng đầu ra được yêu cầu (.com). Để khắc phục vấn đề này, bạn nên ổn bằng cách cập nhật bộ điều khiển nghỉ ngơi (hoặc phương pháp cụ thể) để hỗ trợ định dạng 'ouput' ( @RequestMapping(produces = MediaType.ALL_VALUE)) và tất nhiên cho phép các ký tự như dấu chấm ({username:.+} ).

Thí dụ:

@RequestMapping(value = USERNAME, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class UsernameAPI {

    private final UsernameService service;

    @GetMapping(value = "/{username:.+}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.ALL_VALUE)
    public ResponseEntity isUsernameAlreadyInUse(@PathVariable(value = "username") @Valid @Size(max = 255) String username) {
        log.debug("Check if username already exists");
        if (service.doesUsernameExist(username)) {
            return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
        }
        return ResponseEntity.notFound().build();
    }
}

Spring 5.3 trở lên sẽ chỉ khớp với các hậu tố đã đăng ký (loại phương tiện).

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.