You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Router Dom v6:如何安全规范地在URL搜索参数中存储对象?

在React Router Dom v6中规范安全地存储对象到URL搜索参数

使用React Router Dom v6时,若需要将复杂对象(如下方示例的filters)存储到URL搜索参数中,直接通过JSON序列化/反序列化的实现方式存在不够严谨、可读性差等问题,以下是几种更规范安全的实现方案:

const filters = {
  name: "user1",
  region: {
    city: "Sydney",
    country: "Australia"
  }
};

方案1:扁平化参数命名(推荐用于需URL可读的场景)

将嵌套对象拆解为扁平化的键值对,比如把region.city作为参数名,直接存储原始值,无需JSON转换。这种方式兼容性好,URL可读性高,避免JSON解析错误。

写入参数示例:

import { useSearchParams, createSearchParams } from "react-router-dom";

const [, setSearchParams] = useSearchParams();

const setFilters = (filters: typeof filters) => {
  const flatParams = {
    name: filters.name,
    "region.city": filters.region.city,
    "region.country": filters.region.country
  };
  setSearchParams(createSearchParams(flatParams), { replace: true });
};

读取参数示例:

const [searchParams] = useSearchParams();

const getFilters = () => {
  return {
    name: searchParams.get("name") || "",
    region: {
      city: searchParams.get("region.city") || "",
      country: searchParams.get("region.country") || ""
    }
  };
};

方案2:封装严谨的JSON序列化工具函数

如果必须存储嵌套对象,可将序列化/反序列化逻辑封装为通用工具函数,补充URL编码、严格类型检查和错误处理,避免原始实现的粗糙问题。

工具函数封装:

// 序列化:将对象转换为可存入搜索参数的键值对
const serializeSearchParams = <T extends Record<string, any>>(obj: T): Record<string, string> => {
  return Object.entries(obj).reduce((acc, [key, value]) => {
    if (value === null || value === undefined) {
      acc[key] = "";
    } else if (typeof value === "object") {
      // 先序列化再URL编码,避免特殊字符破坏参数结构
      acc[key] = encodeURIComponent(JSON.stringify(value));
    } else {
      acc[key] = String(value);
    }
    return acc;
  }, {} as Record<string, string>);
};

// 反序列化:从搜索参数还原为原始对象
const deserializeSearchParams = <T extends Record<string, any>>(params: URLSearchParams): T => {
  const result = {} as T;
  for (const [key, value] of params.entries()) {
    if (!value) {
      result[key as keyof T] = null as unknown as T[keyof T];
      continue;
    }
    try {
      // 先URL解码再解析JSON
      const parsed = JSON.parse(decodeURIComponent(value));
      // 验证解析结果是否为对象/数组,避免误解析普通字符串
      if (typeof parsed === "object" && parsed !== null) {
        result[key as keyof T] = parsed as T[keyof T];
      } else {
        result[key as keyof T] = value as unknown as T[keyof T];
      }
    } catch {
      // 解析失败直接返回原始值
      result[key as keyof T] = value as unknown as T[keyof T];
    }
  }
  return result;
};

使用示例:

import { useSearchParams, createSearchParams } from "react-router-dom";
import { useMemo } from "react";

const [searchParams, setSearchParams] = useSearchParams();

// 读取参数
const allSearchParams = useMemo(() => {
  return deserializeSearchParams<typeof filters>(searchParams);
}, [searchParams]);

// 写入参数
const setFilters = (nextFilter: typeof filters, replaceSearch = true) => {
  const serializedParams = serializeSearchParams(nextFilter);
  setSearchParams(createSearchParams(serializedParams), { replace: replaceSearch });
};

方案3:Base64编码(适用于需隐藏参数结构的场景)

将整个对象序列化为JSON后再进行Base64编码(需使用URL安全的Base64格式),优点是URL更简洁,缺点是参数不可读,且Base64并非加密方式,请勿存储敏感信息。

工具函数示例:

// URL安全的Base64编码
const encodeObjToBase64 = <T>(obj: T): string => {
  return btoa(JSON.stringify(obj))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
};

// URL安全的Base64解码
const decodeBase64ToObj = <T>(str: string): T | null => {
  try {
    const restored = str.replace(/-/g, "+").replace(/_/g, "/");
    const padding = restored.length % 4;
    const padded = padding ? restored + "=".repeat(4 - padding) : restored;
    return JSON.parse(atob(padded)) as T;
  } catch {
    return null;
  }
};

使用示例:

const setFilters = (filters: typeof filters) => {
  setSearchParams({ filters: encodeObjToBase64(filters) }, { replace: true });
};

const getFilters = () => {
  const encoded = searchParams.get("filters");
  return encoded ? decodeBase64ToObj<typeof filters>(encoded) : null;
};

内容的提问来源于stack exchange,提问作者Crocsx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 07:45:35