Spring MVC @PathVariable mulai terpotong

142

Saya memiliki pengontrol yang menyediakan akses tenang ke informasi:

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

Masalah yang saya alami adalah bahwa jika saya menekan server dengan variabel path dengan karakter khusus itu akan terpotong. Misalnya: http: // localhost: 8080 / blah-server / blah / get / blah2010.08.19-02: 25: 47

Parameter blahName akan menjadi blah2010.08

Namun, panggilan untuk meminta.getRequestURI () berisi semua informasi yang disampaikan.

Tahu cara mencegah Spring memotong @PathVariable?

phogel
sumber
Tampaknya ini telah diselesaikan pada Musim Semi 3.2-M2: lihat Izinkan jalur ekstensi file yang valid untuk negosiasi konten yang akan ditentukan dan dokumentasinya .
Arjan

Jawaban:

149

Coba ekspresi reguler untuk @RequestMappingargumen:

RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}")
earldouglas
sumber
1
Terima kasih atas jawabannya, ini membantu saya memecahkan kasus di mana nama pengguna dipangkas entah bagaimana .. (-: Opsi lain dengan 'useDefaultSuffixPattern' bukan pilihan karena kami menggunakan kelas semi @Configuration alih-alih XML.
evandongen
3
Ini berfungsi, tetapi apa arti penting dari usus besar di regex?
Noah Yetter
6
Noah, saya sudah lama tidak menggunakan ini, tapi saya pikir usus besar memisahkan ekspresi reguler dari nama argumen untuk mengikatnya.
earldouglas
3
kami memiliki problam /item/[email protected] yang serupa, apa pun setelah @ terpotong, ini diselesaikan dengan menambahkan slash lain /item/[email protected]/
Titi Wangsa bin Damhore
59

Ini mungkin terkait erat dengan SPR-6164 . Secara singkat, kerangka kerja mencoba menerapkan beberapa kecerdasan ke interpretasi URI, menghapus apa yang dianggapnya sebagai ekstensi file. Ini akan memiliki efek berubah blah2010.08.19-02:25:47menjadi blah2010.08, karena dianggap .19-02:25:47ekstensi file.

Seperti dijelaskan dalam masalah yang ditautkan, Anda dapat menonaktifkan perilaku ini dengan mendeklarasikan DefaultAnnotationHandlerMappingbean Anda sendiri dalam konteks aplikasi, dan menyetel useDefaultSuffixPatternpropertinya ke false. Ini akan mengesampingkan perilaku default, dan menghentikannya mencabuli data Anda.

skaffman
sumber
3
Mengaktifkan negosiasi konten berbasis ekstensi secara default sepertinya pilihan yang aneh. Berapa banyak sistem yang mengekspos sumber daya yang sama dalam format yang berbeda dalam praktik?
Affe
Saya mencoba ini pagi hari dan masih memiliki variabel path terpotong.
phogel
30
+1 untuk jawaban yang bagus dan juga untuk menggunakan frasa "menganiaya data Anda"
Chris Thompson
11
Untuk pengguna Spring 3.1 - jika Anda menggunakan yang baru RequestMappingHandlerMapping, properti yang akan diatur adalah useSuffixPatternMatch(juga ke false). @Ted: masalah terkait menyebutkan bahwa dalam 3.2 mereka berharap untuk menambahkan sedikit lebih banyak kontrol sehingga tidak harus semuanya atau tidak sama sekali.
Nick
2
Di Spring 4.2 ini sedikit lebih mudah untuk dikonfigurasi. Kami menggunakan kelas konfigurasi Java dan memperluas WebMvcConfigurationSupportyang menyediakan pengait sederhana: public void configurePathMatch(PathMatchConfigurer configurer)- cukup timpa itu dan atur jalur yang sesuai dengan keinginan Anda.
pmckeown
31

Spring menganggap bahwa apa pun di belakang titik terakhir adalah ekstensi file seperti .jsonatau .xmldan memotongnya untuk mengambil parameter Anda.

Jadi, jika Anda memiliki /{blahName}:

  • /param, /param.json, /param.xmlAtau /param.anythingakan menghasilkan param dengan nilaiparam
  • /param.value.json, /param.value.xmlatau /param.value.anythingakan menghasilkan param dengan nilaiparam.value

Jika Anda mengubah pemetaan menjadi /{blahName:.+}seperti yang disarankan, titik apa pun, termasuk yang terakhir, akan dianggap sebagai bagian dari parameter Anda:

  • /param akan menghasilkan param dengan nilai param
  • /param.json akan menghasilkan param dengan nilai param.json
  • /param.xml akan menghasilkan param dengan nilai param.xml
  • /param.anything akan menghasilkan param dengan nilai param.anything
  • /param.value.json akan menghasilkan param dengan nilai param.value.json
  • ...

Jika Anda tidak peduli dengan pengenalan ekstensi, Anda dapat menonaktifkannya dengan mengesampingkan mvc:annotation-drivenautomagic:

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

Jadi, sekali lagi, jika Anda memiliki /{blahName}:

  • /param, /param.json, /param.xmlAtau /param.anythingakan menghasilkan param dengan nilaiparam
  • /param.value.json, /param.value.xmlatau /param.value.anythingakan menghasilkan param dengan nilaiparam.value

Catatan: perbedaan dari konfigurasi default hanya terlihat jika Anda memiliki pemetaan seperti /something.{blahName}. Lihat masalah proyek Resthub .

Jika Anda ingin mempertahankan manajemen ekstensi, sejak Spring 3.2 Anda juga dapat mengatur properti useRegisteredSuffixPatternMatch dari RequestMappingHandlerMapping bean untuk menjaga suffixPattern pengenalan diaktifkan tetapi terbatas pada ekstensi terdaftar.

Di sini Anda hanya mendefinisikan ekstensi json dan 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>

Perhatikan bahwa mvc: annotation-driven menerima sekarang opsi contentNegotiation untuk menyediakan kacang kustom tetapi properti dari RequestMappingHandlerMapping harus diubah menjadi true (default false) (lih. Https://jira.springsource.org/browse/SPR-7632 ).

Karena itu, Anda masih harus mengganti semua konfigurasi mvc: annotation-driven. Saya membuka tiket ke Spring untuk meminta RequestMappingHandlerMapping kustom: https://jira.springsource.org/browse/SPR-11253 . Berikan suara jika Anda tertarik.

Sementara mengesampingkan, berhati-hatilah untuk mempertimbangkan juga menimpa manajemen Eksekusi kustom. Jika tidak, semua pemetaan Pengecualian khusus Anda akan gagal. Anda harus menggunakan kembali messageCoverters dengan daftar kacang:

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

Saya menerapkan, dalam proyek open source Resthub yang menjadi bagian saya, serangkaian tes pada subjek ini: lihat https://github.com/resthub/resthub-spring-stack/pull/219/files dan https: // github.com/resthub/resthub-spring-stack/issues/217

bmeurant
sumber
16

Semuanya setelah titik terakhir ditafsirkan sebagai ekstensi file dan terpotong secara default.
Dalam pegas konfigurasi xml Anda, Anda dapat menambah DefaultAnnotationHandlerMappingdan mengatur useDefaultSuffixPatternke false(default adalah true).

Jadi buka xml pegas Anda mvc-config.xml(atau bagaimanapun disebut) dan tambahkan

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

Sekarang @PathVariable blahName(dan semua yang lain juga) harus berisi nama lengkap termasuk semua titik.

EDIT: Berikut ini tautan ke api musim semi

Jan
sumber
Saya belum mencoba, tetapi orang lain mengklaim Anda juga harus menghapus <mvc:annotation-driven />jika berlaku.
Arjan
7

Saya juga mengalami masalah yang sama, dan mengatur properti ke false juga tidak membantu saya. Namun, API mengatakan :

Perhatikan bahwa jalur yang menyertakan sufiks ".xxx" atau diakhiri dengan "/" sudah tidak akan ditransformasikan menggunakan pola sufiks default dalam hal apa pun.

Saya mencoba menambahkan "/ akhiri" ke URL TETAP saya, dan masalahnya hilang. Saya tidak senang dengan solusinya, tetapi itu berhasil.

BTW, saya tidak tahu apa yang dipikirkan desainer Spring ketika mereka menambahkan "fitur" ini dan kemudian menyalakannya secara default. IMHO, itu harus dihapus.

Steve11235
sumber
Saya setuju. Saya baru-baru ini digigit oleh ini.
llambda
7

Menggunakan kelas konfigurasi Java yang benar:

@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);
    }
}
jebeaudet
sumber
Ini bekerja baik untuk saya. Berjalan dengan Tomcat Spring versi 4.3.14
Dave
4

