Precyzyjne linki do pasujących identyfikatorów URI

Aby dopasować hierarchiczne identyfikatory URI do wzorców i wyodrębnić argumenty, użyj UriDeepLinkMatcher. Do deserializacji dopasowanych argumentów do klas kluczy używa kotlinx.serialization.

Aby utworzyć UriDeepLinkMatcher, podaj wzorzec DeepLinkUri i serializator dla odpowiedniego klucza:

W przypadku identyfikatorów URI bez hierarchii lub schematów niestandardowych (takich jak tel:) zapoznaj się z artykułem Tworzenie niestandardowych dopasowań precyzyjnych linków.

Obsługiwane wzorce dopasowania

UriDeepLinkMatcher dopasowuje identyfikatory URI na podstawie ich 5 komponentów: schematu, urzędu, ścieżki, zapytania i fragmentu. W sekcjach poniżej opisano obsługiwaną składnię wzorców, obiekty zastępcze argumentów i reguły dopasowywania dla każdego komponentu.

Dopasowanie schematu

Jeśli we wzorcu identyfikatora URI nie ma schematu, dopasowywane są zarówno http, jak i https. Aby dopasować konkretny schemat, uwzględnij go we wzorcu. Wyjątkiem jest schemat http we wzorcu, który pasuje zarówno do identyfikatorów URI żądania http, jak i https, podczas gdy schemat https we wzorcu pasuje tylko do żądań https.

Identyfikator URI wzorca Identyfikator URI żądania Dopasowanie
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 ✅

Dopasowywanie urzędów

UriDeepLinkMatcher wykonuje dopasowanie ścisłe, w którym wielkość liter nie jest rozróżniana, w identyfikatorze URI (host i opcjonalny port). Symbole zastępcze i wieloznaczne nie są obsługiwane w elemencie authority i nie są wyodrębniane żadne argumenty:

Identyfikator URI wzorca Identyfikator URI żądania Dopasowanie
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 ❌

Dopasowywanie ścieżek

Obsługiwane są te wzorce ścieżek:

Identyfikator URI wzorca Identyfikator URI żądania Dopasowanie Wyodrębnione argumenty
www.example.com/users https://www.example.com/users ✅ Brak
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: "" (pusty ciąg znaków)
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 ✅ Brak
www.example.com/users https://www.example.com/users/ ❌ (ukośnik na końcu tworzy dodatkowy segment) Nie dotyczy

Dopasowywanie zapytań

Kolejność parametrów zapytania w identyfikatorze URI żądania nie musi być zgodna z kolejnością we wzorcu identyfikatora URI. Dodatkowo parametry obecne w identyfikatorze URI żądania, ale nie we wzorcu identyfikatora URI, są ignorowane.

Obsługiwane są te wzorce parametrów zapytania:

Identyfikator URI wzorca Identyfikator URI żądania Wyodrębnione argumenty
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: "" (pusty ciąg znaków)
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"

Dopasowywanie fragmentów

Obsługiwane są te typy wzorców fragmentów:

Identyfikator URI wzorca Identyfikator URI żądania Wyodrębnione argumenty
www.example.com/#section1 https://www.example.com/#section1 Brak
www.example.com/#section_{id} https://www.example.com/#section_123 id: "123"
www.example.com/#section_.* https://www.example.com/#section_123 Brak

Obsługiwane typy danych

UriDeepLinkMatcher obsługuje deserializację argumentów identyfikatora URI na typy podstawowe, typy wyliczeniowe, kolekcje i obiekty niestandardowe. Publikacja w odcinkach dzieli się na 2 kategorie:

  • Standardowa serializacja: używa kotlinx.serialization do deserializacji do:
    • Typy podstawowe (Boolean, Int, Long, Float, Double, Char, Byte, Short) i String
    • Wartości w polu enum
    • Set, List lub Array elementów podstawowych, ciągów znaków lub typów wyliczeniowych
    • Zagnieżdżone klasy @Serializable (których właściwości są spłaszczone do poszczególnych obiektów zastępczych identyfikatorów URI)
  • Niestandardowa serializacja za pomocą DeepLinkSerializer: konwertuje między pojedynczym obiektem String a obiektami niestandardowymi, typami zewnętrznymi (takimi jak java.time.LocalDate) lub kolekcjami z niestandardowymi separatorami.

