Spring経由のREST:リソースへの厳密なURLマッピング

ご存知のように、Spring MVCはバージョン2.5以降、新しい注釈ベースの構成モデルを使用しています。 これらのパンを入手するには、構成ファイルで<mvc:annotation-driven />タグを使用する必要があります。 このタグは、アプリケーションコンテキストにDefaultAnnotationHandlerMappingおよびAnnotationMethodHandlerAdaptorを登録します。







DefaultAnnotationHandlerMappingは、クラス内で@RequestMappingアノテーションを検索し、それぞれにハンドラーへのマッピングを作成します。同じハンドラーに対して、サフィックス「。*」および「/」が付いた2つのマッピングが追加されます。 AnnotationMethodHandlerAdaptorのタスクは、HTTP要求の処理を正しいメソッドに委任することです。このメソッドでは、アノテーションが通知されます。



したがって、次のコントローラーのために



@Controller @RequestMapping("/service/hotels") public class HotelsCollectionController { @Autowired private HotelService hotelService; @RequestMapping(method = RequestMethod.GET) public String getHotelList(Model model) { List<Hotel> list = hotelService .getHotelList(); model.addAttribute("hotels", list); return "service/hotels/read"; } public void setHotelService(HotelService hotelService) { this.hotelService = hotelService; } }
      
      







/ service / hotels、/ service / hotels 、および/service/hotels.*へのリクエストに対して3つのマッピングを取得します



最初の2つの目的は明らかにユーザーフレンドリーなアプリケーションにすることであり、後者はContentNegotiatingViewResolverでリソースの最適な表現を決定するために使用されます。



すべてがうまくいくだろう...



この方法でハンドラーにリクエストをマッピングするアノテーションを使用して、WebサービスにRESTfulなアプローチをとろうとすると、問題が発生します。 RESTのURLはリソースであるため、異なるURLは異なるリソースを指すようになり、アプリケーションは不用意にそれらを使用したり、存在しないリソースに対して同様の暗黙的なハンドラーを使用したりしないでください。 この問題は、コレクションマーカーとしてスラッシュまたはアスタリスクを使用したいという欲求によって複雑になります。つまり、この場合、 / service / hotelの代わりに、少なくとも/ service / hotel /または/ service / hotel / *をすべてのホテルのリストのURLとして使用できますあまり直観的ではなく、拡張性さえありません。

大雑把に言えば、404を返す必要があり、そのためには、そのような暗黙のマッピングの生成をオフにする必要があります。



別のことは、春自体がこれらの暗黙のアドレスの処理を指しているということです。 明らかな方法は、アプリケーションのDefaultAnnotationHandlerMappingを構成し、そのdefaultSuffixPatternプロパティをfalseに設定することです。これは、思っているほど単純ではありません。



一見すると次のことがわかります。



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







それでは、コンテキストを作成するときに何が起こるかを一緒に考えましょう。 springは<mvc:annotation-driven />を検出するとすぐに、1つのDefaultAnnotationHandlerMappingを作成し、コンテキストに配置します。 上記の特定のBeanを見つけるとすぐに、彼女はDefaultAnnotationHandlerMappingの別のインスタンスを作成し、それをコンテキストに入れます。 したがって、クールなアプリケーションにはDefaultAnnotationHandlerMappingの 2つのインスタンスがあり、1つはデフォルト設定で、もう1つは必要に応じて構成されます。 どのHandlerMappingが最初にHTTPリクエストを噛むかは、その内部順序に依存し、制御不能です(まあ、ほとんど...順序付けを適用できますが、この場合は松葉杖です)。



/ service / hotelsには違いはありませんが、 / service / hotels/service/hotels.*には違いがあります 。 おそらく、 ContentNegotiatingViewResolverを使用して、クライアントのリソースの最適な表現についてクライアントとネゴシエートします。この場合、暗黙的に作成されたマッピングのこの興味深い重要なRESTプロセスの制御を実際に失います。 それらへのクエリの結果は、正しい表示でも間違った表示でもかまいません。状況によっては500の例外が発生する場合があります。詳細には触れませんが、象はこの動作を避ける必要があることを理解しています。



これを回避するには、 HandlerMappingのいずれかをコンテキストから削除する必要があります。 したがって、 <mvc:annotation-driven />を削除し、 AnnotationMethodHandlerAdaptorを手作業で登録するという大変な作業を行う必要があります。



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







これは必要なことを行う必要があり、既存のリソースへの暗黙的なURL、つまり存在しないへの明示的なURLに対して、保証された望ましい404を取得します。



All Articles