کار با URL در جاوااسکریپت

آدرس‌های URL بخش مهمی از هر اپلیکیشن وب هستند. اگر اپلیکیشن ما درخواست‌هایی به یک API ارسال می‌کند، ایجاد آدرس‌های صحیح برای این درخواست‌ها اهمیت زیادی دارد. رابط کاربری API URL در جاوااسکریپت، که در تمامی مرورگرهای مدرن پشتیبانی می‌شود، راهی برای تجزیه و دستکاری آدرس‌های URL فراهم می‌کند و دسترسی آسان به بخش‌های مختلف یک آدرس URL را ممکن می‌سازد.

آشنایی با بخش‌های مختلف یک URL

آدرس URL زیر را در نظر می‌گیریم:

https://example.com/api/search?query=foo&sort=asc#results

این آدرس URL شامل بخش‌های زیر است:

  • پروتکل: https
  • هاست: example.com
  • مسیر: /api/search
  • query string: ?query=foo&sort=asc
  • هش: #results

با استفاده از جاوااسکریپت مدرن، می‌توانیم آدرس‌های URL را تجزیه کرده و این بخش‌های مختلف را به راحتی استخراج کنیم.

تجزیه URL‌ها

در مرورگرهای قدیمی، قبل از اینکه API URL معرفی شود، یکی از روش‌های معمول برای تجزیه آدرس‌های URL استفاده از المنت <a> بود. این المنت قابلیت‌هایی برای تجزیه ابتدایی URL ارائه می‌داد. به عنوان مثال، می‌توانستیم query string را از یک URL استخراج کنیم:

function getQueryString(url) {
  const link = document.createElement('a');
  link.href = url;
  return url.search;
}

این روش، با وجود سادگی، محدودیت‌هایی داشت:

  1. نیازمند یک محیط DOM بود، به این معنا که در محیط‌هایی مثل Node.js کار نمی‌کرد.
  2. هیچ مکانیزم مدیریت خطایی نداشت؛ اگر یک URL نامعتبر به ویژگی href داده می‌شد، خطایی رخ نمی‌داد.

روش دیگر، استفاده از Regular Expressionها برای تجزیه بخش‌های مختلف URL بود، اما این کار بسیار پیچیده و مستعد خطا بود.

استفاده از API URL برای تجزیه URL‌ها بسیار ساده است. تنها کافی است آدرس URL مورد نظر را به URL constructor ارسال کنیم. اگر رشته URL معتبر باشد، یک آبجکت URL به ما return می‌شود که شامل ویژگی‌هایی برای دسترسی به بخش‌های مختلف URL است.

const url = new URL('https://example.com/api/search?query=foobar');
console.log(url.host); // example.com
console.log(url.pathname); // /api/search
console.log(url.search); // ?query=foobar

تجزیه Query String

می‌توانیم query string یک URL را به دو روش استخراج کنیم:

  1. ویژگی search: این ویژگی، یک رشته است که شامل کل query string می‌شود (همراه با کاراکتر ?).
  2. ویژگی searchParams: این ویژگی، یک آبجکت از نوع URLSearchParams است.

اگر بخواهیم مقدار یک پارامتر خاص در query string را بدانیم، می‌توانیم از متد get استفاده کنیم تا مقدار آن پارامتر را با نامش دریافت نماییم.

