正如你可能观察到的,这个程序有严重的安全问题:一个用户可以随意提供路径在服务器上度/写。为缓解这个问题,我们可以写一个函数使用正则表达式校验标题。
首先,添加“regexp“到导入列表。然后我们可以创建全局变量来保存我们的验证表达式:
var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")
这个regexp.MustCompile函数将解析并编译正则表达式,并返回一个不同于Compile的regexp.Regexp.MustCompile,如果表达式编译失败它将引起恐慌失败。
现在,让我们写一个函数,用validPath表达式验证路径并导出页面标题:
func getTitle(w http.ResponseWriter, r *http.Request) (string, error) {
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return "", errors.New("Invalid Page Title")
}
return m[2], nil // The title is the second subexpression.
}
如果标题是有效的,它会顺便带着一个nil的错误值被返回。如果标题无效,此函数会将一个“404 Not Found“错误写入到HTTP连接,并向处理函数返回一个错误。为了创建一个新的错误,我们得导入errors包。
让我们在每个处理函数里放入一个getTitle的调用:
func viewHandler(w http.ResponseWriter, r *http.Request) {
title, err := getTitle(w, r)
if err != nil {
return
}
p, err := loadPage(title)
if err != nil {
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request) {
title, err := getTitle(w, r)
if err != nil {
return
}
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request) {
title, err := getTitle(w, r)
if err != nil {
return
}
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err = p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}