URL Decode JavaScript — decodeURIComponent()
Používejte bezplatný URL Decode Online přímo v prohlížeči — bez instalace.
Vyzkoušet URL Decode Online online →Procentem zakódované řetězce se v JavaScriptu vyskytují neustále — vyhledávací dotaz přichází jako q=standing+desk%26price%3A200, OAuth přesměrování jako next=https%3A%2F%2Fdashboard.internal%2F, cesta k souboru jako reports%2F2025%2Fq1.pdf. Dekódování URL v JavaScriptu spočívá v výběru správné ze tří vestavěných funkcí: decodeURIComponent(), decodeURI() a URLSearchParams — a volba mezi nimi je hlavní příčinou tiché korupce dat, kterou jsem viděl v produkčních kódech, zejména okrajový případ + jako mezera a dvojité dekódování. Pro rychlé jednorázové dekódování bez psaní kódu zvládne URL Decoder od ToolDeck to okamžitě v prohlížeči. Tento výukový kurz dekódování URL v JavaScriptu pokrývá všechny tři funkce do hloubky (ES2015+ / Node.js 10+): kdy použít kterou, jak se liší pro mezery a rezervované znaky, dekódování ze souborů a HTTP požadavků, bezpečné zpracování chyb a čtyři chyby způsobující nejvíce záludných produkčních problémů.
- ✓decodeURIComponent() dekóduje všechny procentem zakódované sekvence — je to správná volba pro jednotlivé hodnoty query parametrů a segmenty cesty
- ✓decodeURI() zachovává strukturální znaky URI jako / ? & = # : — používejte ho pouze při dekódování celého URL řetězce, nikdy pro jednotlivé hodnoty
- ✓URLSearchParams.get() vrací již dekódované hodnoty — volání decodeURIComponent() na jeho výstup způsobuje dvojité dekódování
- ✓decodeURIComponent() NEDEKÓDUJE + jako mezeru — pro form-zakódovaná data (application/x-www-form-urlencoded) použijte URLSearchParams nebo nahraďte + před dekódováním
- ✓Vždy obalte decodeURIComponent() blokem try/catch, když vstup pochází z uživatelských dat — holé % nebo neúplná sekvence vyhodí URIError
Co je dekódování URL?
Procentové kódování (formálně definované v RFC 3986) nahrazuje znaky, které jsou v URL nebezpečné nebo strukturálně významné, znakem % následovaným dvěma hexadecimálními číslicemi — hodnotou UTF-8 bajtu daného znaku. Dekódování URL tuto transformaci obrátí: každá %XX sekvence se převede zpět na původní bajt a výsledná bytová sekvence se interpretuje jako UTF-8 text. Mezera dekódovaná z %20, lomítko z %2F, non-ASCII znak ü z %C3%BC (jeho dvou-bajtová UTF-8 reprezentace).
Znaky, které se nikdy nekódují — takzvané nerezervované znaky — jsou písmena A–Z a a–z, číslice 0–9 a - _ . ~. Vše ostatní má buď strukturální roli v URL (jako / pro oddělování segmentů cesty nebo & pro oddělování query parametrů), nebo musí být zakódováno při použití jako datová hodnota. Praktický výsledek: vyhledávací filtr jako status=active&tier=premium zakódovaný jako jediná hodnota query parametru vypadá při příchodu naprosto jinak než originál.
// Procentem zakódováno — jak přijato v HTTP požadavku nebo webhook payloadu "q=price%3A%5B200+TO+800%5D%20AND%20brand%3ANorthwood%20%26%20status%3Ain-stock"
// Po dekódování URL — původní Elasticsearch dotazovací filtr "q=price:[200 TO 800] AND brand:Northwood & status:in-stock"
decodeURIComponent() — Standardní funkce pro dekódování hodnot
decodeURIComponent() je tahounem dekódování URL v JavaScriptu. Dekóduje každou %XX sekvenci ve vstupním řetězci — včetně znaků se strukturálním významem v URL, jako %2F (lomítko), %3F (otazník), %26 (ampersand) a %3D (rovnítko). To z ní dělá správnou volbu pro dekódování jednotlivých hodnot query parametrů a segmentů cesty, ale špatnou volbu pro dekódování celého URL — kde tyto strukturální znaky musí zůstat zakódované. Jde o globální funkci: žádný import není potřeba v žádném JavaScriptovém prostředí.
Minimální funkční příklad
// Dekódování jednotlivých hodnot query parametrů — běžný případ
const city = decodeURIComponent('S%C3%A3o%20Paulo') // 'São Paulo'
const district = decodeURIComponent('It%C3%A1im%20Bibi') // 'Itáim Bibi'
const category = decodeURIComponent('office%20furniture') // 'office furniture'
const filter = decodeURIComponent('price%3A%5B200+TO+800%5D') // 'price:[200+TO+800]'
// Poznámka: + se NEDEKÓDUJE jako mezera — viz sekci + v porovnávací tabulce
console.log(city) // São Paulo
console.log(district) // Itáim Bibi
console.log(category) // office furniture
console.log(filter) // price:[200+TO+800]Dekódování přesměrovávací URL extrahované z query parametru
// Přesměrovávací URL byla procentem zakódována, když byla vložena jako hodnota parametru
// přijímající strana: extrahujte ji, pak ji použijte — s URLSearchParams není potřeba ruční dekódování
const incomingUrl = 'https://auth.company.com/callback' +
'?next=https%3A%2F%2Fdashboard.internal%2Freports%3Fview%3Dweekly%26team%3Dplatform' +
'&session_id=sid_7x9p2k'
const url = new URL(incomingUrl)
const rawNext = url.searchParams.get('next') // Automaticky dekódováno pomocí URLSearchParams
const sessionId = url.searchParams.get('session_id') // 'sid_7x9p2k'
// rawNext je již dekódováno: 'https://dashboard.internal/reports?view=weekly&team=platform'
// NEVOLEJTE decodeURIComponent(rawNext) znovu — bylo by to dvojité dekódování
const nextUrl = new URL(rawNext!)
console.log(nextUrl.hostname) // dashboard.internal
console.log(nextUrl.searchParams.get('view')) // weekly
console.log(nextUrl.searchParams.get('team')) // platformDekódování non-ASCII a Unicode segmentů cesty
// REST API s internacionalizovanými segmenty cesty
// Každý UTF-8 bajt původního znaku byl zakódován samostatně
const encodedSegments = [
'%E6%9D%B1%E4%BA%AC', // 東京 (Tokio) — 3 bajty na znak
'M%C3%BCnchen', // München — ü zakódováno jako 2 bajty
'caf%C3%A9', // café — é jako předskládané NFC
'S%C3%A3o%20Paulo', // São Paulo
]
encodedSegments.forEach(seg => {
console.log(decodeURIComponent(seg))
})
// 東京
// München
// café
// São Paulo
// Extrakce klíče souboru z URL storage API
// Klíč objektu obsahoval /, proto byl zakódován jako %2F uvnitř segmentu cesty
const storageUrl = 'https://storage.api.example.com/v1/objects/reports%2F2025%2Fq1-financials.pdf'
const rawKey = new URL(storageUrl).pathname.replace('/v1/objects/', '')
// .pathname dekóduje kódování na úrovni URL, ale %2F (jako %252F na úrovni URL) zůstane
// Použijte decodeURIComponent pro poslední krok:
const fileKey = decodeURIComponent(rawKey) // 'reports/2025/q1-financials.pdf'
console.log(fileKey)decodeURIComponent() vyhodí URIError, když vstup obsahuje %, které není následováno dvěma platnými hexadecimálními číslicemi — například holé % na konci řetězce nebo sekvence jako %GH. Vždy ji obalte blokem try/catch při dekódování vstupu zadaného uživatelem. Bezpečné vzory dekódování jsou popsány v sekci Ošetření chyb níže.Funkce pro dekódování URL v JavaScriptu — Referenční tabulka znaků
Tři vestavěné funkce pro dekódování se liší v přesně tom, které zakódované sekvence dekódují. Tabulka ukazuje chování pro znaky, na kterých v praxi záleží nejvíce:
| Zakódováno | Znak | decodeURIComponent() | decodeURI() | URLSearchParams |
|---|---|---|---|---|
| %20 | mezera | mezera ✅ | mezera ✅ | mezera ✅ |
| + | plus (form) | + (zachováno) | + (zachováno) | mezera ✅ |
| %2B | + literál | + ✅ | + ✅ | + ✅ |
| %26 | & | & ✅ | & (zach.) ❌ | & ✅ |
| %3D | = | = ✅ | = (zach.) ❌ | = ✅ |
| %3F | ? | ? ✅ | ? (zach.) ❌ | ? ✅ |
| %23 | # | # ✅ | # (zach.) ❌ | # ✅ |
| %2F | / | / ✅ | / (zach.) ❌ | / ✅ |
| %3A | : | : ✅ | : (zach.) ❌ | : ✅ |
| %40 | @ | @ ✅ | @ (zach.) ❌ | @ ✅ |
| %25 | % | % ✅ | % ✅ | % ✅ |
| %C3%BC | ü | ü ✅ | ü ✅ | ü ✅ |
Dva kritické řádky jsou + a strukturální znaky (%26, %3D, %3F). URLSearchParams dekóduje + jako mezeru, protože dodržuje specifikaci application/x-www-form-urlencoded — správné pro HTML formuláře, ale odlišné od chování decodeURIComponent() se stejným znakem. A decodeURI() tiše přeskakuje %26, %3D a %3F — což vypadá správně, dokud hodnota skutečně neobsahuje zakódovaný ampersand nebo rovnítko.
decodeURI() — Dekódování celého URL bez zničení struktury
decodeURI() je protějškem encodeURI(). Dekóduje celý URL řetězec a přitom zachovává znaky se strukturálním významem v URI: ; , / ? : @ & = + $ #. Ty zůstávají v procentem zakódované formě (nebo jako doslovné znaky, pokud se v nezakódované podobě vyskytly ve vstupu). To dělá decodeURI() bezpečným pro sanitizaci celých URL, které mohou obsahovat non-ASCII znaky v cestě nebo názvu hostitele, aniž by omylem zničily strukturu query stringu.
Sanitizace URL s non-ASCII segmenty cesty
// CDN URL s internacionalizovanými segmenty cesty a strukturovaným query stringem // decodeURI() dekóduje non-ASCII cestu, ale zachovává ? & = nedotčené const encodedUrl = 'https://cdn.example.com/assets/%E6%9D%B1%E4%BA%AC%2F2025%2Fq1-report.pdf' + '?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600' const readable = decodeURI(encodedUrl) console.log(readable) // https://cdn.example.com/assets/東京/2025/q1-report.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600 // ↑ Non-ASCII dekódováno; ? & = zachovány — URL zůstává strukturálně platné // decodeURIComponent by URL zničilo — : / ? & = by byly dekódovány najednou const broken = decodeURIComponent(encodedUrl) // Vypadá zde stejně, ale URL jako 'https%3A%2F%2F...' by bylo zničeno
URL před decodeURI(), když potřebujete přistupovat také k jednotlivým komponentám URL. new URL(str) normalizuje vstup, ověřuje jeho strukturu a zpřístupňuje .pathname, .searchParams a .hostname jako již dekódované vlastnosti. Rezervujte decodeURI() pro případy, kdy potřebujete pouze řetězcový výsledek a nemůžete použít konstruktor URL (například ve velmi starých prostředích Node.js 6 bez globální třídy URL).URLSearchParams — Automatické dekódování pro query stringy
URLSearchParams je idiomatický způsob parsování query stringů v moderním JavaScriptu. Každá hodnota vrácená metodami .get(), .getAll() nebo iterací je automaticky dekódována — včetně + jako mezery pro form-zakódovaná data. Je globálně dostupná ve všech moderních prohlížečích a Node.js 10+, nevyžaduje import a ošetřuje okrajové případy, které ruční rozdělování řetězců kazí.
Parsování příchozího query stringu
// Parsování webhook callbacku nebo OAuth přesměrovávacího query stringu
const rawSearch =
'?event_id=evt_9c2f4a1b' +
'&product_name=Standing+Desk+Pro' +
'&filter=price%3A%5B200+TO+800%5D' +
'&tag=ergonomic&tag=adjustable' +
'&redirect=https%3A%2F%2Fdashboard.internal%2Forders%3Fview%3Dpending'
const params = new URLSearchParams(rawSearch)
console.log(params.get('event_id')) // 'evt_9c2f4a1b'
console.log(params.get('product_name')) // 'Standing Desk Pro' ← + dekódováno jako mezera
console.log(params.get('filter')) // 'price:[200+TO+800]' ← %3A a %5B dekódovány
console.log(params.getAll('tag')) // ['ergonomic', 'adjustable']
console.log(params.get('redirect')) // 'https://dashboard.internal/orders?view=pending'
// Iterace přes všechny parametry
for (const [key, value] of params) {
console.log(`${key}: ${value}`)
}
// event_id: evt_9c2f4a1b
// product_name: Standing Desk Pro
// filter: price:[200+TO+800]
// tag: ergonomic
// tag: adjustable
// redirect: https://dashboard.internal/orders?view=pendingParsování query parametrů z aktuálního URL prohlížeče
// Aktuální URL: https://app.example.com/search
// ?q=standing+desk
// &category=office+furniture
// &sort=price_asc
// &page=2
interface SearchFilters {
query: string | null
category: string | null
sort: string
page: number
}
function getSearchFilters(): SearchFilters {
const params = new URLSearchParams(window.location.search)
return {
query: params.get('q'), // 'standing desk'
category: params.get('category'), // 'office furniture'
sort: params.get('sort') ?? 'relevance',
page: Number(params.get('page') ?? '1'),
}
}
const filters = getSearchFilters()
console.log(filters.query) // standing desk (+ automaticky dekódováno)
console.log(filters.category) // office furnitureDekódování URL-zakódovaných dat ze souborů a API odpovědí
Ve skutečných projektech se neustále vyskytují dva scénáře: zpracování souboru na disku obsahujícího procentem zakódovaná data (přístupové logy, exporty dat, soubory zachycených webhooků) a parsování URL příchozího HTTP požadavku v Node.js serveru. Oba se řídí stejným principem — používejte konstruktor URL nebo URLSearchParams namísto ručního rozdělování řetězců — ale detaily se liší.
Čtení a dekódování URL-zakódovaných záznamů ze souboru
import { createReadStream } from 'fs'
import { createInterface } from 'readline'
// Soubor: orders-export.txt — jeden URL-zakódovaný záznam na řádek
// customer_name=Tom%C3%A1%C5%A1+Nov%C3%A1k&order_id=ord_9c2f4a&product=Standing+Desk+Pro&total=149.99%20EUR
// customer_name=Jana+Proch%C3%A1zkov%C3%A1&order_id=ord_7b3a1c&product=Ergonomic+Chair&total=89.00%20EUR
// customer_name=Tom%C3%A1%C5%A1+Nov%C3%A1k&order_id=ord_2e8d5f&product=Monitor+Arm&total=59.00%20EUR
interface Order {
customerName: string | null
orderId: string | null
product: string | null
total: string | null
}
async function parseOrdersFile(filePath: string): Promise<Order[]> {
const fileStream = createReadStream(filePath, { encoding: 'utf-8' })
const rl = createInterface({ input: fileStream, crlfDelay: Infinity })
const orders: Order[] = []
for await (const line of rl) {
if (!line.trim()) continue
// URLSearchParams dekóduje + jako mezeru a %XX sekvence automaticky
const params = new URLSearchParams(line)
orders.push({
customerName: params.get('customer_name'), // 'Tomáš Novák'
orderId: params.get('order_id'), // 'ord_9c2f4a'
product: params.get('product'), // 'Standing Desk Pro'
total: params.get('total'), // '149.99 EUR'
})
}
return orders
}
const orders = await parseOrdersFile('./orders-export.txt')
console.log(orders[0])
// { customerName: 'Tomáš Novák', orderId: 'ord_9c2f4a', product: 'Standing Desk Pro', total: '149.99 EUR' }Parsování Nginx přístupového logu pro dekódování vyhledávacích dotazů
import { createReadStream } from 'fs'
import { createInterface } from 'readline'
// Řádky Nginx přístupového logu vypadají takto:
// 192.168.1.42 - - [11/Mar/2026:10:23:01 +0000] "GET /api/search?q=standing%20desk%26brand%3ANorthwood&sort=price_asc HTTP/1.1" 200 1842
async function extractSearchQueries(logFile: string): Promise<string[]> {
const rl = createInterface({ input: createReadStream(logFile), crlfDelay: Infinity })
const queries: string[] = []
for await (const line of rl) {
// Extrakce cesty požadavku z řádku logu
const match = line.match(/"GET ([^ ]+) HTTP/)
if (!match) continue
try {
const requestUrl = new URL(match[1], 'http://localhost')
const query = requestUrl.searchParams.get('q')
if (query) queries.push(query) // URLSearchParams dekóduje automaticky
} catch {
// Přeskočit poškozené řádky — přístupové logy mohou obsahovat zkrácené záznamy
}
}
return queries
}
const queries = await extractSearchQueries('/var/log/nginx/access.log')
console.log(queries)
// ['standing desk&brand:Northwood', 'ergonomic chair', 'monitor arm 27 inch']Parsování query parametrů v Node.js HTTP serveru
import http from 'http'
// Příchozí URL: /api/products?q=standing+desk&warehouse=eu%2Dwest%2D1&minStock=10&cursor=eyJpZCI6MTIzfQ%3D%3D
const server = http.createServer((req, res) => {
// Sestavení celého URL — druhý argument je základ vyžadovaný konstruktorem URL
const requestUrl = new URL(req.url!, 'http://localhost')
const searchQuery = requestUrl.searchParams.get('q') // 'standing desk'
const warehouseId = requestUrl.searchParams.get('warehouse') // 'eu-west-1'
const minStock = Number(requestUrl.searchParams.get('minStock') ?? '0')
const cursor = requestUrl.searchParams.get('cursor') // 'eyJpZCI6MTIzfQ=='
if (!warehouseId) {
res.writeHead(400, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ error: 'warehouse parameter is required' }))
return
}
const cursorData = cursor ? JSON.parse(Buffer.from(cursor, 'base64').toString()) : null
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ searchQuery, warehouseId, minStock, cursorData }))
})
server.listen(3000)Když potřebujete prozkoumat zakódované URL během vývoje — abyste pochopili, co webhook posílá, ještě než napíšete kód pro parsování — vložte ho přímo do URL Decoder od ToolDeck a okamžitě uvidíte dekódovanou podobu bez spuštění skriptu.
Dekódování URL z příkazové řádky
Pro shell skripty, CI pipeline nebo rychlou jednorázovou kontrolu zakódovaných řetězců funguje několik přístupů bez psaní celého skriptu. Node.js jednořádkové příkazy jsou multiplatformní; na macOS a Linuxu je vždy dostupný také python3.
# ── Jednořádkové příkazy Node.js ─────────────────────────────────────────
# Dekódování jedné procentem zakódované hodnoty
node -e "console.log(decodeURIComponent(process.argv[1]))" "S%C3%A3o%20Paulo%20%26%20Rio"
# São Paulo & Rio
# Parsování query stringu a výpis každého páru klíč=hodnota (dekódovaného)
node -e "
const params = new URLSearchParams(process.argv[1])
for (const [k, v] of params) console.log(`${k} = ${v}`)
" "q=standing+desk&warehouse=eu%2Dwest%2D1&minStock=10"
# q = standing desk
# warehouse = eu-west-1
# minStock = 10
# Dekódování a pěkný výpis URL-zakódovaného JSON těla (běžné při ladění webhooků)
node -e "
const raw = decodeURIComponent(process.argv[1])
console.log(JSON.stringify(JSON.parse(raw), null, 2))
" '%7B%22event%22%3A%22purchase%22%2C%22amount%22%3A149.99%2C%22currency%22%3A%22EUR%22%7D'
# {
# "event": "purchase",
# "amount": 149.99,
# "currency": "EUR"
# }
# ── Jednořádkový příkaz Python (dostupný na většině macOS/Linux systémů) ──
# unquote_plus také dekóduje + jako mezeru — správné pro form-zakódovaná data
python3 -c "
from urllib.parse import unquote_plus
import sys
print(unquote_plus(sys.argv[1]))
" "Standing+Desk+%26+Ergonomic+Chair"
# Standing Desk & Ergonomic Chair
# ── curl — zachycení a dekódování přesměrovávacího URL z hlavičky odpovědi ──────────
curl -sI "https://api.example.com/short/abc123" | grep -i location | node -e "
const line = require('fs').readFileSync('/dev/stdin', 'utf8')
const url = line.replace(/^location:s*/i, '').trim()
console.log(decodeURIComponent(url))
"Tolerantní dekódování s decode-uri-component
Vestavěná funkce decodeURIComponent() vyhodí URIError při jakékoli poškozené sekvenci — včetně koncového % bez dvou následujících hexadecimálních číslic. V produkčním kódu zpracovávajícím URL zadané uživatelem nebo třetí stranou — logy vyhledávacích dotazů, proklikávací URL z e-mailových kampaní, scrapovaná webová data — jsou poškozené sekvence natolik časté, že způsobují pády. Balíček decode-uri-component (~30 mil. týdenních stažení z npm) zpracovává poškozené sekvence tolerantně tím, že vrací původní sekvenci beze změny místo vyhazování výjimky.
npm install decode-uri-component # nebo pnpm add decode-uri-component
import decodeUriComponent from 'decode-uri-component'
// Nativní funkce vyhazuje výjimku při poškozeném vstupu — může zhavarovat obslužnou rutinu požadavku
try {
decodeURIComponent('product%name%') // ❌ URIError: URI malformed
} catch (e) {
console.error('Nativní vyhodilo:', (e as Error).message)
}
// decode-uri-component vrací surovou sekvenci místo vyhazování výjimky
console.log(decodeUriComponent('product%name%'))
// product%name% ← poškozené sekvence ponechány beze změny, žádná výjimka
// Platné sekvence jsou správně dekódovány
console.log(decodeUriComponent('S%C3%A3o%20Paulo%20%26%20Rio'))
// São Paulo & Rio
// Smíšené: platné sekvence dekódovány, neplatné zachovány
console.log(decodeUriComponent('Praha%20Office%20%ZZ%20HQ'))
// Praha Office %ZZ HQ ← %ZZ není platný hex — ponecháno beze změny
// Praktické použití v pipeline zpracování logů
const rawSearchQueries = [
'standing%20desk%20ergonomic',
'price%3A%5B200+TO+800%5D',
'50%25+off', // ← by vyhazovalo s decodeURIComponent (holé %)
'wireless%20keyboard%20%26%20mouse',
]
const decoded = rawSearchQueries.map(q => decodeUriComponent(q))
console.log(decoded)
// ['standing desk ergonomic', 'price:[200+TO+800]', '50%25+off', 'wireless keyboard & mouse']Používejte decodeURIComponent() s blokem try/catch pro aplikační kód, kde chcete okamžitě vědět o příchodu neočekávaného vstupu. Sáhněte po decode-uri-component v datových pipeline, procesorech logů a obslužných rutinách webhooků, kde chcete pokračovat ve zpracování i když některé vstupy obsahují neplatné kódování.
Zpracování poškozených procentem zakódovaných řetězců
Holé %, neúplná sekvence jako %A nebo neplatný pár jako %GH způsobují, že decodeURIComponent() vyhodí URIError: URI malformed. Jakýkoli vstup kontrolovaný uživatelem — vyhledávací dotazy, fragmenty URL, formulářová pole obsahující URL, parametry z odkazů e-mailových kampaní — může tyto sekvence obsahovat. Bezpečná obálka je nezbytná pro jakýkoli externě orientovaný kód.
Bezpečné obálky dekódování pro běžné scénáře
// ── 1. Základní bezpečné dekódování — při chybě vrátí původní řetězec ────────────────
function safeDecode(encoded: string): string {
try {
return decodeURIComponent(encoded)
} catch {
return encoded // vrátit surový vstup pokud dekódování selže — nikdy nespadnout
}
}
// ── 2. Bezpečné dekódování + zpracování + jako mezery (pro form-zakódované hodnoty) ──
function safeFormDecode(formEncoded: string): string {
try {
return decodeURIComponent(formEncoded.replace(/+/g, ' '))
} catch {
return formEncoded.replace(/+/g, ' ') // alespoň nahradit + i když zbytek selže
}
}
// ── 3. Bezpečný úplný parser query stringu → prostý objekt ───────────────────────────
function parseQueryString(queryString: string): Record<string, string> {
const result: Record<string, string> = {}
try {
const params = new URLSearchParams(queryString)
for (const [key, value] of params) {
result[key] = value // URLSearchParams zpracovává veškeré dekódování interně
}
} catch {
// Tiše vrátit prázdný objekt při zcela poškozeném vstupu
}
return result
}
// Použití
console.log(safeDecode('S%C3%A3o%20Paulo')) // São Paulo
console.log(safeDecode('search%20for%2050%25+off')) // search for 50% off (holé %)
// → ve skutečnosti v pořádku; % zde je %25
console.log(safeDecode('malformed%string%')) // malformed%string% (žádná výjimka)
console.log(safeFormDecode('standing+desk+pro')) // standing desk pro
console.log(parseQueryString('q=hello+world&tag=node%20js&page=2'))
// { q: 'hello world', tag: 'node js', page: '2' }Časté chyby
Viděl jsem, jak tyto čtyři vzory způsobují tiché poškození dat nebo neočekávané pády v produkci — obvykle pouze tehdy, když hodnota náhodou obsahuje speciální znak, což znamená, že projdou unit testy a vyjeví se až s reálnými uživatelskými daty.
Chyba 1 — Dvojité dekódování hodnoty URLSearchParams
Problém: URLSearchParams.get() vrací již dekódovaný řetězec. Volání decodeURIComponent() na něj dekóduje hodnotu dvakrát — přeměňuje zbývající % znaky v dekódovaném výstupu na %25, čímž data poškodí. Oprava: Použijte hodnotu z URLSearchParams.get() přímo — žádné další dekódování není potřeba.
// ❌ URLSearchParams již dekódovalo — opětovné dekódování poškodí hodnoty obsahující %
const qs = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rawRate = qs.get('rate') // '50%' ← již dekódováno
const wrongRate = decodeURIComponent(rawRate)
// '50%25' ← % bylo při druhém průchodu dekódováno na %25 — nyní opět špatně// ✅ Použijte URLSearchParams.get() přímo — dekódování je automatické
const qs = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rate = qs.get('rate') // '50%' ← správně, žádný další krok není potřeba
const redirect = qs.get('redirect') // 'https://dashboard.internal/'
const parsed = new URL(redirect!) // Bezpečné ke konstrukci — již dekódováno
console.log(parsed.hostname) // dashboard.internalChyba 2 — Nedekódování + jako mezery pro form-zakódovaná data
Problém: Formulářová odeslání a některé OAuth knihovny kódují mezery jako + (application/x-www-form-urlencoded). Volání decodeURIComponent() na tato data ponechá + jako doslovný znak plus. Oprava: Použijte URLSearchParams pro parsování form-zakódovaných dat, nebo nahraďte + mezerou před voláním decodeURIComponent().
// ❌ decodeURIComponent zachází s + jako doslovným znakem plus, ne mezerou
// OAuth token endpoint posílá: grant_type=authorization_code&code=SplxlOBeZQQYb...
// &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
// Některé starší OAuth implementace také používají + pro mezery v kódech
const formBody = 'customer_name=Tom%C3%A1%C5%A1+Nov%C3%A1k&product=Standing+Desk+Pro&qty=2'
const [, rawVal] = formBody.split('&')[0].split('=')
const name = decodeURIComponent(rawVal)
console.log(name) // 'Tomáš+Novák' ← + nepřevedeno na mezeru// ✅ Použijte URLSearchParams — dodržuje specifikaci application/x-www-form-urlencoded
const formBody = 'customer_name=Tom%C3%A1%C5%A1+Nov%C3%A1k&product=Standing+Desk+Pro&qty=2'
const params = new URLSearchParams(formBody)
console.log(params.get('customer_name')) // 'Tomáš Novák' ← + správně dekódováno jako mezera
console.log(params.get('product')) // 'Standing Desk Pro'Chyba 3 — Použití decodeURI() pro jednotlivé hodnoty query parametrů
Problém: decodeURI() nedekóduje %26 (&), %3D (=) ani %3F (?) — znaky běžně zakódované v hodnotách parametrů. Použití pro dekódování jedné hodnoty ponechá tyto sekvence nedotčené a tiše produkuje nesprávný výstup. Oprava: Používejte decodeURIComponent() pro jednotlivé hodnoty; rezervujte decodeURI() pro celé URL řetězce.
// ❌ decodeURI nedekóduje & a = — hodnota zůstane poškozená const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west' const filter = decodeURI(encodedFilter) console.log(filter) // 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west' ← %3D a %26 nedekódovány
// ✅ decodeURIComponent dekóduje všechny sekvence včetně & a = const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west' const filter = decodeURIComponent(encodedFilter) console.log(filter) // 'status=active&tier=premium®ion=eu-west' ← správně dekódováno
Chyba 4 — Neobalení decodeURIComponent blokem try/catch pro uživatelský vstup
Problém: Holé %bez dvou hexadecimálních číslic — běžné ve vyhledávacích dotazech zadaných uživatelem (“50% sleva”, “100% bavlna”) — způsobí, že decodeURIComponent() vyhodí URIError: URI malformed a způsobí pád obslužné rutiny požadavku, pokud není zachyceno. Oprava: Vždy obalte blokem try/catch, když vstup pochází z uživatelských dat, fragmentů URL nebo externích systémů.
// ❌ Uživatel zadal '100% bavlna' do vyhledávacího pole — holé % způsobí pád serveru
// GET /api/search?q=100%25+bavlna ← Tento konkrétní případ je v pořádku (%25 = %)
// GET /api/search?q=100%+bavlna ← Toto způsobí pád (% není následováno 2 hexadecimálními číslicemi)
app.get('/api/search', (req, res) => {
const query = decodeURIComponent(req.query.q as string)
// ↑ vyhodí URIError: URI malformed pro '100% bavlna' → neošetřená chyba 500
})// ✅ Obalte blokem try/catch — při selhání dekódování použijte surový vstup
app.get('/api/search', (req, res) => {
let query: string
try {
query = decodeURIComponent(req.query.q as string)
} catch {
query = req.query.q as string // použít surovou hodnotu místo pádu
}
// pokračovat bezpečně ve zpracování — query je buď dekódováno nebo surové
})decodeURIComponent vs decodeURI vs URLSearchParams — Rychlé srovnání
| Metoda | Dekóduje %XX | Dekóduje + jako mezeru | Vyhazuje při poškozeném vstupu | Dekóduje & = ? # | Případ použití | Vyžaduje instalaci |
|---|---|---|---|---|---|---|
| decodeURIComponent() | ✅ vše | ❌ ne | ✅ URIError | ✅ ano | Jednotlivé hodnoty a segmenty cesty | Ne |
| decodeURI() | ✅ většina | ❌ ne | ✅ URIError | ❌ ne | Celé URL řetězce | Ne |
| URLSearchParams | ✅ vše | ✅ ano | ❌ tiché | ✅ ano | Parsování query stringu s automatickým dekódováním | Ne |
| URL konstruktor | ✅ vše | ✅ ano | ✅ TypeError | ✅ ano | Úplné parsování a normalizace URL | Ne |
| decode-uri-component | ✅ vše | ❌ ne | ❌ tiché | ✅ ano | Hromadné dekódování s tolerancí poškozeného vstupu | npm install |
| querystring.unescape() | ✅ vše | ❌ ne | ❌ tiché | ✅ ano | Starší Node.js (zastaralé od v16) | Ne (vestavěno) |
V naprosté většině případů se volba redukuje na tři scénáře. Používejte URLSearchParams pro parsování query stringů — automaticky zpracovává dekódování, pravidlo + jako mezery a opakované klíče. Používejte decodeURIComponent() (obalené blokem try/catch) pro jednu hodnotu nebo segment cesty, zejména když očekáváte lomítka zakódovaná jako %2F uvnitř segmentu. Používejte decodeURI() pouze tehdy, když máte celý URL řetězec s non-ASCII znaky v cestě a potřebujete, aby strukturální znaky (/ ? & =) zůstaly zakódované ve výstupu.
Často kladené otázky
Související nástroje
Pro jednoklikové dekódování bez psaní kódu vložte svůj procentem zakódovaný řetězec přímo do URL Decoder od ToolDeck — okamžitě dekóduje v prohlížeči a výsledek je připraven ke zkopírování do kódu nebo terminálu.
Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.
Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.