Benutzerdefinierte Serialisierung von Suchparametern

Standardmäßig analysiert und serialisiert TanStack Router Ihre URL-Suchparameter automatisch mit JSON.stringify und JSON.parse. Dieser Prozess beinhaltet das Escaping und Unescaping des Suchstrings, was eine gängige Praxis für URL-Suchparameter ist, zusätzlich zur Serialisierung und Deserialisierung des Suchobjekts.

Wenn Sie beispielsweise die Standardkonfiguration verwenden und das folgende Suchobjekt haben

tsx
const search = {
  page: 1,
  sort: 'asc',
  filters: { author: 'tanner', min_words: 800 },
}
const search = {
  page: 1,
  sort: 'asc',
  filters: { author: 'tanner', min_words: 800 },
}

würde es wie folgt serialisiert und escaped in den folgenden Suchstring umgewandelt werden

txt
?page=1&sort=asc&filters=%7B%22author%22%3A%22tanner%22%2C%22min_words%22%3A800%7D
?page=1&sort=asc&filters=%7B%22author%22%3A%22tanner%22%2C%22min_words%22%3A800%7D

Wir können das Standardverhalten mit dem folgenden Code implementieren

tsx
import {
  createRouter,
  parseSearchWith,
  stringifySearchWith,
} from '@tanstack/solid-router'

const router = createRouter({
  // ...
  parseSearch: parseSearchWith(JSON.parse),
  stringifySearch: stringifySearchWith(JSON.stringify),
})
import {
  createRouter,
  parseSearchWith,
  stringifySearchWith,
} from '@tanstack/solid-router'

const router = createRouter({
  // ...
  parseSearch: parseSearchWith(JSON.parse),
  stringifySearch: stringifySearchWith(JSON.stringify),
})

Dieses Standardverhalten ist jedoch möglicherweise nicht für alle Anwendungsfälle geeignet. Sie möchten zum Beispiel möglicherweise ein anderes Serialisierungsformat verwenden, wie z. B. Base64-Kodierung, oder Sie möchten eine speziell entwickelte Serialisierungs-/Deserialisierungsbibliothek verwenden, wie z. B. query-string, JSURL2 oder Zipson.

Dies kann erreicht werden, indem Sie Ihre eigenen Serialisierungs- und Deserialisierungsfunktionen für die Optionen parseSearch und stringifySearch in der Router-Konfiguration bereitstellen. Dabei können Sie die integrierten Hilfsfunktionen von TanStack Router, parseSearchWith und stringifySearchWith, verwenden, um den Prozess zu vereinfachen.

Tipp

Ein wichtiger Aspekt der Serialisierung und Deserialisierung ist, dass Sie nach der Deserialisierung dasselbe Objekt zurückerhalten können. Dies ist wichtig, da Sie bei fehlerhafter Serialisierung und Deserialisierung möglicherweise Informationen verlieren. Wenn Sie beispielsweise eine Bibliothek verwenden, die keine verschachtelten Objekte unterstützt, können Sie das verschachtelte Objekt beim Deserialisieren des Suchstrings verlieren.

Diagram showing idempotent nature of URL search param serialization and deserialization

Hier sind einige Beispiele, wie Sie die Serialisierung von Suchparametern in TanStack Router anpassen können

Verwendung von Base64

Es ist üblich, Ihre Suchparameter mit Base64 zu kodieren, um eine maximale Kompatibilität zwischen Browsern und URL-Unfurllern usw. zu erreichen. Dies kann mit dem folgenden Code erfolgen

tsx
import {
  Router,
  parseSearchWith,
  stringifySearchWith,
} from '@tanstack/solid-router'

const router = createRouter({
  parseSearch: parseSearchWith((value) => JSON.parse(decodeFromBinary(value))),
  stringifySearch: stringifySearchWith((value) =>
    encodeToBinary(JSON.stringify(value)),
  ),
})

function decodeFromBinary(str: string): string {
  return decodeURIComponent(
    Array.prototype.map
      .call(atob(str), function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
      })
      .join(''),
  )
}

