web-dev-qa-db-ja.com

Spring MVC @PathVariableが切り捨てられる

情報へのRESTfulアクセスを提供するコントローラーがあります。

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

私が経験している問題は、特殊文字を含むパス変数でサーバーにヒットすると、切り捨てられることです。例: http:// localhost:8080/blah-server/blah/get/blah2010.08.19-02:25:47

パラメーターblahNameはblah2010.08になります

ただし、request.getRequestURI()の呼び出しには、渡されたすべての情報が含まれます。

Springが@PathVariableを切り捨てないようにする方法はありますか?

132
phogel

@RequestMapping引数の正規表現を試してください。

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

これはおそらく SPR-6164 と密接に関連しています。簡単に言うと、フレームワークはURIの解釈にいくつかのスマートを適用しようとし、ファイル拡張子と思われるものを削除します。これは、blah2010.08.19-02:25:47がファイル拡張子であると考えるため、blah2010.08.19-02:25:47に変換する効果があります。

リンクされた問題で説明したように、アプリコンテキストで独自のDefaultAnnotationHandlerMapping Beanを宣言し、そのuseDefaultSuffixPatternプロパティをfalseに設定することにより、この動作を無効にできます。これにより、デフォルトの動作が上書きされ、データの改ざんが停止されます。

55
skaffman

Springは、最後のドットの後ろにあるものは.jsonまたは.xmlなどのファイル拡張子であると見なし、それを切り捨ててパラメーターを取得します。

/{blahName}がある場合:

  • /param/param.json/param.xml、または/param.anythingは、値がparamのパラメーターになります
  • /param.value.json/param.value.xml、または/param.value.anythingは、値がparam.valueのパラメーターになります

提案どおりにマッピングを/{blahName:.+}に変更すると、最後のドットを含むすべてのドットがパラメーターの一部と見なされます。

  • /paramは、値paramを持つパラメーターになります
  • /param.jsonは、値param.jsonを持つパラメーターになります
  • /param.xmlは、値param.xmlを持つパラメーターになります
  • /param.anythingは、値param.anythingを持つパラメーターになります
  • /param.value.jsonは、値param.value.jsonを持つパラメーターになります
  • ...

拡張機能の認識を気にしない場合は、mvc:annotation-driven automagicをオーバーライドして無効にすることができます。

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

したがって、/{blahName}がある場合:

  • /param/param.json/param.xml、または/param.anythingは、値がparamのパラメーターになります
  • /param.value.json/param.value.xml、または/param.value.anythingは、値がparam.valueのパラメーターになります

注:デフォルトの設定との違いは、/something.{blahName}のようなマッピングがある場合にのみ表示されます。 Resthubプロジェクトの問題 を参照してください。

拡張機能の管理を維持する場合は、Spring 3.2以降、RequestMappingHandlerMapping BeanのuseRegisteredSuffixPatternMatchプロパティを設定して、suffixPattern認識をアクティブ化するが、登録済みの拡張機能に限定することもできます。

ここでは、jsonおよび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>

Mvc:annotation-drivenは、カスタムBeanを提供するcontentNegotiationオプションを受け入れるようになりましたが、RequestMappingHandlerMappingのプロパティをtrue(デフォルトはfalse)に変更する必要があることに注意してください(cf. https://jira.springsource.org/browse/SPR-7632 )。

そのため、すべてのmvc:annotation-driven構成をオーバーライドする必要があります。カスタムRequestMappingHandlerMappingを要求するために、Springへのチケットを開きました: https://jira.springsource.org/browse/SPR-1125 。興味のある方は投票してください。

オーバーライド中に、カスタム実行管理オーバーライドも考慮してください。そうしないと、すべてのカスタム例外マッピングが失敗します。リストBeanでmessageCovertersを再利用する必要があります。

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

私は、オープンソースプロジェクトに Resthub 私が参加しているこれらのテーマに関する一連のテストを実装しました: https://github.com/resthub/resthub-spring-stackを参照してください/ pull/219/files および https://github.com/resthub/resthub-spring-stack/issues/217

29
bmeurant

最後のドット以降はすべてファイル拡張子として解釈され、デフォルトで切断されます。
spring config xmlでDefaultAnnotationHandlerMappingを追加し、useDefaultSuffixPatternfalseに設定できます(デフォルトはtrueです)。

だからあなたの春のXML mvc-config.xml(またはそれが呼び出されます)を開き、追加します

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

これで、@PathVariableblahName(およびその他すべて)にすべてのドットを含むフルネームが含まれるようになります。

編集:これは spring apiへのリンク です

15
Jan

私も同じ問題に遭遇し、プロパティをfalseに設定しても助けにはなりませんでした。ただし、 APIによると

「.xxx」接尾辞を含むパスまたは「/」で終わるパスは、すでにデフォルトの接尾辞パターンを使用して変換されないことに注意してください。

RESTful URLに「/ end」を追加しようとすると、問題はなくなりました。私は解決策に満足していませんが、うまくいきました。

ところで、Springのデザイナーがこの "機能"を追加し、既定で有効にしたときに何を考えていたのかわかりません。私見、それは削除する必要があります。

7
Steve11235

正しいJava構成クラスを使用する:

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

このハックで解決しました

1)以下のように@PathVariableにHttpServletRequestを追加しました

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