const url = new URL('https://example.com/api/search?query=foobar&maxResults=10');
console.log(url.searchParams.get('query'); // foobar
console.log(url.searchParams.get('maxResults'); // 10

اگر چندین پارامتر با نام یکسان وجود داشته باشد، می‌توانیم از متد getAll استفاده کنیم تا آرایه‌ای شامل تمامی مقادیر آن نام را دریافت نماییم:

const url = new URL('https://example.com/api/search?tag=tag1&tag=tag2&tag=tag3');
console.log(url.searchParams.getAll('tag')); // ['tag1', 'tag2', 'tag3']

ساخت Query String

ساختن query string به صورت دستی می‌تواند مشکل باشد، مخصوصاً اگر پارامترها شامل کاراکترهای خاصی باشند که نیاز به کدگذاری دارند. به‌عنوان مثال، اگر یک پارامتر نیاز داشته باشد که شامل کاراکتر & باشد، باید آن را به عنوان %۲۶ کدگذاری کنیم. برای مدیریت این موارد، باید از تابع encodeURIComponent استفاده نماییم:

let queryString = 'foo=bar';
queryString += '&baz=qux';
queryString += '&tag=' + encodeURIComponent('one&two');
console.log(queryString); // foo=bar&baz=qux&tag=one%26two

اما می‌توانیم با استفاده از آبجکت URLSearchParams به صورت ایمن‌تر query string را بسازیم:

const params = new URLSearchParams();
params.append('foo', 'bar');
params.append('baz', 'qux');
params.append('tag', 'one&two');
console.log(params.toString()); // foo=bar&baz=qux&tag=one%26two

مزایای استفاده از  URLSearchParams

استفاده از URLSearchParams مزایایی دارد که عبارتند از:

  • نیازی نیست نگران کاراکتر & برای جداسازی پارامترها باشیم.
  • نیازی به کدگذاری مقادیر پارامترها به صورت دستی نیست.
  • نیازی به استفاده از الحاق رشته (String Concatenation) نداریم.

پیمایش پارامترهای جستجو

بدون استفاده از آبجکت URLSearchParams، پیمایش پارامترهای یک query string کمی پیچیده می‌شود. در این حالت، باید چندین بار رشته را تقسیم کنیم؛ یک بار برای جدا کردن پارامترها به عنوان جفت‌های key/value و بار دیگر برای تفکیک key از value در هر پارامتر:

function listQueryParams(queryString) {
  queryString.split('&').forEach(param => {
    const [key, value] = param.split('=');
    console.log(`${key}: ${value}`);
  });
}

اگر پارامترها شامل کاراکترهای کدگذاری شده باشند، باید آن‌ها را نیز decode کنیم:

function listQueryParams(queryString) {
  queryString.split('&').forEach(param => {
    const [key, value] = param.split('=');
    console.log(`${key}: ${decodeURIComponent(value)}`);
  });
}

اما با استفاده از متد entries آبجکت URLSearchParams می‌توانیم به سادگی روی جفت‌های key/value پیمایش کنیم:

function listQueryParams(queryString) {
  const params = new URLSearchParams(queryString);
  params.entries().forEach(([key, value]) => console.log(`${key}: ${value}`));
}

ساخت یک URL کامل

برای ساخت یک URL کامل با یک آدرس پایه و چندین پارامتر جستجو، می‌توانیم از ترکیب URL و URLSearchParams استفاده کنیم. مثال زیر یک روش کامل را نشان می‌دهد:

const url = new URL('https://example.com/api/search');
url.searchParams.append('query', 'test');
url.searchParams.append('tag', 'tag1');
url.searchParams.append('tag', 'tag2');

// https://example.com/api/search?query=test&tag=tag1&tag=tag2
console.log(url.toString());

بررسی معتبر بودن URLها

ممکن است بخواهیم از یک regular expression برای اعتبارسنجی یک URL استفاده کنیم، اما نوشتن یک regular expression که بتواند همه URLهای معتبر را به درستی شناسایی کند، کار بسیار دشواری است.

به جای آن، می‌توانیم از API URL استفاده کنیم. اگر یک URL نامعتبر به URL Constructor داده شود، خطا تولید می‌کند. این ویژگی برای بررسی معتبر بودن URLها مفید است:

function isValidURL(url) {
  try {
    new URL(url);
    return true;
  } catch (error) {
    return false;
  }
}

مرورگرهای جدیدتر این کار را ساده‌تر کرده‌اند. متد استاتیک URL.canParse اضافه شده است که با یک خط کد، اعتبارسنجی مشابهی را انجام می‌دهد. درست مانند تابع isValidURL که در بالا دیدیم، این متد در جاوااسکریپت یک رشته URL دریافت می‌کند و بسته به معتبر بودن آن، مقدار true یا false برمی‌گرداند.

ساخت URLهای نسبی

API URL یک مکانیزم قدرتمند برای تبدیل URLهای نسبی به کامل ارائه می‌دهد. به‌طور معمول، اگر URLای که به URL constructor داده می‌شود، کامل و معتبر نباشد، خطا ایجاد می‌کند. اما اگر یک آرگومان دوم به آن بدهیم، این آرگومان به عنوان یک URL پایه عمل می‌کند تا URL نسبی را بر اساس آن بسازد.

در این روش، یعنی روش دو آرگومانی، آرگومان اول نیازی به معتبر یا کامل بودن ندارد، اما آرگومان دوم باید یک URL معتبر و کامل باشد. به عنوان مثال:

new URL('/about', 'https://example.com').href;

URL constructor، آدرس پایه https://example.com را می‌گیرد و مسیر نسبی /about را به آن اضافه می‌کند. در نتیجه، خروجی https://example.com/about خواهد بود.

یک مثال دیگر:

new URL('profile', 'https://example.com/users').href;

ممکن است انتظار داشته باشیم که نتیجه https://example.com/users/profile باشد، اما در واقع خروجی https://example.com/profile خواهد بود. این رفتار دقیقاً مانند یک لینک نسبی عمل می‌کند؛ یعنی بخش parent مسیر را که ریشه example.com است، در نظر می‌گیرد و سپس profile را به آن اضافه می‌کند.

یک مثال دیگر از استفاده URL نسبی را بررسی می‌کنیم. می‌توانیم از .. استفاده کنیم تا یک سطح به عقب در ساختار مسیر برویم:

new URL('../profile', 'https://example.com/users/123').href;

در این مثال URL در نهایت به https://example.com/profile تبدیل می‌شود. باید این موضوع را به خاطر داشته باشیم که آدرس‌های URL نسبی از بخش parent مسیر شروع می‌شوند. در اینجا، .. باعث می‌شود که یک سطح بالاتر در مسیر حرکت کنیم.

اگر هنگام استفاده از URL constructor یک آدرس نسبی را وارد کنیم اما یک آدرس نامعتبر یا ناقص به عنوان URL پایه مشخص نماییم، با خطا مواجه می‌شویم. همچنین، اگر بدون مشخص کردن یک URL پایه کامل از یک آدرس نسبی استفاده کنیم، باز هم خطا دریافت خواهیم کرد.

new URL('../profile', '/about'); // error!
new URL('../profile'); // error

کار با آبجکت window.location

احتمالا با آبجکت window.location در جاوااسکریپت آشنا هستیم، که نماینده URL صفحه فعلی می‌باشد. این آبجکت دارای ویژگی‌هایی مانند href و pathname است، بنابراین ممکن است فکر کنیم که یک آبجکت URL است. اما در واقع این یک آبجکت Location است که برخی ویژگی‌های مشترک با URL دارد، اما برخی ویژگی‌ها مانند searchParams را ندارد.

با اینکه این آبجکت یک URL کامل نیست، اما می‌توانیم از آن برای ساخت آبجکت‌های جدید URL استفاده کنیم. به عنوان مثال، می‌توانیم window.location را به URL constructor بدهیم تا یک URL کامل با ویژگی‌هایی مثل searchParams بر اساس URL فعلی بسازیم، یا حتی از آن به عنوان URL پایه برای ساخت URLهای نسبی استفاده کنیم:

new URL('/profile', window.location).href;

مطابقت الگوها در URL با استفاده از URLPattern

استفاده از URL باعث می‌شود تا بتوانیم به سادگی path را از یک URL استخراج کنیم. به عنوان مثال، در URL https://example.com/api/users/123/profile مقدار pathname برابر با /api/users/123/profile است. اما اگر بخواهیم فقط ID کاربر (۱۲۳) را از این URL استخراج کنیم، چه کاری باید انجام بدهیم؟

همان‌طور که قبلاً به آن اشاره کردیم، ایجاد regular expressionها برای اعتبارسنجی و استخراج بخش‌های خاص از یک URL می‌تواند دشوار باشد.

API جدیدی به نام URLPattern در جاوااسکریپت وجود دارد که می‌تواند برای تطبیق و استخراج بخش‌هایی از URL، بر اساس الگوهای مشخص مورد استفاده قرار بگیرد. این قابلیت به ویژه در برنامه‌هایی مثل routing سمت کلاینت در اپلیکیشن‌های تک‌صفحه‌ای (SPA) بسیار مفید است.

با استفاده از URL مربوط به پروفایل کاربر به عنوان مثال، می‌خواهیم یک URLPattern ایجاد کنیم تا ID کاربر را استخراج نماییم. می‌توانیم از کاراکتر : در ابتدای یک placeholder نام‌گذاری شده استفاده کنیم، که بعداً برای تطبیق آن بخش از URL به کار می‌رود:

const pattern = new URLPattern('https://example.com/api/users/:userId/profile');
const matcher = pattern.exec('https://example.com/api/users/123/profile');
console.log(matcher.pathname.groups.userId); // 123

عملکرد exec در URLPattern

هنگامی که متد exec را روی یک URLPattern فراخوانی می‌کنیم، به یک URL معتبر نیاز دارد. این متد یک آبجکت matcher برمی‌گرداند که شامل ویژگی‌هایی برای هر بخش از URL (مانند protocol، host، pathname و غیره) است.

هر یک از این ویژگی‌ها دارای یک ویژگی groups هستند که نام placeholderها (مانند :userId) را به مقادیر آن‌ها در URL نگاشت می‌کند.

اگر فقط بخواهیم یک بخش خاص از URL، مانند نام مسیر را تطبیق دهیم، می‌توانیم از کاراکترهای wildcard (مانند *) در الگوی URL استفاده کنیم. همچنین، به جای استفاده از یک رشته URL، می‌توانیم یک آبجکت حاوی بخش‌هایی از URL که می‌خواهیم تطبیق دهیم، ارسال نماییم:

new URLPattern('https://*/api/users/:userId/profile');
new URLPattern({ pathname: '/api/users/:userId/profile' });

API URLPattern هنوز در تمام مرورگرها قابل استفاده نیست. در زمان نوشتن این مطلب، مرورگرهای Firefox و Safari از آن پشتیبانی نمی‌کنند. برای مشاهده آخرین وضعیت پشتیبانی مرورگرها می‌توانیم به وب‌سایت CanIUse.com مراجعه کنیم.

جمع‌بندی

API URL یک ابزار چندمنظوره برای ساخت، اعتبارسنجی و مدیریت URLها در جاوااسکریپت است. استفاده از این API نسبت به روش‌های دستی یا regular expressionها ایمن‌تر و کم‌خطاتر است. همچنین با استفاده از آبجکت URLSearchParams می‌توانیم بدون نگرانی درباره اتصال رشته‌ها یا کدگذاری کاراکترهای خاص، به راحتی یک query string بسازیم.

دیدگاه‌ها:

افزودن دیدگاه جدید