URI ディープリンクを照合する

階層 URI をパターンと照合して引数を抽出するには、UriDeepLinkMatcher を使用します。kotlinx.serialization を使用して、一致する引数をキー クラスに逆シリアル化します。

UriDeepLinkMatcher を作成するには、パターン DeepLinkUri と対応するキーのシリアライザーを指定します。

階層型以外の URI やカスタム スキーム(tel: など)については、カスタム ディープリンク マッチャーを作成するをご覧ください。

サポートされている照合パターン

UriDeepLinkMatcher は、スキーム、オーソリティ、パス、クエリ、フラグメントの 5 つのコンポーネントに基づいて URI を照合します。以降のセクションでは、各コンポーネントでサポートされているパターン構文、引数プレースホルダ、照合ルールについて説明します。

スキーム マッチング

URI パターンにスキームがない場合、httphttps の両方が一致します。特定のスキームに一致させるには、パターンにスキームを含めます。例外として、パターン内の http スキームは httphttps の両方のリクエスト URI に一致しますが、パターン内の httpshttps リクエストにのみ一致します。

パターン URI リクエスト URI 一致
www.example.com https://www.example.com
www.example.com http://www.example.com
http://www.example.com http://www.example.com
http://www.example.com https://www.example.com
https://www.example.com http://www.example.com
myapp://www.example.com myapp://www.example.com

権限のマッチング

UriDeepLinkMatcher は、URI 権限(ホストとオプションのポート)に対して大文字と小文字を区別しない完全一致を実行します。プレースホルダまたはワイルドカードは権限でサポートされておらず、引数は抽出されません。

パターン URI リクエスト URI 一致
example.com https://example.com
example.com https://EXAMPLE.COM
example.com https://sub.example.com
example.com https://www.example.com
example.com https://example.com:8080
example.com:8080 https://example.com:8080
example.com:8080 https://example.com

パスマッチング

次のパスパターンがサポートされています。

パターン URI リクエスト URI 一致 抽出された引数
www.example.com/users https://www.example.com/users なし
www.example.com/users/{id} https://www.example.com/users/123 id: "123"
www.example.com/users/{first}-{last} https://www.example.com/users/john-doe first: "john"last: "doe"
www.example.com/users/{id}/profile https://www.example.com/users//profile id: ""(空の文字列)
www.example.com/users/user_{id} https://www.example.com/users/user_123 id: "123"
www.example.com/users/{userId}/posts/{postId} https://www.example.com/users/123/posts/456 userId: "123"postId: "456"
www.example.com/users/.* https://www.example.com/users/john-doe なし
www.example.com/users https://www.example.com/users/ ❌(末尾のスラッシュにより余分なセグメントが作成される) なし

クエリ マッチング

リクエスト URI のクエリ パラメータの順序は、パターン URI の順序と一致する必要はありません。また、リクエスト URI には存在するがパターン URI には存在しないパラメータは無視されます。

次のクエリ パラメータ パターンがサポートされています。

パターン URI リクエスト URI 抽出された引数
www.example.com/users?name={name} https://www.example.com/users?name=john name: "john"
www.example.com/users?name={name} https://www.example.com/users?name= name: ""(空の文字列)
www.example.com/users?{rawQuery} https://www.example.com/users?anything&else rawQuery: ["anything", "else"]
www.example.com/users?type=user_{id} https://www.example.com/users?type=user_123 id: "123"
www.example.com/users?name={first}_{last} https://www.example.com/users?name=john_doe first: "john"last: "doe"
www.example.com/users?list={list} https://www.example.com/users?list=10&list=20 list: ["10", "20"]
www.example.com/users?name={name}&{other} https://www.example.com/users?name=john&tab=info name: "john"other: ["tab=info"]
www.example.com/users?type=user_.* https://www.example.com/users?type=user_admin type: "admin"

フラグメント マッチング

次のフラグメント パターン タイプがサポートされています。

パターン URI リクエスト URI 抽出された引数
www.example.com/#section1 https://www.example.com/#section1 なし
www.example.com/#section_{id} https://www.example.com/#section_123 id: "123"
www.example.com/#section_.* https://www.example.com/#section_123 なし

サポートされるデータタイプ

UriDeepLinkMatcher は、URI 引数のプリミティブ型、列挙型、コレクション、カスタム オブジェクトへの逆シリアル化をサポートしています。シリアル化は次の 2 つのカテゴリに分類されます。

  • 標準シリアル化: kotlinx.serialization を使用して次のものに逆シリアル化します。
    • プリミティブ(BooleanIntLongFloatDoubleCharByteShort)と String
    • 列挙型
    • プリミティブ、文字列、列挙型の SetList、または Array
    • ネストされた @Serializable クラス(プロパティが個々の URI プレースホルダにフラット化される)
  • DeepLinkSerializer を使用したカスタム シリアル化: 単一の String とカスタム オブジェクト、外部型(java.time.LocalDate など)、カスタム区切りコレクションの間で変換します。

標準のシリアル化

UriDeepLinkMatcher は、カスタム シリアライザーの実装を必要とせずに、標準タイプとフラット化された構造で動作します。

プリミティブと文字列

UriDeepLinkMatcher は、プリミティブ型(BooleanIntLongFloatDoubleCharByteShort)と String を自動的にデコードします。

列挙型

列挙値は、列挙要素名と大文字と小文字を区別して照合されます。

繰り返されるクエリのコレクション

キーが繰り返されるクエリ パラメータ(?id=10&id=20 など)は、List<T>Set<T>、または Array<T> に自動的に逆シリアル化されます。ここで、T はプリミティブ型、String、または列挙型です。

ネストされた @Serializable クラス