2)リクエストでURLを直接取得します(このレベルでは切り捨てなし)

request.getPathInfo() 

Spring MVC @PathVariableとドット(。)は切り捨てられます

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

私はちょうどこれに出くわしました、そして、ここでの解決策は私が予想したように一般に働きませんでした。

SpEL式と複数のマッピングを使用することをお勧めします。

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

「:。+」を追加するとうまくいきましたが、外側の中括弧を削除するまではうまくいきませんでした。

value = {"/username/{id:.+}"}が機能しませんでした

value = "/username/{id:.+}"は動作します

私が誰かを助けたことを願っています:]

3
Martin Čejka

リクエストの送信先アドレスを編集できる場合、簡単な修正方法として、末尾にスラッシュを追加します(@RequestMapping値にも):

/path/{variable}/

したがって、マッピングは次のようになります。

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

Spring MVC @PathVariable with dot(。)iscutected も参照してください。

3
Michał Rybak

ファイル拡張子の問題は、パラメーターがURLの最後の部分にある場合にのみ存在します。変化する

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

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

そしてすべてが再び元気になります-

3
chrismarx

切り捨てを防ぐための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 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;
    }
}

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

UPDATE:

上記のアプローチを使用したときに、Spring Bootの自動構成にいくつかの問題があることに気付きました(一部の自動構成は効果的ではありません)。

代わりに、BeanPostProcessorアプローチを使用し始めました。うまく機能しているように見えました。

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

インスピレーション:http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html

2
burcakulug

テキストがデフォルトの拡張子のいずれとも一致しないことが確実な場合は、以下のコードを使用できます。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

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

Spring MVC @PathVariableが切り捨てられないようにするための望ましい解決策は、パス変数の末尾にスラッシュを追加することです。

例えば:

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

そのため、リクエストは次のようになります。

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

あなたが直面している問題は、springlast部分を解釈するためですafterdot(。)としてファイル拡張子.jsonや.xmlなど。そのため、springがパス変数を解決しようとすると、uriの末尾でドット(。)が検出された後、残りのデータが切り捨てられます。 注:これは、uriの最後にパス変数を保持する場合にのみ発生します。

たとえば、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) {
        // ...  
    }
}

上記のURL firstValue = "gallery.df"およびsecondValue = "link"で、。パス変数が解釈されると切り捨てられます。

そのため、これを防ぐには2つの方法があります。

1。)正規表現マッピングの使用

マッピングの最後の部分で正規表現を使用する

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

+を使用することにより、ドットがパス変数の一部になる後の値を示します。

2。)@PathVariableの最後にスラッシュを追加する

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

これは、Springのデフォルトの動作から保護する2番目の変数を囲みます。

3)Springのデフォルトwebmvc構成をオーバーライドすることにより

Springは、アノテーション@ EnableWebMvcを使用してインポートされるデフォルト構成をオーバーライドする方法を提供します。独自のを宣言することにより、Spring MVC構成をカスタマイズできます。 DefaultAnnotationHandlerMappingBeanをアプリケーションコンテキストで設定し、そのuseDefaultSuffixPatternプロパティをfalseにします。例:

@Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {

    @Bean
    public RequestMappingHandlerMapping 
      requestMappingHandlerMapping() {

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

このデフォルト設定をオーバーライドすると、すべてのURLに影響することに注意してください。

注:ここでは、デフォルトメソッドをオーバーライドするためにWebMvcConfigurationSupportクラスを拡張しています。 WebMvcConfigurerインターフェースを実装することにより、デフォルト設定をオーバーライドする方法がもう1つあります。この詳細については、以下を参照してください。 https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html

0