Saya diselesaikan dengan hack ini

1) Menambahkan HttpServletRequest di @PathVariable seperti di bawah ini

 @PathVariable("requestParam") String requestParam, HttpServletRequest request) throws Exception { 

2) Dapatkan URL secara langsung (Pada tingkat ini tidak ada pemotongan) dalam permintaan

request.getPathInfo() 

Spring MVC @PathVariable dengan dot (.) Semakin terpotong

Kanagavelu Sugumar
sumber
3

Saya hanya mengalami ini dan solusi di sini umumnya tidak berfungsi seperti yang saya harapkan.

Saya sarankan menggunakan ekspresi SpEL dan beberapa pemetaan, misalnya

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

Masalah ekstensi file hanya ada jika parameternya ada di bagian terakhir URL. Perubahan

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

untuk

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

dan semua akan baik-baik saja-

chrismarx
sumber
3

Jika Anda dapat mengedit alamat yang dikirimi permintaan, perbaikan sederhana adalah dengan menambahkan garis miring padanya (dan juga dalam @RequestMappingnilainya):

/path/{variable}/

sehingga pemetaan akan terlihat seperti:

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

Lihat juga Spring MVC @PathVariable dengan dot (.) Semakin terpotong .

Michał Rybak
sumber
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>       
Fareed Alnamrouti
sumber
3

