这一期单独讲一讲路由。在之前,我们编写的程序会针对不同的路径触发不同的 Handler 进行处理,这就属于一种路由。但是之前的例子都是把处理部分放在一个文件甚至一个函数中,这样做不便于后期维护。我们可以设置一个 Controller
层,把不同的请求送到不同的 controller 进行处理,并控制相应的静态资源(例如 css、js、图片等)。
Go 语言提供了一个前置的 controller ,所有进来的请求都会经过这个前置的 controller ,然后这个前置的 controller 再决定要走向具体的哪个 controller 或者 handler 。下图是一个例子,如果有一个请求,URL 是 /about
,该请求来到前置的 controller ,前置的 controller 会根据请求,匹配具体的 handler ,即下图中的 about handler
,随后 about handler
就会处理该请求,并返回响应给请求的发送方。
下面讲一个例子,首先在工程目录下创建一个名为 controller
的 package ,在此 package 中分别创建处理不同路径请求的 handler :
以下为 home.go 代码,用于处理 /home
请求:
package controller
import (
"html/template"
"net/http"
)
func registerHomeRoutes() {
http.HandleFunc("/home", handleHome)
}
func handleHome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("home.html")
t.Execute(w, "Home")
}
以下为 about.go 代码,用于处理 /about
请求:
package controller
import (
"html/template"
"net/http"
)
func registerAboutRoutes() {
http.HandleFunc("/about", handleAbout)
}
func handleAbout(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("about.html")
t.Execute(w, "About")
}
以下为 contact.go 代码,用于处理 /contact
请求:
package controller
import (
"html/template"
"net/http"
)
func registerContactRoutes() {
http.HandleFunc("/contact", handleContact)
}
func handleContact(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("contact.html")
t.Execute(w, "Contact")
}
然后还在 controller
目录下创建 controller.go 文件,用于注册所有路径的请求:
package controller
func RegisterRoutes() {
registerHomeRoutes()
registerAboutRoutes()
registerContactRoutes()
}
然后我们在工程目录下分别创建 HTML 文件, home.html 文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<h1>{{ . }}</h1>
</body>
</html>
about.html 文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>About</title>
</head>
<body>
<h1>{{ . }}</h1>
</body>
</html>
contact.html 文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Contact</title>
</head>
<body>
<h1>{{ . }}</h1>
</body>
</html>
最后创建 main.go 文件:
package main
import (
"goweb/controller"
"net/http"
)
func main() {
// 注册所有路由
controller.RegisterRoutes()
http.ListenAndServe("localhost:8080", nil)
}
工程目录结构如下:
运行程序,访问 http://localhost:8080/home
进入 Home 页面,显示 Home
;
访问 http://localhost:8080/about
进入 About 页面,显示 About
;
访问 http://localhost:8080/contact
进入 Contact 页面,显示 Contact
。
上面讲的路由都属于静态路由,即一个路径对应一个页面,但是一般的路由还带有参数,根据路由参数,显示的内容也有所不同。基于上面的例子,我们创建一个 popular.html 文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Popular</title>
</head>
<body>
<h1>{{ . }}</h1>
</body>
</html>
同理,我们在 controller
目录下创建 popular.go 文件:
package controller
import (
"html/template"
"net/http"
"regexp"
"strconv"
)
func registerPopularRoutes() {
http.HandleFunc("/popular", handlePopular)
// 如果 URL 带参数 就会匹配更具体的 handler 即下面这个 handler
http.HandleFunc("/popular/", handlePopularId)
}
func handlePopular(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("popular.html")
t.Execute(w, "Popular")
}
func handlePopularId(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("popular.html")
pattern, _ := regexp.Compile(`/popular/(\d+)`)
matches := pattern.FindStringSubmatch(r.URL.Path)
if len(matches) > 0 {
id, _ := strconv.Atoi(matches[1])
t.Execute(w, id)
} else {
w.WriteHeader(http.StatusNotFound)
}
}
然后记得在 controller.go 文件中补充调用注册路径函数:
package controller
func RegisterRoutes() {
registerHomeRoutes()
registerAboutRoutes()
registerContactRoutes()
registerPopularRoutes()
}
运行程序,访问 http://localhost:8080/popular
进入 Popular 页面,显示 Popular
;
带整型参数 ID 访问该路径时,页面会显示路径带的参数 ID ,例如访问 http://localhost:8080/popular/123
,页面显示 123
。
当然,我们还可以编写自己的路由规则,有需要的话也可以使用第三方路由器,这里暂时不做介绍。