Standardowa serializacja

UriDeepLinkMatcher działa od razu po uruchomieniu w przypadku standardowych typów i spłaszczonych struktur bez konieczności implementacji niestandardowych serializatorów.

Elementy podstawowe i ciągi znaków

UriDeepLinkMatcher automatycznie dekoduje typy podstawowe (Boolean, Int, Long, Float, Double, Char, Byte, Short) i String:

Wartości w polu enum

Wartości typu wyliczeniowego są dopasowywane do nazw elementów typu wyliczeniowego z uwzględnieniem wielkości liter:

Powtarzające się kolekcje zapytań

Parametry zapytania z powtarzającymi się kluczami (takimi jak ?id=10&id=20) są automatycznie deserializowane do List<T>, Set<T> lub Array<T>, gdzie T jest typem podstawowym, String lub typem wyliczeniowym:

Zagnieżdżone klasy @Serializable

Gdy NavKey zawiera właściwość, której typem jest inna klasa @Serializable, UriDeepLinkMatcher spłaszcza jej właściwości, tak aby każda właściwość klasy zagnieżdżonej była mapowana bezpośrednio na pojedynczy parametr identyfikatora URI o tej samej nazwie:

Aby deserializować obiekty niestandardowe (takie jak Filter(key = "brand", value = "pixel")), typy zewnętrzne (takie jak java.time.LocalDate) lub niestandardowe ciągi znaków rozdzielane separatorami (takie jak wartości rozdzielane przecinkami), rozszerz DeepLinkSerializer<T>.

DeepLinkSerializer<T> to abstrakcyjna klasa KSerializer<T>, która konwertuje między String a T:

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

Weźmy na przykład definicje Filter i FilterSerializer używane w tych fragmentach kodu:

Pojedyncze obiekty niestandardowe

Aby zdekodować obiekt z pojedynczego ciągu parametrów identyfikatora URI (np. ?filter=brand:google), dodaj do właściwości adnotację @Serializable(with = ...):

Obiekty niestandardowe w powtarzających się parametrach zapytania

Aby zdeserializować powtarzające się parametry zapytania do kolekcji obiektów niestandardowych (List<T>, Set<T> lub Array<T>), zaimplementuj DeepLinkSerializer<T> dla typu elementu T i dodaj adnotację do argumentu typu właściwości za pomocą @Serializable(with = ...):

Kolekcje rozdzielane w pojedynczych parametrach

Aby przeanalizować wartości rozdzielone przecinkami lub niestandardowymi separatorami (np. ?ids=1,2,3) i utworzyć z nich kolekcję, zaimplementuj DeepLinkSerializer dla całego typu kolekcji i dodaj do właściwości adnotację @Serializable(with = ...):

Wyniki weryfikacji i dopasowywania argumentów

UriDeepLinkMatcher rozróżnia niezgodności (zwraca null, aby można było wypróbować inne dopasowania) i nieobsługiwane konfiguracje (zgłasza wyjątek).

Niezgodności

Niezgodność występuje, gdy identyfikator URI żądania przychodzącego nie spełnia wymagań dotyczących wzorca lub typu:

  • Brak wymaganych parametrów: właściwości klucza, które nie mogą mieć wartości null i nie mają wartości domyślnych, a których odpowiadające im parametry identyfikatora URI nie występują w identyfikatorze URI żądania.
  • Błędy analizy typu: wyodrębnione wartości argumentów, których nie można przeanalizować na oczekiwany typ właściwości (np. "abc" dla właściwości Int).

W przypadku niezgodności UriDeepLinkMatcher.match zwraca null, co umożliwia ocenę kolejnych dopasowań.

Rozważ klasę klucza i dopasowanie skonfigurowane z wartościami domyślnymi, obiektami zagnieżdżonymi i typami wyliczeniowymi:

