|
| 1 | ++++ |
| 2 | +title = "مشکلات URL و URLSearchParams" |
| 3 | +author = "حمیدرضا مهدوی پناه" |
| 4 | +tags = ["javascript", "typescript", "node.js"] |
| 5 | +keywords = ["js", "جاوااسکریپت", "URLSearchParams", "URL", "node.js", "url", "typescript"] |
| 6 | +year = "۱۴۰۳" |
| 7 | +month = "۹" |
| 8 | +day = "۲" |
| 9 | +date = "2024-11-22" |
| 10 | +draft = false |
| 11 | ++++ |
| 12 | + |
| 13 | +بررسی تفاوتهای ریزی که حین کار با URLها باعث به وجود آمدن باگهای غیرمنتظره میشود. |
| 14 | + |
| 15 | +<!--more--> |
| 16 | + |
| 17 | +## همه چیز از یک باگ شروع شد |
| 18 | + |
| 19 | +کار با URLها در JavaScript و Node.js باید ساده باشد، اما یک باگ اخیر در پروژه ما، من را به دنیایی از جزئیات پیچیده در [APIهای `URL` و `URLSearchParams`](https://nodejs.org/api/url.html) برد. در این پست، به بررسی این جزئیات و مشکلات احتمالی آنها در کد و نحوه اجتناب از آنها خواهیم پرداخت. |
| 20 | + |
| 21 | +--- |
| 22 | + |
| 23 | +## مشکل: مدیریت URL با Axios |
| 24 | + |
| 25 | +ما این مشکل را هنگام تولید URLها و اضافه کردن امضاهای هش به آنها پیدا کردیم. پارامترهای کوئری به طور یکپارچه percent-encode نمیشدند که منجر به رفتار غیرمنتظره و امضاهای هش اشتباه میشد. |
| 26 | + |
| 27 | +واضح شد که تعامل بین اشیای `URL` و `URLSearchParams` نیاز به دقت بیشتری دارد. |
| 28 | + |
| 29 | +--- |
| 30 | + |
| 31 | +### مشکل شماره 1: تفاوت بین `URL.search` و `()URLSearchParams.toString` |
| 32 | + |
| 33 | +اولین شگفتی تفاوت بین `URL.search` و `()URLSearchParams.toString` بود. |
| 34 | + |
| 35 | +هنگام استفاده از `searchParams.` برای تغییر `URL`، دقت کنید، زیرا طبق [مشخصات WHATWG](https://url.spec.whatwg.org/) ، شیء `URLSearchParams` از قوانین متفاوتی برای تعیین اینکه کدام کاراکترها باید percent-encode شوند استفاده میکند. به عنوان مثال، شیء `URL` کاراکتر تیلد ASCII (`~`) را percent-encode نمیکند، در حالی که `URLSearchParams` همیشه آن را encode میکند. |
| 36 | + |
| 37 | +```typescript |
| 38 | +// Example 1 |
| 39 | +const url = new URL("https://example.com?param=foo bar") |
| 40 | +console.log(url.search) // prints param=foo%20bar |
| 41 | +console.log(url.searchParams.toString()) // prints ?param=foo+bar |
| 42 | + |
| 43 | +// Example 2 |
| 44 | +const myURL = new URL("https://example.org/abc?foo=~bar") |
| 45 | +console.log(myURL.search) // prints ?foo=~bar |
| 46 | +// Modify the URL via searchParams... |
| 47 | +myURL.searchParams.sort() |
| 48 | +console.log(myURL.search) // prints ?foo=%7Ebar |
| 49 | +``` |
| 50 | + |
| 51 | +در پروژه ما، لازم بود به طور صریح `()url.search = url.searchParams.toString` را دوباره اختصاص دهیم تا اطمینان حاصل شود که رشته کوئری به طور یکنواخت encode شده است. |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +### مشکل شماره 2: چالش علامت بعلاوه |
| 56 | + |
| 57 | +یکی دیگر از نکات ظریف این است که چگونه `URLSearchParams` با کاراکترهای `+` برخورد میکند. به طور پیشفرض، `URLSearchParams` کاراکتر `+` را به عنوان فضای خالی تفسیر میکند که ممکن است هنگام encode دادههای باینری یا رشتههای Base64 منجر به خرابی دادهها شود. |
| 58 | + |
| 59 | +```typescript |
| 60 | +const params = new URLSearchParams("bin=E+AXQB+A") |
| 61 | +console.log(params.get("bin")) // "E AXQB A" |
| 62 | +``` |
| 63 | + |
| 64 | +یک راه حل این است که قبل از افزودن مقادیر به `URLSearchParams` از `encodeURIComponent` استفاده کنید: |
| 65 | + |
| 66 | +```typescript |
| 67 | +params.append("bin", encodeURIComponent("E+AXQB+A")) |
| 68 | +``` |
| 69 | + |
| 70 | +جزئیات بیشتر در [مستندات MDN](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams#preserving_plus_signs) موجود است. |
| 71 | + |
| 72 | +--- |
| 73 | + |
| 74 | +### مشکل شماره 3: `URLSearchParams.get` در مقابل `()URLSearchParams.toString` |
| 75 | + |
| 76 | +یکی دیگر از جزئیات ظریف زمانی به وجود میآید که خروجیهای `URLSearchParams.get` و `URLSearchParams.toString` را مقایسه میکنید. به عنوان مثال: |
| 77 | + |
| 78 | +```typescript |
| 79 | +const params = new URLSearchParams("?key=value&key=other") |
| 80 | +console.log(params.get("key")) // "value" (اولین مورد) |
| 81 | +console.log(params.toString()) // "key=value&key=other" (همه موارد سریالایز شده) |
| 82 | +``` |
| 83 | + |
| 84 | +در سناریوهای چند مقداری، `get` فقط اولین مقدار را برمیگرداند، در حالی که `toString` همه را سریالایز میکند. |
| 85 | + |
| 86 | +--- |
| 87 | + |
| 88 | +## راهحل در کد ما |
| 89 | + |
| 90 | +در پروژه ما، مشکل را با اختصاص صریح `search` حل کردیم: |
| 91 | + |
| 92 | +```typescript |
| 93 | +url.search = url.searchParams.toString() |
| 94 | +url.searchParams.set( |
| 95 | + "hash", |
| 96 | + cryptography.createSha256HmacBase64UrlSafe(url.href, SECRET_KEY ?? "") |
| 97 | +) |
| 98 | +``` |
| 99 | + |
| 100 | +این اطمینان حاصل کرد که تمام پارامترهای کوئری قبل از اضافه کردن مقدار `hash` به درستی encode شده بودند. |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +## ماژول `querystring` در Node.js |
| 105 | + |
| 106 | +رابط کاربری WHATWG `URLSearchParams` و [ماژول `querystring`](https://nodejs.org/api/querystring.html) هدف مشابهی دارند، اما هدف ماژول `querystring` عمومیتر است، زیرا امکان سفارشیسازی کاراکترهای جداکننده (`&` و `=`) را فراهم میکند. از سوی دیگر، API `URLSearchParams` به طور خاص برای رشتههای کوئری URL طراحی شده است. |
| 107 | + |
| 108 | +ماژول `querystring` از `URLSearchParams` کارآمدتر است اما یک API استاندارد نیست. از `URLSearchParams` زمانی استفاده کنید که عملکرد بحرانی نیست یا وقتی سازگاری با کد مرورگر مطلوب است. |
| 109 | + |
| 110 | +هنگام استفاده از `URLSearchParams` برخلاف ماژول `querystring`، کلیدهای تکراری به صورت آرایه مجاز نیستند. آرایهها با استفاده از `()array.toString` سریالایز میشوند که به سادگی همه عناصر آرایه را با کاما جدا میکند. |
| 111 | + |
| 112 | +```typescript |
| 113 | +const params = new URLSearchParams({ |
| 114 | + user: "abc", |
| 115 | + query: ["first", "second"], |
| 116 | +}) |
| 117 | +console.log(params.getAll("query")) |
| 118 | +// Prints [ 'first,second' ] |
| 119 | +console.log(params.toString()) |
| 120 | +// Prints 'user=abc&query=first%2Csecond' |
| 121 | +``` |
| 122 | + |
| 123 | +با ماژول `querystring`، رشته کوئری `'foo=bar&abc=xyz&abc=123'` به این صورت پارس میشود: |
| 124 | + |
| 125 | +```typescript |
| 126 | +{ |
| 127 | + "foo": "bar", |
| 128 | + "abc": ["xyz", "123"] |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +## نکات کلیدی |
| 135 | + |
| 136 | +1. هنگام استفاده از `URLSearchParams` به نحوه مدیریت کاراکترهای خاص (مانند `~`) و فضاهای خالی توجه کنید. در صورت نیاز از `encodeURIComponent` استفاده کنید. |
| 137 | + |
| 138 | +2. تفاوت بین `URL.search`، `URLSearchParams.get` و `URLSearchParams.toString` را برای جلوگیری از رفتار غیرمنتظره درک کنید. |
| 139 | + |
| 140 | +3. در Node.js از ماژول `querystring` استفاده کنید اگر میخواهید پارامترهای کوئری تکراری را به عنوان یک آرایه پارس کنید. |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +منبع: |
| 145 | +[Pitfalls of URL and URLSearchParams in JavaScript](https://hamidreza.tech/pitfalls-of-url-and-urlsearchparams-in-nodejs) |
| 146 | +از وبلاگ |
| 147 | +Software Alchemist |
0 commit comments