function encodeToBinary(str: string): string {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
      return String.fromCharCode(parseInt(p1, 16))
    }),
  )
}
import {
  Router,
  parseSearchWith,
  stringifySearchWith,
} from '@tanstack/solid-router'

const router = createRouter({
  parseSearch: parseSearchWith((value) => JSON.parse(decodeFromBinary(value))),
  stringifySearch: stringifySearchWith((value) =>
    encodeToBinary(JSON.stringify(value)),
  ),
})

function decodeFromBinary(str: string): string {
  return decodeURIComponent(
    Array.prototype.map
      .call(atob(str), function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
      })
      .join(''),
  )
}

function encodeToBinary(str: string): string {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
      return String.fromCharCode(parseInt(p1, 16))
    }),
  )
}

⚠️ Warum verwendet dieser Ausschnitt nicht atob/btoa?

Wenn wir das vorherige Objekt mit dieser Konfiguration in einen Suchstring umwandeln würden, würde es wie folgt aussehen

txt
?page=1&sort=asc&filters=eyJhdXRob3IiOiJ0YW5uZXIiLCJtaW5fd29yZHMiOjgwMH0%3D
?page=1&sort=asc&filters=eyJhdXRob3IiOiJ0YW5uZXIiLCJtaW5fd29yZHMiOjgwMH0%3D

Warnung

Wenn Sie Benutzereingaben in Base64 serialisieren, riskieren Sie eine Kollision mit der URL-Deserialisierung. Dies kann zu unerwartetem Verhalten führen, z. B. dass die URL nicht korrekt analysiert wird oder als anderer Wert interpretiert wird. Um dies zu vermeiden, sollten Sie die Suchparameter mit einer sicheren Binärkodierungs-/Dekodierungsmethode (siehe unten) kodieren.

Verwendung der query-string-Bibliothek

Die query-string-Bibliothek ist beliebt dafür, Abfragestrings zuverlässig analysieren und als Zeichenkette darstellen zu können. Sie können sie verwenden, um das Serialisierungsformat Ihrer Suchparameter anzupassen. Dies kann mit dem folgenden Code erfolgen

tsx
import { createRouter } from '@tanstack/solid-router'
import qs from 'query-string'

const router = createRouter({
  // ...
  stringifySearch: stringifySearchWith((value) =>
    qs.stringify(value, {
      // ...options
    }),
  ),
  parseSearch: parseSearchWith((value) =>
    qs.parse(value, {
      // ...options
    }),
  ),
})
import { createRouter } from '@tanstack/solid-router'
import qs from 'query-string'

const router = createRouter({
  // ...
  stringifySearch: stringifySearchWith((value) =>
    qs.stringify(value, {
      // ...options
    }),
  ),
  parseSearch: parseSearchWith((value) =>
    qs.parse(value, {
      // ...options
    }),
  ),
})

Wenn wir das vorherige Objekt mit dieser Konfiguration in einen Suchstring umwandeln würden, würde es wie folgt aussehen

txt
?page=1&sort=asc&filters=author%3Dtanner%26min_words%3D800
?page=1&sort=asc&filters=author%3Dtanner%26min_words%3D800

Verwendung der JSURL2-Bibliothek

JSURL2 ist eine nicht standardmäßige Bibliothek, die URLs komprimieren kann, während die Lesbarkeit erhalten bleibt. Dies kann mit dem folgenden Code erfolgen

tsx
import {
  Router,
  parseSearchWith,
  stringifySearchWith,
} from '@tanstack/solid-router'
import { parse, stringify } from 'jsurl2'

const router = createRouter({
  // ...
  parseSearch: parseSearchWith(parse),
  stringifySearch: stringifySearchWith(stringify),
})
import {
  Router,
  parseSearchWith,
  stringifySearchWith,
} from '@tanstack/solid-router'
import { parse, stringify } from 'jsurl2'

const router = createRouter({
  // ...
  parseSearch: parseSearchWith(parse),
  stringifySearch: stringifySearchWith(stringify),
})