NavKey に型が別の @Serializable クラスであるプロパティが含まれている場合、UriDeepLinkMatcher はそのプロパティをフラット化するため、ネストされたクラスの各プロパティは同じ名前の個々の URI パラメータに直接マッピングされます。

カスタム オブジェクト(Filter(key = "brand", value = "pixel") など)、外部型(java.time.LocalDate など)、カスタム区切り文字列(カンマ区切り値など)を逆シリアル化するには、DeepLinkSerializer<T> を拡張します。

DeepLinkSerializer<T> は、StringT の間で変換を行う抽象 KSerializer<T> です。

abstract class DeepLinkSerializer<T : Any> : KSerializer<T> {
    abstract val serialName: String
    abstract fun deserialize(value: String): T
    abstract fun serialize(value: T): String
}

たとえば、次のスニペットで使用されている FilterFilterSerializer の定義について考えてみましょう。

単一のカスタム オブジェクト

単一の URI パラメータ文字列(?filter=brand:google など)からオブジェクトをデコードするには、プロパティに @Serializable(with = ...) アノテーションを付けます。

繰り返しクエリ パラメータのカスタム オブジェクト

繰り返しクエリ パラメータをカスタム オブジェクトのコレクション(List<T>Set<T>Array<T>)に逆シリアル化するには、要素型 TDeepLinkSerializer<T> を実装し、プロパティの型引数に @Serializable(with = ...) でアノテーションを付けます。

単一のパラメータ内の区切り文字付きコレクション

カンマ区切りまたはカスタム区切りの値(?ids=1,2,3 など)をコレクションに解析するには、コレクション型全体DeepLinkSerializer を実装し、プロパティに @Serializable(with = ...) のアノテーションを付けます。

引数の検証と一致結果

UriDeepLinkMatcher は、不一致null を返して他のマッチャーを試せるようにする)とサポートされていない構成(例外をスローする)を区別します。

不一致

不一致は、受信リクエスト URI がパターンまたは型の要件を満たしていない場合に発生します。

  • 必須パラメータがありません: デフォルト値のない非 null のキー プロパティで、対応する URI パラメータがリクエスト URI に存在しない。
  • 型の解析の失敗: 抽出された引数値が、想定されるプロパティ型(Int プロパティの "abc" など)に解析できない。

不一致が発生すると、UriDeepLinkMatcher.matchnull を返し、後続のマッチャーを評価できるようになります。

デフォルト値、ネストされたオブジェクト、列挙型で構成されたキー クラスとマッチャーについて考えてみましょう。

次の表は、さまざまなリクエスト URI の一致結果を示しています。

リクエスト URI 結果のデコード 試合結果
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE 成功(すべてのパラメータが指定されている) UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE)))
https://www.example.com/map/paris?style=dark 成功zoom はデフォルトで 12layer はデフォルトで STANDARD UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map/paris?zoom=&style=dark 成功(省略可能なクエリ パラメータが空の場合、デフォルトの 12 が使用されます) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map?style=dark 不一致(必須の location パラメータがない) null
https://www.example.com/map/paris?zoom=close&style=dark 不一致"close"Int ではない) null
https://www.example.com/map/paris?style=dark&layer=HYBRID 不一致"HYBRID" が列挙型に含まれていない) null

サポートされていない設定

キー クラスにサポートされていないデータ型が含まれている場合、UriDeepLinkMatchernull を返すのではなく、照合中に例外をスローします。

  • マップと多次元コレクション: UriDeepLinkMatcher は、プリミティブ、文字列、列挙型、または DeepLinkSerializer でアノテーションされたカスタム型の 1 次元コレクションのみをサポートします。Map 型は IllegalArgumentException をスローしますが、ネストされたコレクション(List<List<String>> など)は SerializationException をスローします。
  • アノテーションのないカスタム オブジェクト コレクション: 要素の型が DeepLinkSerializer でアノテーションされていない場合、カスタム型(List<Filter> など)のコレクションは SerializationException をスローします。
  • フラット化されていないネストされたクラス: ネストされた @Serializable クラスは、DeepLinkSerializer なしで単一のプレースホルダ(?user={user} など)にマッピングできません。

UriMatchResultの比較

UriMatchResult インスタンスは、次の基準で順位付けされます。

  1. MatchResult 型: UriMatchResult は他の MatchResult 型よりもランクが高くなります。
  2. 完全なパス: リテラル パスの一致は、プレースホルダまたはワイルドカードの一致よりもランクが高くなります。
  3. パス引数の数: パス引数の数が多いほど、ランクが高くなります。
  4. 引数の有無: 引数をキャプチャする一致は、引数をキャプチャしない一致よりもランクが高くなります。
  5. 引数の合計数: 引数(パス、クエリ、フラグメント)の合計数が最終的なタイブレークになります。

UriDeepLinkMatcher をカスタマイズ

UriDeepLinkMatcher は、サブクラス化して URI の照合と引数の抽出の動作をカスタマイズできる open クラスです。

  • matchRequest: 受信 DeepLinkRequest の最上位のマッチング エントリ ポイント。URI の照合前にリクエストの追加情報を検査したり、カスタムの前提条件を適用したりするには、このメソッドをオーバーライドします。
  • matchUri: 構成されたパターンに対して DeepLinkUri を照合します。super.matchUri を呼び出す前に、このメソッドをオーバーライドして、受信 URI をインターセプトして正規化します(たとえば、動的サブドメインやレガシー パス形式を書き換えます)。
  • matchArguments: 提供された serializer を使用して、抽出されたパス、クエリ、フラグメント引数マップをナビゲーション キー インスタンスに逆シリアル化します。これをオーバーライドして、キーのインスタンス化の前に動的な値を挿入したり、引数を変換したりします。

次の例は、照合前にレガシー URL パス接頭辞を正規化するために UriDeepLinkMatcher をサブクラス化する方法を示しています。