---
id: router.validated-search
kind: recipe
product: router
framework: react
locale: zh-CN
revision: 2
sourceCheckedOn: "2026-08-03"
versionRange: "^1"
verifiedAgainst: "@tanstack/react-router@1.170.18"
contentModel: 2
packages:
  - "@tanstack/react-router"
tasks:
  - "validate-search-params"
  - "pagination"
  - "url-state"
sourceRefs:
  - "https://tanstack.com/router/latest/docs/guide/search-params"
---

# 验证分页与筛选参数

> 将不可信的 URL 搜索参数转换为稳定、有类型的路由输入。

## 执行契约

- **适用场景:** 筛选、排序、分页或可分享视图需要由 URL 持有。
- **不要使用:** 不要存放秘密、临时输入或无法安全序列化的大对象。
- **前置检查:** 定义允许的键、运行时验证规则、默认值以及规范化后的 URL 形态。
- **验证结果:** 测试有效、缺失和非法值；刷新、后退与分享链接都必须恢复相同视图。
- **失败模式:** 若一次更新清空其他键，检查 search 更新函数是否基于 previous 合并，而不是创建不完整对象。
- **安全边界:** URL 输入不可信；验证通过仍不代表可以直接拼接 SQL、文件路径或外部请求。

## 契约

validateSearch 接收解析后但不可信的值。返回值将成为 loader、Route.useSearch() 和子路由看到的搜索类型。

## 实现

为非法页码提供稳定默认值，并将 status 限制到明确联合。

File: `src/routes/issues/index.tsx`

```tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/issues/')({
  validateSearch: (search: Record<string, unknown>) => ({
    page: Math.max(1, Number(search.page) || 1),
    status: search.status === 'closed' ? 'closed' as const : 'open' as const,
  }),
  component: IssuesPage,
})

function IssuesPage() {
  const { page, status } = Route.useSearch()
  return <p>{status} issues · page {page}</p>
}
```

## 常见失败

不要在每个组件里重复解析搜索值。不要假设 Number() 一定返回有效数字。如果项目已使用 Zod 或 Valibot，优先复用现有 schema。

## Official sources

- https://tanstack.com/router/latest/docs/guide/search-params