W tabeli poniżej znajdziesz wyniki dopasowania dla różnych identyfikatorów URI żądania:

Identyfikator URI żądania Wynik dekodowania Wynik dopasowania
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE Success (podano wszystkie parametry) UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE)))
https://www.example.com/map/paris?style=dark Sukces (zoom domyślnie 12, layer domyślnie STANDARD) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map/paris?zoom=&style=dark Sukces (pusty opcjonalny parametr zapytania używa domyślnej wartości 12) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map?style=dark Niezgodność (brak wymaganego parametru location) null
https://www.example.com/map/paris?zoom=close&style=dark Niezgodność ("close" nie jest Int) null
https://www.example.com/map/paris?style=dark&layer=HYBRID Niezgodność ("HYBRID" nie jest w wyliczeniu) null

Nieobsługiwane konfiguracje

Jeśli klasa klucza zawiera nieobsługiwane typy danych, UriDeepLinkMatcher zgłasza wyjątek podczas dopasowywania, zamiast zwracać null.

  • Mapy i kolekcje wielowymiarowe: UriDeepLinkMatcher obsługuje tylko jednowymiarowe kolekcje elementów podstawowych, ciągów tekstowych, typów wyliczeniowych lub typów niestandardowych z adnotacją DeepLinkSerializer. Typy Map zwracają błąd IllegalArgumentException, a kolekcje zagnieżdżone (takie jak List<List<String>>) – błąd SerializationException.
  • Kolekcje obiektów niestandardowych bez adnotacji: kolekcje typów niestandardowych (takich jak List<Filter>) zgłaszają błąd SerializationException, chyba że typ elementu ma adnotację DeepLinkSerializer.
  • Niespłaszczone klasy zagnieżdżone: klas zagnieżdżonych @Serializable nie można mapować na pojedynczy obiekt zastępczy (np. ?user={user}) bez DeepLinkSerializer.

Porównanie: UriMatchResult

Instancje UriMatchResult są klasyfikowane według tych kryteriów w podanej kolejności:

  1. Typ MatchResult: UriMatchResult ma wyższą pozycję niż inne typy MatchResult.
  2. Dokładna ścieżka: dopasowania ścieżki dosłownej mają wyższą pozycję niż dopasowania obiektów zastępczych lub symboli wieloznacznych.
  3. Liczba argumentów ścieżki: dopasowania z większą liczbą argumentów ścieżki mają wyższą pozycję.
  4. Obecność argumentów: dopasowania, które zawierają argumenty, mają wyższą pozycję niż te, które ich nie zawierają.
  5. Łączna liczba argumentów: łączna liczba argumentów (ścieżka, zapytanie, fragment) jest ostatecznym kryterium rozstrzygającym.

Dostosuj UriDeepLinkMatcher

UriDeepLinkMatcher to klasa open, którą możesz rozszerzyć, aby dostosować dopasowywanie identyfikatorów URI i wyodrębnianie argumentów:

  • matchRequest: punkt wejścia dopasowania najwyższego poziomu dla przychodzącego DeepLinkRequest. Zastąp tę metodę, aby sprawdzać dodatkowe informacje o żądaniu lub stosować niestandardowe warunki wstępne przed dopasowaniem identyfikatora URI.
  • matchUri: dopasowuje DeepLinkUri do skonfigurowanego wzorca. Zastąp tę metodę, aby przechwytywać i normalizować przychodzące identyfikatory URI (np. przepisywać dynamiczne subdomeny lub starsze formaty ścieżek) przed wywołaniem super.matchUri.
  • matchArguments: deserializuje wyodrębnione mapy argumentów ścieżki, zapytania i fragmentu do instancji klucza nawigacji za pomocą podanego serializer. Zastąp tę metodę, aby wstawić wartości dynamiczne lub przekształcić argumenty przed utworzeniem instancji klucza.

Poniższy przykład pokazuje, jak utworzyć podklasę UriDeepLinkMatcher, aby znormalizować starsze prefiksy ścieżek adresów URL przed dopasowaniem: