Nokta (.) İle değişken MVC @PathVariable kesiliyor


362

Bu, Spring MVC @PathVariable'ın kısaltılması sorusunun devamıdır

Bahar forumu, ContentNegotiationManager'ın bir parçası olarak düzeltildiğini (3.2 sürümü) belirtir. aşağıdaki bağlantıya bakın.
https://jira.springsource.org/browse/SPR-6164
https://jira.springsource.org/browse/SPR-7632

Uygulama isteğimde: .com ile parametre kesildi.

Birisi bana bu yeni özelliğin nasıl kullanılacağını açıklayabilir mi? xml'de nasıl yapılandırılabilir?

Not: bahar forumu- # 1 Nokta (.) İle Bahar MVC @PathVariable kesiliyor

Yanıtlar:


485

Bildiğim kadarıyla bu sorun sadece requestmapping sonunda pathvariable için görünür.

Bunu, istek haritasındaki normal ifade eklentisini tanımlayarak çözebildik.

 /somepath/{variable:.+}

1
Teşekkürler, bu düzeltmenin daha önce de mevcut olduğunu düşünüyorum (3.2V'den önce)? Ancak bu düzeltmeyi sevmiyorum; benim uygulamada ele alınması gereken tüm url gerekli olduğundan ve gelecekteki URL uygulaması da bununla ilgilenmek için ...
Kanagavelu Sugumar

4
3.0.5 baharda sorunu nasıl çözdüm<!-- 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, sözdizimi {variable_name:regular_expression}, yani burada adlandırılmış değişkenimiz var variable, bu değer regex kullanılarak eşleştirilecek .+(burada .'herhangi bir karakter' +anlamına gelir ve 'bir veya daha fazla kez' anlamına gelir).
Michał Rybak

4
@StefanHaberl variabledüzenli bir şekilde eşleşirseniz , Spring son ek algılama özelliklerini kullanır ve noktadan sonra her şeyi keser. Normal ifade eşleşmesini kullandığınızda, bu özellikler kullanılmaz - değişken yalnızca sağladığınız normal ifade ile eşleştirilir.
Michał Rybak

9
@martin "variable:.+", değişkente birden fazla nokta olduğunda çalışmaz. örneğin e-postaları gibi huzurlu yolların sonuna koymak /path/abc@server.com.au. Denetleyici bile çağrılmaz, ancak yalnızca bir nokta olduğunda çalışır /path/abc@server.com. Neden ve / veya geçici bir çözüm var mı?
Bohemya

242

Spring, son noktanın arkasındaki herhangi bir şeyin .jsonveya gibi bir dosya uzantısı olduğunu .xmldüşünür ve parametrenizi almak için bu öğeyi tructe eder.

Eğer varsa /somepath/{variable}:

  • /somepath/param, /somepath/param.json, /somepath/param.xmlYa da /somepath/param.anythingbir değere sahip bir param sonuçlanırparam
  • /somepath/param.value.json, /somepath/param.value.xmlYa da /somepath/param.value.anythingbir değere sahip bir param sonuçlanırparam.value

eşlemenizi /somepath/{variable:.+}önerilen şekilde değiştirirseniz , sonuncusu da dahil olmak üzere herhangi bir nokta parametrenizin bir parçası olarak kabul edilir:

  • /somepath/param değeri olan bir param ile sonuçlanacak param
  • /somepath/param.json değeri olan bir param ile sonuçlanacak param.json
  • /somepath/param.xml değeri olan bir param ile sonuçlanacak param.xml
  • /somepath/param.anything değeri olan bir param ile sonuçlanacak param.anything
  • /somepath/param.value.json değeri olan bir param ile sonuçlanacak param.value.json
  • ...

Uzantı tanımayı önemsemiyorsanız, mvc:annotation-drivenotomajik özelliği geçersiz kılarak devre dışı bırakabilirsiniz :

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

Yani, yine, eğer varsa /somepath/{variable}:

  • /somepath/param, /somepath/param.json, /somepath/param.xmlYa da /somepath/param.anythingbir değere sahip bir param sonuçlanırparam
  • /somepath/param.value.json, /somepath/param.value.xmlYa da /somepath/param.value.anythingbir değere sahip bir param sonuçlanırparam.value

not: varsayılan yapılandırmadan farkı yalnızca benzer bir eşlemeniz varsa görünür somepath/something.{variable}. bkz Resthub proje sorunu

eklenti yönetimini korumak istiyorsanız, Bahar 3.2'den bu yana sonekPattern tanıma özelliğini etkinleştirilmiş ancak kayıtlı uzantıyla sınırlı tutmak için RequestMappingHandlerMapping bean'un useRegisteredSuffixPatternMatch özelliğini de ayarlayabilirsiniz.

Burada yalnızca json ve xml uzantılarını tanımlarsınız:

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

Mvc: ek açıklamaya dayalı özel bir fasulye sağlamak için artık bir contentNegotiation seçeneğini kabul ettiğini, ancak RequestMappingHandlerMapping özelliğinin true (varsayılan false) olarak değiştirilmesi gerektiğini unutmayın (bkz. Https://jira.springsource.org/browse/SPR-7632 ).

Bu nedenle, tüm mvc: ek açıklamaya dayalı yapılandırmayı geçersiz kılmanız gerekir. Özel bir RequestMappingHandlerMapping istemek için Spring'e bir bilet açtım: https://jira.springsource.org/browse/SPR-11253 . Eğer intereted iseniz oy verin.

Geçersiz kılma sırasında, özel Yürütme yönetimini geçersiz kılmayı da göz önünde bulundurun. Aksi takdirde, tüm özel İstisna eşlemeleriniz başarısız olur. İletiyi bir liste çekirdeği ile yeniden kullanmanız gerekecek:

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

Bir parçası olduğum açık kaynak projesi Resthub'da bu konularda bir dizi test uyguladım : bkz. Https://github.com/resthub/resthub-spring-stack/pull/219/files & https: // github.com/resthub/resthub-spring-stack/issues/217


Affet beni acemiyim, yani fasulyeyi nereye koyarsın? ve hangi yaylı versiyon için geçerlidir?
Splash

@Splash: Bu çekirdekleri "standart" Spring applicationContext.xml dosyalarınıza tanımlamanız gerekir. Bu en azından Bahar 3.2 için geçerlidir. Muhtemelen (en azından kısmen) önce
bmeurant

Bence bu doğru cevap. Görünüşe göre "useRegisteredSuffixPatternMatch" parametresi OPs sorunu için tam olarak tanıtıldı.
lrxw

Bu benim için çözümün sadece yarısıydı. @ Paul Aerer'in cevabına bakınız.
8bitjunkie

96

Bahar 4 için güncelleme: 4.0.1 beri kullanabilirsiniz PathMatchConfigurer(sizin aracılığı WebMvcConfigurer), örneğin

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

XML'de şöyle olur ( https://jira.spring.io/browse/SPR-10163 ):

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

11
bu en temiz çözümdür: Etrafında kesmek yerine, neden olan özelliği kapatın. Bu özelliği zaten kullanmıyoruz, bu yüzden problem çözüldü - mükemmel!
David Lavender

AllResources sınıfı nereye gidiyor?
irl_irl

1
@ste_irl Main ile aynı pakete bir java sınıfı ekleyin.
kometen

5
Son matcher.setUseSuffixPatternMatch(false)ek eşleşmesini tamamen devre dışı bırakmak için kullanın .
Gian Marco Gherardi

Bu benim için çözümün sadece yarısıydı. @ Paul Aerer'in cevabına bakınız.
8bitjunkie

87

Martin Frey'in cevabına ek olarak, RequestMapping değerine bir eğik çizgi ekleyerek de düzeltilebilir:

/path/{variable}/

Bu düzeltmenin sürdürülebilirliği desteklemediğini unutmayın. Artık tüm URI'lerin sonunda bir eğik çizgi olması gerekiyor - bu, API kullanıcıları / yeni geliştiriciler tarafından görülemeyebilir. Muhtemelen tüm parametrelerin içinde bir tane .olmayabilir, ayrıca aralıklı hatalar da oluşturabilir


2
Bu daha temiz bir çözüm. IE son eke göre kabul üstbilgileri ayarlamak zor yolunu bulmak zorunda kaldı. Bu yüzden bazı .doc requestmapping göndermek istedim ve her zaman yeni html sayfası yerine bir indirme var. Bu yaklaşım bunu düzeltti.
Martin Frey

bu benim için en basit çözüm ve sorunumu çözdü; regexp, birçok vaka için biraz abartı gibi görünüyor
Riccardo Cossu

7
ancak sondaki eğik çizgileri otomatik olarak kaldırmak için AngularJS'nin varsayılan davranışı ile çarpışır. Bu, en son Açısal sürümlerde yapılandırılabilir, ancak ne olduğunu bilmiyorsanız saatlerce izlenecek bir şeydir.
dschulten

1
@dschulten Ve beni saatlerce hata ayıklamadan kurtardın, teşekkürler! Bununla birlikte, cevapta HTPP taleplerinde sondaki eğik çizginin gerekli olacağını belirtmelisiniz.
Hoffmann

1
Bu çok tehlikeli! Herhangi bir API uygulayan en az beklediğiniz gibi kesinlikle tavsiye etmem. Çok bakım gerektirmez.
sparkyspider

32

Spring Boot Rest Controller'da bunları aşağıdaki adımları izleyerek çözdüm:

RestController:

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

Ve Dinlenme İstemcisinden:

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

2
Bu cevap çalışması için bir eğik çizgiye bağlıdır.
8bitjunkie

2
çekicilik gibi çalışır (ayrıca eğik çizgi olmadan). teşekkür ederim!
afe

27

":. +" ekleyerek benim için çalıştı, ama dış süslü parantez kaldırılıncaya kadar.

value = { "/username/{id:.+}" } çalışmadı

value = "/username/{id:.+}" çalışır

Umarım birine yardım ettim :)


Çünkü kıvırcık parantezler id
RegEx'i

15

/somepath/{variable:.+}Java requestMappingetiketinde çalışır .


Bu cevabı tercih ediyorum çünkü neyin işe yaramadığını göstermiyor.
johnnieb

Birden fazla noktalı e-posta adresleri için çalışmaz.
8bitjunkie

1
@ Gibi 8bitjunkie Sth "/{code:.+}"birçok noktalar için işler değil bir deyişle 61.12.7o da ie için çalışıyork.a.p@o.i.n
tryingHard

13

İşte tamamen java yapılandırmasına dayanan bir yaklaşım:

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

Teşekkürler, benim için çözdü. Ayrıca, çok temiz ve açık. +1
bkis

11

Bu soruna geçici bir çözüm bulmak için oldukça kolay bir yolu, sondaki eğik çizgi eklemek ...

Örneğin:

kullanın:

/somepath/filename.jpg/

onun yerine:

/somepath/filename.jpg

11

Bahar Önyükleme, Düzenli ifade sorunu çözmek gibi

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

Bunun yalnızca bir nokta için çalıştığını unutmayın. E-posta adresleri için çalışmaz.
8bitjunkie

1
@ 8bitjunkie Sth gibi "/{code:.+}"pek çok nokta için değil, yani 61.12.7aynı zamanda da çalışırk.a.p@o.i.n
çalışıyorHard

1
@ 8bitjunkie IP adresi ile test ettim. Çok iyi çalışıyor. Yani, bu çoklu noktalar için çalışıyor demektir.
Dapper Dan

6

İlkbahar 4.2 için yol adlarında e-posta adreslerini içeren eksiksiz çözüm

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

Bunu application-xml dosyasına ekle


Olumlu oy - bu, hem ContentNegotiationManagerFactoryBean hem de contentNegotiationManager yapılandırma öğelerinin gerekli
8bitjunkie

5

Spring 3.2.x kullanıyorsanız ve <mvc:annotation-driven /> bu küçük oluşturun 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;
    }
}

Sonra bunu MVC config xml'nize koyun:

<bean class="spring.DoNotTruncateMyUrls" />

ContentNegotiationManager ile ilgili mi?
Kanagavelu Sugumar

Kodum yalnızca URL'lerin kısaltılmaması için RequestMappingHandlerMapping öğesini yapılandırır. ContentNegotiationManager başka bir canavar.
Jukka

2
Bu eski, ama bunun BeanPostProcessoriçin gerçekten ihtiyacınız yok . Eğer kullanırsanız WebMvcConfigurationSupportsize geçersiz kılabilirsiniz requestMappingHandlerMapping @Beanyöntemi. XML config kullanıyorsanız, sadece kendi RequestMappingHandlerMappingfasulyenizi ve bu özelliği beyan edebilirsiniz .
Sotirios Delimanolis

Çok teşekkür ederim, aynı sorun için farklı sayıda çözüm denedim, sadece bu benim için çalıştı. :-)
Biz Borg

3

Sonunda Spring Docs'ta çözüm buldum :

Dosya uzantılarının kullanımını tamamen devre dışı bırakmak için aşağıdakilerin her ikisini de ayarlamanız gerekir:

 useSuffixPatternMatching(false), see PathMatchConfigurer

 favorPathExtension(false), see ContentNegotiationConfigurer

Bunu WebMvcConfigurerAdapteruygulamama eklemek sorunu çözdü:

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

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

2

Benim için

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

ancak istek URL'nizdeki "nokta" yı "% 2E" olarak kodlarsanız çalışır. Ancak URL'lerin hepsinin geçerli olması gerekir ... ki bu geçerli olsa da "standart" bir kodlama değildir. Hata bir şey gibi geliyor: |

"Sondaki eğik çizgi" yoluna benzer diğer bir çözüm de nokta "satır içi" olacak değişkeni hareket ettirmektir:

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

şimdi tüm noktalar korunacak, hiçbir değişiklik veya normal ifade gerekmeyecek.


1

Bahar 5.2.4 itibariyle (Spring Boot v2.2.6.RELEASE) PathMatchConfigurer.setUseSuffixPatternMatchve ContentNegotiationConfigurer.favorPathExtensionkullanımdan kaldırılmıştır ( https://spring.io/blog/2020/03/24/spring-framework-5-2-5-available-now ve https://github.com/spring-projects/spring-framework/issues/24179 ).

Asıl sorun, istemcinin belirli bir ortam türü (.com gibi) istemesi ve Spring'in varsayılan olarak tüm bu ortam türlerini eklemesidir. Çoğu durumda REST denetleyiciniz yalnızca JSON üretir, bu nedenle istenen çıktı biçimini (.com) desteklemez. Bu sorunun üstesinden gelmek için dinlenme denetleyicinizi (veya belirli yöntemi) 'çıkış' biçimini ( @RequestMapping(produces = MediaType.ALL_VALUE)) destekleyecek şekilde güncelleyerek ve elbette nokta ({username:.+} ) .

Misal:

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

İlkbahar 5.3 ve üstü yalnızca kayıtlı eklerle (medya türleri) eşleşecektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.