ドット(。)を含むSpring MVC @PathVariableが切り捨てられる


361

これは質問の続きです MVC @PathVariableが切り捨てられます

Springフォーラムは、ContentNegotiationManagerの一部として修正済み(バージョン3.2)であると述べています。以下のリンクを参照してください。
https://jira.springsource.org/browse/SPR-6164
https://jira.springsource.org/browse/SPR-7632

私のアプリケーションでは、.comのrequestParameterが切り捨てられています。

この新機能の使い方を誰かに説明してもらえますか?xmlでどのように構成できますか?

注:Springフォーラム-#1 Spring MVC @PathVariable with dot(。)is truncated

回答:


485

私の知る限り、この問題はリクエストマッピングの最後のパス変数でのみ発生します。

requestmappingで正規表現アドオンを定義することで、これを解決できました。

 /somepath/{variable:.+}

1
おかげで、この修正は以前にも(3.2Vより前に)利用できると思いますか?しかし、この修正は好きではありません。私のアプリケーションで処理する必要のあるすべてのURLで必要です...そして将来のURL実装もこれに
対処する必要があり

4
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、構文はなので、{variable_name:regular_expression}ここではという名前の変数がありvariable、その値は正規表現を使用して照合されます.+(ここ.で「任意の文字」を+意味し、「1回以上」を意味します)。
のMichałRybakの

4
@StefanHaberl variableが通常の方法で一致する場合、Springは接尾辞検出機能を使用し、ドットの後のすべてを切り捨てます。正規表現照合を使用する場合、それらの機能は使用されません。変数は、指定した正規表現にのみ照合されます。
のMichałRybakの

9
"variable:.+"変数に複数のドットがある場合、@ martin は機能しません。たとえば、のような落ち着いた経路の最後にメールを置く/path/abc@server.com.au。コントローラーは呼び出されませんが、ドットが1つしかない場合に機能し/path/abc@server.comます。理由や回避策はありますか?
ボヘミアン

242

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

だからあなたが持っている場合/somepath/{variable}

  • /somepath/param/somepath/param.json/somepath/param.xmlまたは/somepath/param.anything値PARAMをもたらしますparam
  • /somepath/param.value.json/somepath/param.value.xmlまたは/somepath/param.value.anything値を持つパラメータになりますparam.value

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

  • /somepath/param 値を持つパラメータになります param
  • /somepath/param.json 値を持つパラメータになります param.json
  • /somepath/param.xml 値を持つパラメータになります param.xml
  • /somepath/param.anything 値を持つパラメータになります param.anything
  • /somepath/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>

だから、もう一度、あなたが持っているなら/somepath/{variable}

  • /somepath/param/somepath/param.json/somepath/param.xmlまたは/somepath/param.anything値PARAMをもたらしますparam
  • /somepath/param.value.json/somepath/param.value.xmlまたは/somepath/param.value.anything値を持つパラメータになりますparam.value

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

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

ここでは、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)に変更する必要があることに注意してくださいhttps://jira.springsource.org/browse/SPR-7632を参照))。

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

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

<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/fileshttps://でgithub.com/resthub/resthub-spring-stack/issues/217


私が初心者であることを私に許してください、それであなたは豆の設定をどこに置きますか?それはどの春バージョンに適用されますか?
スプラッシュ

@Splash:これらのBeanを「標準」Spring applicationContext.xmlファイルに定義する必要があります。これは、少なくともSpring 3.2に適用されます。おそらく(少なくとも部分的に)以前
bmeurant 2014

これは私の意見では正しい答えです。パラメータ「useRegisteredSuffixPatternMatch」は、OPの問題のためだけに導入されたようです。
lrxw

これは私にとってソリューションの半分にすぎませんでした。@Paul Aererの回答を参照してください。
8bitjunkie 2018

96

Spring 4の更新:4.0.1以降、PathMatchConfigurer(を介してWebMvcConfigurer)使用できます。例:

@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では、次のようになります(https://jira.spring.io/browse/SPR-10163):

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

11
これははるかにクリーンなソリューションです。ハッキングするのではなく、原因となる機能をオフにします。とにかくこの機能を使用していないので、問題は解決しました-完璧です!
David Lavender 2015年

AllResourcesクラスはどこに行きますか?
irl_irl

1
@ste_irlメインと同じパッケージにJavaクラスを追加します。
kometen 16

5
matcher.setUseSuffixPatternMatch(false)サフィックスの一致を完全に無効にするために使用します。
Gian Marco Gherardi 2017年

これは私にとってソリューションの半分にすぎませんでした。@Paul Aererの回答を参照してください。
8bitjunkie 2018

87

Martin Freyの回答に加えて、RequestMapping値に末尾のスラッシュを追加することでこれを修正することもできます。

/path/{variable}/

この修正は保守性をサポートしないことに注意してください。現在では、すべてのURIに末尾にスラッシュを付ける必要があります。これは、APIユーザーや新しい開発者にはわかりにくいものです。すべてのパラメーターにが含まれている.とは限らないため、断続的なバグが発生する可能性もあります


2
それもよりクリーンなソリューションです。IEがサフィックスに従ってAcceptヘッダーを設定する難しい方法を見つけなければなりませんでした。だから私はいくつかの.docリクエストマッピングに投稿したかった、そして私はいつも新しいhtmlページの代わりにダウンロードを得た。このアプローチはそれを修正しました。
Martin Frey

これは私にとって最も簡単な解決策であり、私の問題を解決しました。多くの場合、正規表現は少しやり過ぎに思われます
リカルド・コス14

7
しかし、AngularJSのデフォルトの動作と衝突して、末尾のスラッシュを自動的に削除します。これは最新のAngularリリースで設定できますが、何が起こっているのかわからない場合は何時間も追跡する必要があります。
dschulten 2014

1
@dschultenおかげで、デバッグの時間を節約できました。それにもかかわらず、HTPPリクエストでは末尾のスラッシュが必要になることを回答で言及する必要があります。
ホフマン

1
これはとても危険です!APIを実装する人は誰もそれを期待しないので、私は確かにそれをお勧めしません。非常に保守不可能です。
sparkyspider

32

Spring Boot Rest Controllerでは、次の手順でこれらを解決しました:

RestController:

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

そして残りのクライアントから:

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

2
この答えは、機能するために末尾のスラッシュに依存しています。
8bitjunkie 2018

2
チャームのように機能します(末尾のスラッシュもありません)。ありがとうございました!
18

27

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

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

値= "/username/{id:.+}"は 機能します

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


中括弧は正規表現を評価し、すでにいくつかの周りを持っているからだid
8bitjunkie

15

/somepath/{variable:.+}Java requestMappingタグで動作します。


うまくいかなかったものを示していないので、私はこの答えを好みます。
johnnieb 2017年

複数のドットが含まれるメールアドレスでは機能しません。
8bitjunkie

1
@ 8bitjunkie Sth "/{code:.+}"は、1つではなく多くのドットで61.12.7機能します。つまり、IE でも機能します。k.a.p@o.i.n
tryingHard

13

純粋に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;
    }
}

