Revel路由仅匹配路径部分,不解析查询参数;正确做法是在routes中声明干净路径,控制器内用c.Params.Get()获取query/form/path参数,且同名时query优先于form和path。

Revel 的路由不是“配出来”的,而是靠 conf/routes 文件声明路径与控制器方法的映射关系,且不支持在路径中写查询参数(如 /company?name=:name),这种写法直接导致路由匹配失败。
路由文件里不能出现 ? 和查询参数占位符
很多刚接触 Revel 的人会下意识模仿其他框架,在 routes 里写 GET /company?name=:name APP.ShowByName,这是错的。Revel 的 RouterFilter 根本不解析 URL 中的 ? 后内容——它只匹配路径部分(path),所有 query string、form data、path param 都统一由 ParamsFilter 提取并塞进 c.Params。
正确做法是:
-
conf/routes中只写干净路径:GET /company APP.ShowByName - 控制器里用
c.Params.Get("name")或c.Params.Values["name"]拿值 - 若需多值(如
?tag=a&tag=b),c.Params.Values["tag"]返回[]string,而c.Params.Get("tag")只返回第一个
路径参数绑定::id 和 *filepath 的区别
:id 是单段路径变量,匹配除 / 外任意字符(如 /hotels/123、/hotels/abc);*filepath 是通配符,必须放在路由末尾,能匹配多级子路径(如 /public/js/app.js、/public/css/main.css)。
注意两个坑:
-
*filepath前必须有/,写成GET /public*filepath会匹配失败,正确是GET /public/*filepath -
:id绑定到函数参数时,类型要和实际传入一致(func(c Hotels) Show(id int)),否则Params.Bind()会静默失败或 panic - 如果路径含斜线后缀(如
/hotels/),默认和/hotels视为同一路由,除非你显式定义两条规则(不推荐)
RESTful 路由别硬凑,用好默认行为
Revel 不强制 REST 风格,但它的默认路由机制天然适配:比如 GET /companies → Companies.Index,POST /companies → Companies.Create。不需要在 routes 里逐条写 GET /companies/:id Companies.Show 这类规则——只要控制器方法签名匹配(如 func(c Companies) Show(id int)),ParamsFilter 就会自动把 :id 解析出来。
更省事的是,如果你不想用路径参数,也可以全走 query 参数:
- 路由写
GET /companies APP.ListCompanies - 控制器里用
c.Params.Get("page")、c.Params.Get("limit")等统一读取 - 这样前后端约定更松,也避免因路径格式变化引发 404
反向路由生成要注意 controller.action 名字拼写
Revel 支持通过 revel.URLFor("APP.ShowByName", "name", "ABC") 生成带 query 的完整 URL(如 /company?name=ABC)。但这里 "APP.ShowByName" 必须和 conf/routes 中声明的完全一致,包括大小写和点号位置。
常见翻车点:
- 写成
"app.ShowByName"(小写 app)→ 生成空字符串 - 写成
"APP.ShowbyName"(驼峰错位)→ 运行时报Action not found - query 参数键名必须是字符串字面量,不能是变量(
revel.URLFor("APP.ShowByName", "name", nameVar)是合法的,但键名"name"不能动态算出来)
真正容易被忽略的,是 c.Params 的聚合逻辑:它把 query、form、path、multipart 全混在一起,同名 key 时 query 优先于 form,form 优先于 path——这个顺序不会变,但如果你在同一个请求里既提交了 ?id=123 又 POST 了 id=456,最终 c.Params.Get("id") 拿到的是 123,不是 456。



















