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

لمطابقة معرّفات الموارد المنتظمة الهرمية مع الأنماط واستخراج الوسيطات، استخدِم UriDeepLinkMatcher. تعتمد هذه الطريقة على kotlinx.serialization لتحويل السلسلة إلى تنسيقها الأصلي للمَعلمات المتطابقة إلى فئاتك الرئيسية.

لإنشاء UriDeepLinkMatcher، يجب توفير نمط DeepLinkUri وبرنامج التسلسل للمفتاح المقابل:

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

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

تطابق UriDeepLinkMatcher عناوين URI استنادًا إلى مكوّناتها الخمسة: المخطط والسلطة والمسار والاستعلام والجزء. تصف الأقسام التالية بنية النمط المتوافق وعناصر نائب الوسيطة وقواعد المطابقة لكل مكوّن.

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

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

معرّف الموارد المنتظم للنمط عنوان 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 للطلب المطابقة
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 للطلب المطابقة الوسيطات المستخرَجة
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 للطلب الوسيطات المستخرَجة
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 للطلب الوسيطات المستخرَجة
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 لطلب وارد متطلبات النمط أو النوع:

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

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

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

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

عنوان 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 هي 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 الخطأ IllegalArgumentException، بينما تُصدر المجموعات المتداخلة (مثل List<List<String>>) الخطأ 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 القديمة قبل المطابقة: