كيفية فك ترميز عنوان URL في JavaScript (دليل شامل)
توفّر لغة JavaScript الدالة decodeURIComponent() لفك ترميز مكوّنات URI الفردية، والدالة decodeURI() لفك ترميز عناوين URI الكاملة. استخدم try-catch للتعامل مع عناوين URI المشوّهة. كما توفّر واجهتا URL و URLSearchParams بدائل أكثر أماناً لتحليل عناوين URL.
decodeURIComponent() — فك ترميز مكوّن من مكوّنات URI
تُعدّ decodeURIComponent() الدالة الأساسية لفك ترميز السلاسل النصية المُرمَّزة بصيغة URL في JavaScript. فهي تفكّ ترميز جميع التسلسلات المُرمَّزة بالنسبة المئوية (percent-encoded)، وتُعيدها إلى محارفها الأصلية. استخدم هذه الدالة عند فك ترميز قيم معاملات الاستعلام، أو أجزاء المسار، أو أي مكوّن مفرد من مكوّنات URI.
// فك ترميز أساسي
console.log(decodeURIComponent('hello%20world'));
// "hello world"
console.log(decodeURIComponent('price%3D10%26qty%3D2'));
// "price=10&qty=2"
// فك ترميز محارف Unicode
console.log(decodeURIComponent('caf%C3%A9'));
// "cafe" (مع علامة النبرة)
console.log(decodeURIComponent('%E4%B8%AD%E6%96%87'));
// محارف صينية
// فك ترميز قيمة معامل استعلام من عنوان URL
const url = 'https://example.com/search?q=C%2B%2B%20%26%20Java';
const params = url.split('?')[1];
const value = params.split('=')[1];
console.log(decodeURIComponent(value));
// "C++ & Java"
تفكّ decodeURIComponent() ترميز جميع التسلسلات المُرمَّزة بالنسبة المئوية، بما في ذلك تسلسلات المحارف المحجوزة مثل %2F (/) و %3F (?). وهذا سلوك صحيح عند التعامل مع مكوّنات URI الفردية، لكنه قد يسبّب مشكلات إذا طُبِّق على عنوان URL كامل.
decodeURI() — فك ترميز عنوان URI كامل
تفكّ decodeURI() ترميز عنوان URI كامل مع الحفاظ على بنيته. وعلى خلاف decodeURIComponent()، فهي لا تفكّ ترميز التسلسلات التي تمثّل محارف URI المحجوزة مثل %2F (/) و %3F (?) و %23 (#) و %26 (&).
// decodeURI تحافظ على بنية URI
console.log(decodeURI('https://example.com/my%20page?q=hello%20world'));
// "https://example.com/my page?q=hello world"
// يُفكّ ترميز المسافات، لكن /، و ?، و = تبقى محفوظة
// المقارنة مع decodeURIComponent على عنوان URL كامل
console.log(decodeURIComponent('https%3A%2F%2Fexample.com%2Fpath'));
// "https://example.com/path" - يُفكّ الترميز بشكل صحيح إذا كان العنوان بأكمله مُرمَّزاً
// decodeURI لن تفكّ ترميز تسلسلات المحارف المحجوزة
console.log(decodeURI('path%2Fto%2Ffile'));
// "path%2Fto%2Ffile" - %2F لا يُفكّ ترميزه لأن / محرف محجوز
console.log(decodeURIComponent('path%2Fto%2Ffile'));
// "path/to/file" - %2F يُفكّ ترميزه
استخدم decodeURI() عندما تريد جعل عنوان URL أكثر قابلية للقراءة (لأغراض العرض مثلاً) دون تغيير بنيته. أما في معظم حالات الاستخدام البرمجي، فستحتاج إلى decodeURIComponent() مطبَّقة على المكوّنات الفردية.
التعامل مع عناوين URI المشوّهة
تُطلق كلٌّ من decodeURI() و decodeURIComponent() خطأً من نوع URIError عند مصادفة تسلسلات مُرمَّزة بالنسبة المئوية غير صالحة. ويحدث ذلك مع علامات النسبة المئوية المنفردة، أو التسلسلات غير المكتملة، أو تسلسلات بايتات UTF-8 غير الصالحة. احرص دائماً على تغليف عمليات فك الترميز داخل كتلة try-catch عند التعامل مع عناوين URL المقدّمة من المستخدم أو الخارجية.
// هذه تُطلق URIError: URI malformed
try {
decodeURIComponent('%'); // نسبة مئوية منفردة
} catch (e) {
console.error(e.message); // "URI malformed"
}
try {
decodeURIComponent('%2'); // تسلسل غير مكتمل
} catch (e) {
console.error(e.message); // "URI malformed"
}
// دالة فك ترميز آمنة
function safeDecode(str) {
try {
return decodeURIComponent(str);
} catch (e) {
console.warn('Failed to decode:', str);
return str; // إعادة السلسلة الأصلية عند الفشل
}
}
// إصلاح تسلسلات النسبة المئوية المشوّهة قبل فك الترميز
function fixAndDecode(str) {
// استبدال % المنفردة بـ %25 (علامة نسبة مئوية مُرمَّزة)
const fixed = str.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
return decodeURIComponent(fixed);
}
console.log(fixAndDecode('100% complete'));
// "100% complete"
استخدام واجهة URL API (موصى به)
توفّر واجهتا URL و URLSearchParams الحديثتان طريقة أكثر أماناً وتنظيماً لتحليل عناوين URL وفك ترميزها. فهما تتعاملان مع الترميز وفك الترميز تلقائياً، مما يقلّل من خطر حدوث الأخطاء.
// تحليل عنوان URL والوصول إلى مكوّناته (تُفكّ ترميزها تلقائياً)
const url = new URL('https://example.com/path%20here?q=hello%20world&lang=en');
console.log(url.pathname); // "/path here" (مفكوك الترميز)
console.log(url.search); // "?q=hello%20world&lang=en" (خام)
// URLSearchParams تفكّ ترميز قيم المعاملات تلقائياً
console.log(url.searchParams.get('q')); // "hello world"
console.log(url.searchParams.get('lang')); // "en"
// التكرار عبر جميع المعاملات
for (const [key, value] of url.searchParams) {
console.log(key, '=', value);
}
// q = hello world
// lang = en
// URLSearchParams تتعامل مع + كمسافة (ترميز النماذج)
const formParams = new URLSearchParams('q=hello+world&lang=en');
console.log(formParams.get('q')); // "hello world"
// إنشاء عناوين URL مع ترميز تلقائي
const newUrl = new URL('https://example.com/search');
newUrl.searchParams.set('q', 'C++ & Java');
newUrl.searchParams.set('page', '1');
console.log(newUrl.toString());
// "https://example.com/search?q=C%2B%2B+%26+Java&page=1"
أخطاء فك الترميز الشائعة
الخطأ الأول: فك ترميز عنوان URL كامل باستخدام decodeURIComponent(). قد يؤدي ذلك إلى إفساد بنية العنوان إذا كان يحتوي على محارف محجوزة مُرمَّزة. فقيمة %2F داخل قيمة استعلام ستتحوّل إلى /، مما قد يغيّر معنى العنوان.
الخطأ الثاني: فك الترميز المزدوج. إذا كانت السلسلة قد فُكّ ترميزها مرة واحدة، فإن فك ترميزها مجدداً قد يؤدي إلى نتائج أو أخطاء غير متوقعة. فمثلاً، السلسلة %2520 تُفكّ أولاً إلى %20، ثم إلى مسافة. وإذا كنت تتوقّع مستوى ترميز واحداً فقط، فإن فك الترميز المزدوج يُفسد البيانات.
// مشكلة فك الترميز المزدوج
const encoded = '%2520'; // هذا هو %20 مُرمَّزاً
console.log(decodeURIComponent(encoded)); // "%20" (صحيح - مستوى واحد)
console.log(decodeURIComponent(decodeURIComponent(encoded))); // " " (فُكّ ترميزه مرتين!)
// تحقّق مما إذا كانت السلسلة بحاجة إلى فك ترميز قبل فكّه
function needsDecoding(str) {
return str !== decodeURIComponent(str);
}
الخطأ الثالث: عدم التعامل مع علامة +. إن decodeURIComponent() لا تحوّل + إلى مسافات. فإذا كنت تفكّ ترميز بيانات بصيغة form-urlencoded، فعليك أولاً استبدال + بمسافات، أو استخدام URLSearchParams التي تتعامل مع ذلك تلقائياً.
// decodeURIComponent لا تفكّ ترميز + كمسافة
console.log(decodeURIComponent('hello+world'));
// "hello+world" (وليس "hello world"!)
// الحل: استبدل + قبل فك الترميز
function decodeFormValue(str) {
return decodeURIComponent(str.replace(/\+/g, ' '));
}
console.log(decodeFormValue('hello+world'));
// "hello world"
// أو استخدم URLSearchParams (تتعامل مع + تلقائياً)
const params = new URLSearchParams('q=hello+world');
console.log(params.get('q'));
// "hello world"