menambahkan ":. +" bekerja untuk saya, tetapi tidak sampai saya menghapus kurung keriting luar.

value = {"/username/{id:.+}"} tidak bekerja

value = "/username/{id:.+}" bekerja

Semoga saya membantu seseorang:]

Martin Čejka
sumber
2

Solusi konfigurasi berbasis Java untuk mencegah pemotongan (menggunakan kelas yang tidak usang):

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

Sumber: http://www.javacodegeeks.com/2013/01/spring-mvc-customizing-requestmappinghandlermapping.html

MEMPERBARUI:

Saya menyadari ada beberapa masalah dengan konfigurasi otomatis Boot Musim Semi ketika saya menggunakan pendekatan di atas (beberapa konfigurasi otomatis tidak menjadi efektif).

Sebaliknya, saya mulai menggunakan BeanPostProcessorpendekatan itu. Tampaknya bekerja lebih baik.

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

Terinspirasi dari: http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html

burcakulug
sumber
2

jika Anda yakin teks Anda tidak akan cocok dengan ekstensi default apa pun, Anda dapat menggunakan kode di bawah ini:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseRegisteredSuffixPatternMatch(true);
    }
}
Bassem Reda Zohdy
sumber
1

Solusi saya yang lebih disukai untuk mencegah Spring MVC @PathVariable untuk terpotong adalah dengan menambahkan trailing slash di akhir variabel path.

Sebagai contoh:

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

Jadi, permintaan akan terlihat seperti:

http://localhost:8080/api/email/[email protected]/
Johnny
sumber
1

Masalah yang Anda hadapi adalah karena menafsirkan pegas itu terakhir bagian dari uri setelah itu titik (.) Sebagai ekstensi file seperti .json atau .xml. Jadi ketika pegas mencoba untuk menyelesaikan variabel path, ia hanya memotong sisa data setelah menemukan titik (.) Di akhir uri. catatan: ini juga terjadi hanya jika Anda menyimpan variabel path di akhir uri.

Misalnya pertimbangkan 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) {
        // ...  
    }
}

Dalam url di atas firstValue = "gallery.df" dan secondValue = "tautan", bit terakhir setelah. akan terpotong ketika variabel path ditafsirkan.

Jadi, untuk mencegahnya ada dua cara yang mungkin:

1.) Menggunakan pemetaan regexp

Gunakan regex di bagian akhir pemetaan

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

Dengan menggunakan +, kami menunjukkan nilai apa pun setelah titik juga akan menjadi bagian dari variabel path.

2.) Menambahkan garis miring di akhir @PathVariable kami

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

Ini akan menyertakan variabel kedua kami yang melindunginya dari perilaku default Spring.

3) Dengan mengesampingkan konfigurasi webmvc default Spring

Spring menyediakan cara untuk mengesampingkan konfigurasi default yang diimpor dengan menggunakan anotasi @EnableWebMvc . Kita dapat menyesuaikan konfigurasi Spring MVC dengan mendeklarasikan kacang DefaultAnnotationHandlerMapping kita sendiri dalam konteks aplikasi dan menyetel properti useDefaultSuffixPattern menjadi false. Contoh:

@Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {

    @Bean
    public RequestMappingHandlerMapping 
      requestMappingHandlerMapping() {

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

Perlu diingat bahwa mengesampingkan konfigurasi default ini, memengaruhi semua url.

Catatan: di sini kita memperluas kelas WebMvcConfigurationSupport untuk mengganti metode default. Ada satu cara lagi untuk mengganti konfigurasi deault dengan mengimplementasikan antarmuka WebMvcConfigurer. Untuk detail lebih lanjut tentang ini, baca: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

Ananthapadmanabhan
sumber