ありがとう、解決してくれました。また、非常にクリーンで明示的です。+1
bkis

11

この問題を回避する非常に簡単な方法の1つは、末尾にスラッシュを追加することです...

例えば:

使用する :

/somepath/filename.jpg/

の代わりに:

/somepath/filename.jpg

11

Spring Bootでは、正規表現は次のような問題を解決します

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

これは1つのドットに対してのみ機能することに注意してください。メールアドレスには対応していません。
8bitjunkie

1
@ 8bitjunkie Sth "/{code:.+}"は、1つのドットではなく多くのドットで61.12.7機能します。つまり、k.a.p@o.i.n
IE

1
@ 8bitjunkie IPアドレスでテストしました。それは非常にうまくいきます。つまり、複数のドットに対して機能します。
Dapper Dan

6

Spring 4.2のパス名にメールアドレスを含む完全なソリューションは、

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

これをapplication-xmlに追加します


賛成票-これがここでの唯一の回答です。これにより、ContentNegotiationManagerFactoryBeanとcontentNegotiationManagerの両方の構成アイテムが必要
8bitjunkie

5

Spring 3.2.xとを使用している場合は、次のコードを<mvc:annotation-driven />作成します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;
    }
}

次に、これをMVC構成xmlに入れます。

<bean class="spring.DoNotTruncateMyUrls" />

ContentNegotiationManagerに関連していますか?
Kanagavelu Sugumar 2013年

私のコードでは、URLが切り捨てられないようにRequestMappingHandlerMappingのみを構成しています。ContentNegotiationManagerは別の獣です。
ジュッカ2013年

2
これは古いですが、これは本当に必要ありませんBeanPostProcessor。使用するWebMvcConfigurationSupport場合は、requestMappingHandlerMapping @Beanメソッドをオーバーライドできます。XML構成を使用する場合は、独自のRequestMappingHandlerMappingBeanを宣言し、そのプロパティを宣言するだけです。
Sotirios Delimanolis 2013年

どうもありがとうございました。同じ問題に対してさまざまな解決策を試してみましたが、この解決策だけがうまくいきました。:-)
私たちはボルグで、

3

最後に私はSpring Docsで解決策を見つけました:

ファイル拡張子の使用を完全に無効にするには、次の両方を設定する必要があります。

 useSuffixPatternMatching(false), see PathMatchConfigurer

 favorPathExtension(false), see ContentNegotiationConfigurer

これを私のWebMvcConfigurerAdapter実装に追加すると問題が解決しました:

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

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

2

私にとって

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

動作しますが、リクエストURLの「ドット」も「%2E」としてエンコードした場合にのみ機能します。ただし、URLがすべてであることを必要とします...これは有効ですが、「標準」エンコーディングではありません。バグのような気がします:|

「末尾のスラッシュ」の方法と同様の別の回避策は、ドット「インライン」を持つ変数を移動することですex:

@GetMapping(パス= "/ {変数名} / a")

これで、すべてのドットが保持され、変更や正規表現は必要ありません。


1

Spring 5.2.4(Spring Boot v2.2.6.RELEASE)以降 PathMatchConfigurer.setUseSuffixPatternMatchContentNegotiationConfigurer.favorPathExtension非推奨(https://spring.io/blog/2020/03/24/spring-framework-5-2-5-available-now and https://github.com/spring-projects/spring-framework/issues/24179)。

実際の問題は、クライアントが特定のメディアタイプ(.comなど)をリクエストし、Springがデフォルトでそれらすべてのメディアタイプを追加したことです。ほとんどの場合、RESTコントローラーはJSONのみを生成するため、要求された出力形式(.com)はサポートされません。この問題を克服するには、レストコントローラ(または特定のメソッド)を更新して「出力」形式(@RequestMapping(produces = MediaType.ALL_VALUE))をサポートし、ドット({username:.+})です。

例:

@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以降では、登録済みのサフィックス(メディアタイプ)のみが一致します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.