Wenn wir das vorherige Objekt mit dieser Konfiguration in einen Suchstring umwandeln würden, würde es wie folgt aussehen

txt
?page=1&sort=asc&filters=(author~tanner~min*_words~800)~
?page=1&sort=asc&filters=(author~tanner~min*_words~800)~

Verwendung der Zipson-Bibliothek

Zipson ist eine sehr benutzerfreundliche und performante JSON-Komprimierungsbibliothek (sowohl in Bezug auf die Laufzeit- als auch auf die Komprimierungsleistung). Um Ihre Suchparameter damit zu komprimieren (was auch das Escaping/Unescaping und die Base64-Kodierung/Dekodierung erfordert), können Sie den folgenden Code verwenden

tsx
import {
  Router,
  parseSearchWith,
  stringifySearchWith,
} from '@tanstack/solid-router'
import { stringify, parse } from 'zipson'

const router = createRouter({
  parseSearch: parseSearchWith((value) => parse(decodeFromBinary(value))),
  stringifySearch: stringifySearchWith((value) =>
    encodeToBinary(stringify(value)),
  ),
})

function decodeFromBinary(str: string): string {
  return decodeURIComponent(
    Array.prototype.map
      .call(atob(str), function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
      })
      .join(''),
  )
}

function encodeToBinary(str: string): string {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
      return String.fromCharCode(parseInt(p1, 16))
    }),
  )
}
import {
  Router,
  parseSearchWith,
  stringifySearchWith,
} from '@tanstack/solid-router'
import { stringify, parse } from 'zipson'

const router = createRouter({
  parseSearch: parseSearchWith((value) => parse(decodeFromBinary(value))),
  stringifySearch: stringifySearchWith((value) =>
    encodeToBinary(stringify(value)),
  ),
})

function decodeFromBinary(str: string): string {
  return decodeURIComponent(
    Array.prototype.map
      .call(atob(str), function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
      })
      .join(''),
  )
}

function encodeToBinary(str: string): string {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
      return String.fromCharCode(parseInt(p1, 16))
    }),
  )
}

⚠️ Warum verwendet dieser Ausschnitt nicht atob/btoa?

Wenn wir das vorherige Objekt mit dieser Konfiguration in einen Suchstring umwandeln würden, würde es wie folgt aussehen

txt
?page=1&sort=asc&filters=JTdCJUMyJUE4YXV0aG9yJUMyJUE4JUMyJUE4dGFubmVyJUMyJUE4JUMyJUE4bWluX3dvcmRzJUMyJUE4JUMyJUEyQ3UlN0Q%3D
?page=1&sort=asc&filters=JTdCJUMyJUE4YXV0aG9yJUMyJUE4JUMyJUE4dGFubmVyJUMyJUE4JUMyJUE4bWluX3dvcmRzJUMyJUE4JUMyJUEyQ3UlN0Q%3D

Sichere Binärkodierung/-dekodierung

Im Browser funktionieren die Funktionen atob und btoa nicht garantiert ordnungsgemäß mit Nicht-UTF8-Zeichen. Wir empfehlen stattdessen die Verwendung dieser Kodierungs-/Dekodierungs-Utilities

Zum Kodieren von einer Zeichenkette in eine Binärzeichenkette

typescript
export function encodeToBinary(str: string): string {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
      return String.fromCharCode(parseInt(p1, 16))
    }),
  )
}
export function encodeToBinary(str: string): string {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
      return String.fromCharCode(parseInt(p1, 16))
    }),
  )
}

Zum Dekodieren von einer Binärzeichenkette in eine Zeichenkette

typescript
export function decodeFromBinary(str: string): string {
  return decodeURIComponent(
    Array.prototype.map
      .call(atob(str), function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
      })
      .join(''),
  )
}
export function decodeFromBinary(str: string): string {
  return decodeURIComponent(
    Array.prototype.map
      .call(atob(str), function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
      })
      .join(''),
  )
}
Unsere Partner
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.