目前为止,我们的代码中有一段重复性很高的代码:
w.Header().Set("Content-Type", "text/html; charset=utf-8")
使用中间件
我们可以使用中间件来完成该操作。
main.go
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "<h1>Hello, 欢迎来到 goblog!</h1>")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "此博客是用以记录编程笔记,如您有反馈或建议,请联系 "+
"<a href=\"mailto:summer@example.com\">summer@example.com</a>")
}
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "<h1>请求页面未找到 :(</h1><p>如有疑惑,请联系我们。</p>")
}
func articlesShowHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
fmt.Fprint(w, "文章 ID:"+id)
}
func articlesIndexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "访问文章列表")
}
func articlesStoreHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "创建新的文章")
}
func forceHTMLMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 设置标头
w.Header().Set("Content-Type", "text/html; charset=utf-8")
// 2. 继续处理请求
next.ServeHTTP(w, r)
})
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/", homeHandler).Methods("GET").Name("home")
router.HandleFunc("/about", aboutHandler).Methods("GET").Name("about")
router.HandleFunc("/articles/{id:[0-9]+}", articlesShowHandler).Methods("GET").Name("articles.show")
router.HandleFunc("/articles", articlesIndexHandler).Methods("GET").Name("articles.index")
router.HandleFunc("/articles", articlesStoreHandler).Methods("POST").Name("articles.store")
// 自定义 404 页面
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
// 中间件:强制内容类型为 HTML
router.Use(forceHTMLMiddleware)
// 通过命名路由获取 URL 示例
homeURL, _ := router.Get("home").URL()
fmt.Println("homeURL: ", homeURL)
articleURL, _ := router.Get("articles.show").URL("id", "1")
fmt.Println("articleURL: ", articleURL)
http.ListenAndServe(":3000", router)
}
可以看到我们新增了 forceHTMLMiddleware
中间件方法:
func forceHTMLMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 设置标头
w.Header().Set("Content-Type", "text/html; charset=utf-8")
// 2. 继续处理请求
h.ServeHTTP(w, r)
})
}
然后使用 Gorilla Mux 的 mux.Use()
方法来加载中间件:
router.Use(forceHTMLMiddleware)
浏览器打开 localhost:3000/about ,或者其他存在的页面,查看header
解决路径结尾斜杠问题
访问以下两个链接:
可以看到有 /
的链接会报 404 错误。
我们希望 URL 后面是否加斜杆的情况下,皆使用同一个返回结果。
StrictSlash
对于这个问题 Gorilla Mux 提供了一个 StrictSlash(value bool)
函数,我们先来试试:
在 main
函数中,请将以下这一行:
router := mux.NewRouter()
修改为:
router := mux.NewRouter().StrictSlash(true)
浏览器再次访问 localhost:3000/about/
可以看到 URL 被校正了,跳转速度太快,我们再次试试看。
我们打开 Chrome 的控制台,查看请求,注意把 Disable cache
打钩
以看到当请求 about/
时产生了两个请求,第一个是 301 跳转,第二个是跳转到的 about
去掉斜杆的链接。
浏览器在处理 301 请求时,会缓存起来。后续的 about/
浏览器都会自动去请求 about
链接,也就是说两次请求只会在第一次的时候发生。
这个解决方案看起来不错,然而有一个严重的问题 —— 当请求方式为 POST 的时候,遇到服务端的 301
跳转,将会变成 GET 方式。很明显,这并非所愿,我们需要一个更好的方案。
兼容 POST 请求
我们需要在 URL 进入 Gorilla Mux 路由解析之前,就将后面的 /
去掉。
像这种针对所有请求的操作,你第一时间想到的可能是用中间件处理,然而因为执行顺序的问题,Gorilla Mux 会先匹配路由,再执行中间件,故使用中间件永远会返回 404.
解决方法很简单,那就是写一个函数把 Gorilla Mux 包起来,在这个函数中我们先对进来的请求做处理,然后再传给 Gorilla Mux 去解析。
接下来新增 removeTrailingSlash()
函数并调用:
main.go
.
.
.
func removeTrailingSlash(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.TrimSuffix(r.URL.Path, "/")
next.ServeHTTP(w, r)
})
}
func main() {
.
.
.
http.ListenAndServe(":3000", removeTrailingSlash(router))
}Copy
我们使用 strings 包提供的 TrimSuffix(s, suffix string) string
函数来移除 /
后缀,如果不带斜杆后缀的话,r.URL.Path
将会被原封不动地返回。
修改保持后,再次访问 localhost:3000/about/,可见正常的结果
偶然间,我们又发现了另一个问题,访问主页 localhost:3000/ 会出现错误
那是因为我们的 removeTrailingSlash
中这一段:
r.URL.Path = strings.TrimSuffix(r.URL.Path, "/")
会将我们的首页 URL /
给去除了,解决方法是把 /
路径排除在外:
main.go
func removeTrailingSlash(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 除首页以外,移除所有请求路径后面的斜杆
if r.URL.Path != "/" {
r.URL.Path = strings.TrimSuffix(r.URL.Path, "/")
}
// 2. 将请求传递下去
next.ServeHTTP(w, r)
})
}
保存修改后,再次访问 localhost:3000/ ,可见正常的结果