مطابقة الروابط لصفحات في التطبيق المستندة إلى معرّف الموارد المنتظم (URI)

لمطابقة معرّفات الموارد المنتظمة (URI) الهرمية مع الأنماط واستخراج الوسيطات، استخدِم UriDeepLinkMatcher. وتعتمد على kotlinx.serialization لإلغاء تسلسل الوسيطات المطابقة في فئات المفاتيح.

لإنشاء UriDeepLinkMatcher، قدِّم النمط DeepLinkUri وأداة التسلسل للمفتاح المقابل:

بالنسبة إلى معرّفات الموارد المنتظمة (URI) غير الهرمية أو المخططات المخصّصة (مثل tel:)، يُرجى الاطّلاع على إنشاء أدوات مطابقة مخصّصة للروابط لصفحات معيّنة.

أنماط المطابقة المتوافقة

يطابق UriDeepLinkMatcher معرّفات الموارد المنتظمة (URI) استنادًا إلى مكوّناتها الخمسة: المخطط، والسلطة، والمسار، وطلب البحث، والمقتطف. توضّح الأقسام التالية البنية المتوافقة للأنماط والعناصر النائبة للوسيطات وقواعد المطابقة لكل مكوّن.

مطابقة المخطط

إذا لم يتوفّر أي نظام في نمط معرّف الموارد المنتظم (URI)، تتم مطابقة كل من http وhttps. لمطابقة مخطّط معيّن، يجب تضمينه في النمط. كاستثناء، يتطابق المخطط http في النمط مع عناوين URI للطلب http وhttps، بينما يتطابق المخطط https في النمط مع طلبات https فقط.

معرّف الموارد المنتظم (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) إلى أنواع أساسية وتعدادات ومجموعات وعناصر مخصّصة. يندرج التسلسل ضمن فئتين:

  • التسلسل العادي: يستخدم kotlinx.serialization لإلغاء التسلسل إلى:
    • الأساسيات (Boolean وInt وLong وFloat وDouble وChar وByte وShort) وString
    • تعدادات
    • ‫Set أو List أو Array من العناصر الأساسية أو السلاسل أو التعدادات
    • فئات @Serializable مدمجة (يتم دمج خصائصها في عناصر نائبة فردية لمعرّفات الموارد المنتظمة)
  • التسلسل المخصّص باستخدام DeepLinkSerializer: يحوّل بين String واحد وعناصر مخصّصة أو أنواع خارجية (مثل java.time.LocalDate) أو مجموعات محدّدة بشكل مخصّص.

نشر الحلقات على نحو متسلسِل عادي

تعمل UriDeepLinkMatcher تلقائيًا مع الأنواع العادية والبُنى المبسّطة بدون الحاجة إلى تنفيذ عمليات تسلسل مخصّصة.

الأنواع الأساسية والسلاسل

يفكّ UriDeepLinkMatcher ترميز الأنواع الأساسية تلقائيًا (Boolean وInt وLong وFloat وDouble وChar وByte وShort) و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> هي KSerializer<T> مجرّدة تحوّل بين String وT:

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

على سبيل المثال، لنلقِ نظرة على تعريفَي Filter وFilterSerializer المستخدَمين في المقتطفات التالية:

عناصر مخصّصة فردية

لفك ترميز كائن من سلسلة مَعلمات معرّف موارد منتظم (URI) واحدة (مثل ?filter=brand:google)، يمكنك إضافة تعليق توضيحي إلى السمة باستخدام @Serializable(with = ...):

العناصر المخصّصة في مَعلمات طلب البحث المتكرّرة

لإلغاء تسلسل معلمات طلب البحث المتكررة في مجموعة من العناصر المخصّصة (List<T> أو Set<T> أو Array<T>)، يمكنك تنفيذ DeepLinkSerializer<T> من أجل نوع العنصر T وإضافة تعليقات توضيحية إلى وسيطة النوع للسمة باستخدام @Serializable(with = ...):

المجموعات المحدّدة في مَعلمات فردية

لتحليل القيم المفصولة بفواصل أو المحدّدة بشكل مخصّص (مثل ?ids=1,2,3) إلى مجموعة، نفِّذ DeepLinkSerializer لنوع المجموعة بالكامل وأضِف تعليقات توضيحية إلى السمة باستخدام @Serializable(with = ...):

التحقّق من الوسيطات ونتائج المطابقة

يميّز UriDeepLinkMatcher بين حالات عدم التطابق (يعرض null حتى يمكن تجربة أدوات مطابقة أخرى) والإعدادات غير المتوافقة (يُظهر استثناءً).

حالات عدم التطابق

يحدث عدم تطابق عندما لا يستوفي عنوان URI للطلب الوارد متطلبات النمط أو النوع:

  • المَعلمات المطلوبة غير متوفّرة: خصائص المفاتيح التي لا يمكن أن تكون فارغة ولا تتضمّن قيمًا تلقائية، ولا تتوفّر مَعلمات معرّف الموارد المنتظم (URI) المقابلة لها في معرّف الموارد المنتظم (URI) للطلب.
  • أخطاء تحليل النوع: قيم الوسيطات المستخرَجة التي لا يمكن تحليلها إلى نوع السمة المتوقّع (على سبيل المثال، "abc" لسمة Int).

عند حدوث عدم تطابق، تعرض الدالة UriDeepLinkMatcher.match القيمة null، ما يسمح بتقييم أدوات المطابقة اللاحقة.

لنفترض أنّ لدينا فئة مفتاح ومطابِق تم ضبطهما باستخدام قيم تلقائية وكائنات متداخلة وتعدادات:

يوضّح الجدول التالي نتائج المطابقة لعناوين URI مختلفة للطلبات:

عنوان URI للطلب نتيجة فك الترميز نتيجة المباراة
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE Success (تم توفير جميع المَعلمات) UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE)))
https://www.example.com/map/paris?style=dark تم الإجراء بنجاح (يتم ضبط zoom تلقائيًا على 12، وlayer على 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

الإعدادات غير المتوافقة

إذا كانت فئة المفتاح تحتوي على أنواع بيانات غير متوافقة، يعرض UriDeepLinkMatcher استثناءً أثناء المطابقة بدلاً من عرض null.

  • الخرائط والمجموعات المتعدّدة الأبعاد: لا تتيح UriDeepLinkMatcher استخدام سوى المجموعات الأحادية الأبعاد من العناصر الأساسية أو السلاسل أو التعدادات أو الأنواع المخصّصة التي تمّت إضافة تعليقات توضيحية إليها باستخدام DeepLinkSerializer. Map types throw an IllegalArgumentException, while nested collections (such as List<List<String>>) throw a SerializationException.
  • مجموعات العناصر المخصّصة غير المزوّدة بتعليقات توضيحية: مجموعات الأنواع المخصّصة (مثل List<Filter>) تعرض SerializationException ما لم تتم إضافة تعليق توضيحي إلى نوع العنصر باستخدام DeepLinkSerializer.
  • الصفوف المُدمَجة غير المسطّحة: لا يمكن ربط صفوف @Serializable المُدمَجة بعنصر نائب واحد (مثل ?user={user}) بدون DeepLinkSerializer.

مقارنة UriMatchResult

يتم ترتيب مثيلات UriMatchResult باستخدام المعايير التالية بالترتيب:

  1. نوع MatchResult: يحتلّ النوع UriMatchResult مرتبة أعلى من أنواع MatchResult الأخرى.
  2. المسار الدقيق: تحصل مطابقات المسار الحرفية على ترتيب أعلى من مطابقات العناصر النائبة أو أحرف البدل.
  3. عدد وسيطات المسار: يتم ترتيب التطابقات التي تحتوي على عدد أكبر من وسيطات المسار في مرتبة أعلى.
  4. وجود وسيطات: يتم ترتيب النتائج المطابقة التي تتضمّن وسيطات في مرتبة أعلى من تلك التي لا تتضمّن وسيطات.
  5. إجمالي عدد الوسيطات: يُعدّ إجمالي عدد الوسيطات (المسار وطلب البحث والمقتطف) هو العامل النهائي لكسر التعادل.

تخصيص UriDeepLinkMatcher

‫UriDeepLinkMatcher هي فئة open يمكنك إنشاء فئة فرعية منها لتخصيص سلوك مطابقة معرّف الموارد المنتظم (URI) واستخراج الوسيطات:

  • matchRequest: نقطة دخول مطابقة ذات مستوى أعلى لـ DeepLinkRequest واردة. يمكنك إلغاء هذا الإجراء لفحص الإضافات في الطلب أو تطبيق شروط مسبقة مخصّصة قبل مطابقة معرّف الموارد المنتظم (URI).
  • ‫matchUri: تطابق DeepLinkUri مع النمط الذي تم ضبطه. يمكنك إلغاء هذه الطريقة لاعتراض معرّفات الموارد المنتظمة (URI) الواردة وتسويتها (على سبيل المثال، إعادة كتابة النطاقات الفرعية الديناميكية أو تنسيقات المسارات القديمة) قبل استدعاء super.matchUri.
  • ‫matchArguments: تلغي تسلسل المسار المستخرَج وخرائط وسيطات طلب البحث والتقسيم إلى مثيل مفتاح تنقّل باستخدام serializer المقدَّم. يمكنك إلغاء هذه الطريقة لإدخال قيم ديناميكية أو تحويل الوسيطات قبل إنشاء المفتاح.

يوضّح المثال التالي كيفية إنشاء فئة فرعية من UriDeepLinkMatcher لتسوية بادئات مسارات عناوين URL القديمة قبل المطابقة: