# 3.10 路由树-参数路径之校验

本节课工程结构如下:

```
(base) yanglei@yuanhong 15-validateParamRoute % tree ./
./
├── context.go
├── handleFunc.go
├── httpServer.go
├── httpServer_test.go
├── node.go
├── router.go
├── router_test.go
└── serverInterface.go

0 directories, 8 files
```

## PART1. 参数路由与通配符路由共存问题

### 1.1 修改逻辑

上节课中说过,我们的设计中是不准备支持同样的参数路径和通配符匹配一起注册的(即`/user/*`和`/user/:id`).

所以从逻辑上来讲,只需要实现:**注册参数路由时检测通配符路由是否存在;注册通配符路由时检测参数路由是否存在.若对方存在,则不允许注册**即可.

`node.go`:

```go
// childOrCreate 本方法用于在节点上获取给定的子节点,如果给定的子节点不存在则创建
func (n *node) childOrCreate(segment string) *node {
	// 如果路径为参数 则查找当前节点的参数子节点 或创建一个当前节点的参数子节点 并返回
	if strings.HasPrefix(segment, ":") {
		// 若当前节点存在通配符子节点 则不允许注册参数子节点
		if n.wildcardChild != nil {
			panic("web: 非法路由,已有通配符路由.不允许同时注册通配符路由和参数路由")
		}

		if n.paramChild == nil {
			n.paramChild = &node{
				path: segment,
			}
		}
		return n.paramChild
	}

	// 若路径为通配符 则查找当前节点的通配符子节点 或创建一个当前节点的通配符子节点 并返回
	if segment == "*" {
		// 若当前节点存在参数子节点 则不允许注册通配符子节点
		if n.paramChild != nil {
			panic("web: 非法路由,已有参数路由.不允许同时注册通配符路由和参数路由")
		}

		if n.wildcardChild == nil {
			n.wildcardChild = &node{
				path: segment,
			}
		}
		return n.wildcardChild
	}

	// 如果当前节点的子节点映射为空 则创建一个子节点映射
	if n.children == nil {
		n.children = map[string]*node{}
	}

	res, ok := n.children[segment]
	// 如果没有找到子节点,则创建一个子节点;否则返回找到的子节点
	if !ok {
		res = &node{
			path: segment,
		}
		n.children[segment] = res
	}
	return res
}
```

### 1.2 测试

`router_test.go`:

```go
// TestRouter_findRoute_param_and_wildcard_coexist 测试针对注册参数路由时,已有通配符路由的情况
func TestRouter_findRoute_param_and_wildcard_coexist(t *testing.T) {
	// step1. 注册有冲突的路由
	r := newRouter()
	mockHandleFunc := func(ctx *Context) {}
	r.addRoute(http.MethodGet, "/order/detail/*", mockHandleFunc)

	// step2. 断言非法用例
	assert.Panicsf(t, func() {
		r.addRoute(http.MethodGet, "/order/detail/:id", mockHandleFunc)
	}, "web: 非法路由,已有通配符路由.不允许同时注册通配符路由和参数路由")
}

// TestRouter_findRoute_wildcard_and_param_coexist 测试针对注册通配符路由时,已有参数路由的情况
func TestRouter_findRoute_wildcard_and_param_coexist(t *testing.T) {
	// step1. 注册有冲突的路由
	r := newRouter()
	mockHandleFunc := func(ctx *Context) {}
	r.addRoute(http.MethodGet, "/order/detail/:id", mockHandleFunc)

	// step2. 断言非法用例
	assert.Panicsf(t, func() {
		r.addRoute(http.MethodGet, "/order/detail/*", mockHandleFunc)
	}, "web: 非法路由,已有参数路由.不允许同时注册通配符路由和参数路由")
}
```

以上两个测试用例均可顺利通过.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://go.sai.show/part03.-lu-you-shu/3.10-lu-you-shu-can-shu-lu-jing-zhi-jiao-yan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
