这一期单独讲一讲路由。在之前,我们编写的程序会针对不同的路径触发不同的 Handler 进行处理,这就属于一种路由。但是之前的例子都是把处理部分放在一个文件甚至一个函数中,这样做不便于后期维护。我们可以设置一个 ​​Controller​​ 层,把不同的请求送到不同的 controller 进行处理,并控制相应的静态资源(例如 css、js、图片等)。

Go 语言提供了一个前置的 controller ,所有进来的请求都会经过这个前置的 controller ,然后这个前置的 controller 再决定要走向具体的哪个 controller 或者 handler 。下图是一个例子,如果有一个请求,URL 是 ​​/about​​​ ,该请求来到前置的 controller ,前置的 controller 会根据请求,匹配具体的 handler ,即下图中的 ​​about handler​​​ ,随后 ​​about handler​​ 就会处理该请求,并返回响应给请求的发送方。

Go Web 路由_html

下面讲一个例子,首先在工程目录下创建一个名为 ​​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)
}

工程目录结构如下:

Go Web 路由_整型_02


运行程序,访问 ​​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​​ 。

当然,我们还可以编写自己的路由规则,有需要的话也可以使用第三方路由器,这里暂时不做介绍。