第十五章修改 (#834)

Co-authored-by: Joe Chen <jc@unknwon.io>
This commit is contained in:
Haigang Zhou
2022-05-17 16:33:20 +08:00
committed by GitHub
parent 60fe3dd076
commit 72f2eccbc5
12 changed files with 102 additions and 92 deletions

View File

@@ -133,7 +133,7 @@ func load(title string) (*Page, error) {
```
把要插入的数据结构字段放在 `{{` 和 `}}` 之间,这里是把 `Page` 结构体数据 `{{.Title |html}}` 和 `{{printf "%s" .Body |html}}` 插入页面(当然可以是非常复杂的 html但这里尽可能地简化了以突出模板的原理。`{{.Title |html}}` 和 `{{printf "%s" .Body |html}}` 语法说明详见后续章节)。
2. `template.Must(template.ParseFiles(tmpl + ".html"))` 把模板文件转换为 `*template.Template` 类型的对象,为了高效,在程序运行时仅做一次解析,在 `init()` 函数中处理可以方便地达到目的。所有模板对象都被保持在内存中,存放在以 html 文件名作为索引的 map 中:
2. `template.Must(template.ParseFiles(tmpl + ".html"))` 把模板文件转换为 `*template.Template` 类型的对象,为了高效,在程序运行时仅做一次解析,在 `init()` 函数中处理可以方便地达到目的。所有模板对象都被保持在内存中,存放在以 html 文件名作为索引的 `map` 中:
```go
templates = make(map[string]*template.Template)
```
@@ -143,10 +143,10 @@ func load(title string) (*Page, error) {
```go
templates[tmpl].Execute(w, p)
```
它基于模板执行,用 `Page` 结构体对象 p 作为参数对模板进行替换,并写入 `ResponseWriter` 对象 w。必须检查该方法的 error 返回值,万一有一个或多个错误,我们可以调用 `http.Error` 来明示。在我们的应用程序中,这段代码会被多次调用,所以把它提取为单独的函数 `renderTemplate`。
- 在 `main()` 中网页服务器用 `ListenAndServe` 启动并监听 8080 端口。但正如 [15.2节](15.2.md) 那样,需要先为紧接在 URL `localhost:8080/` 之后, 以`view`, `edit` 或 `save` 开头的 url 路径定义一些处理函数。在大多数网页服务器应用程序中,这形成了一系列 URL 路径到处理函数的映射,类似于 Ruby 和 RailsDjango 或 ASP.NET MVC 这样的 MVC 框架中的路由表。请求的 URL 与这些路径尝试匹配,较长的路径被优先匹配。如不与任何路径匹配,则调用 / 的处理程序。
它基于模板执行,用 `Page` 结构体对象 `p` 作为参数对模板进行替换,并写入 `ResponseWriter` 对象 `w`。必须检查该方法的 `error` 返回值,万一有一个或多个错误,我们可以调用 `http.Error()` 来明示。在我们的应用程序中,这段代码会被多次调用,所以把它提取为单独的函数 `renderTemplate()`。
- 在 `main()` 中网页服务器用 `ListenAndServe()` 启动并监听 8080 端口。但正如 [15.2节](15.2.md) 那样,需要先为紧接在 URL `localhost:8080/` 之后, 以 `view`, `edit` 或 `save` 开头的 url 路径定义一些处理函数。在大多数网页服务器应用程序中,这形成了一系列 URL 路径到处理函数的映射,类似于 Ruby 和 RailsDjango 或 ASP.NET MVC 这样的 MVC 框架中的路由表。请求的 URL 与这些路径尝试匹配,较长的路径被优先匹配。如不与任何路径匹配,则调用 / 的处理程序。
在此定义了 3 个处理函数,由于包含重复的启动代码,我们将其提取到单独的 `makeHandler` 函数中。这是一个值得研究的特殊高阶函数:其参数是一个函数,返回一个新的闭包函数:
在此定义了 3 个处理函数,由于包含重复的启动代码,我们将其提取到单独的 `makeHandler()` 函数中。这是一个值得研究的特殊高阶函数:其参数是一个函数,返回一个新的闭包函数:
```go
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -159,10 +159,10 @@ func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.Handl
}
}
```
- 闭包封闭了函数变量 `fn` 来构造其返回值。但在此之前,它先用 `titleValidator.MatchString(title)` 验证输入标题 `title` 的有效性。如果标题包含了字母和数字以外的字符,就触发 NotFound 错误(例如:尝试 `localhost:8080/view/page++`)。`viewHandler``editHandler` 和 `saveHandler` 都是传入 `main()` 中 `makeHandler` 的参数,类型必须都与 `fn` 相同。
- 闭包封闭了函数变量 `fn` 来构造其返回值。但在此之前,它先用 `titleValidator.MatchString(title)` 验证输入标题 `title` 的有效性。如果标题包含了字母和数字以外的字符,就触发 `NotFound` 错误(例如:尝试 `localhost:8080/view/page++`)。`viewHandler``editHandler` 和 `saveHandler` 都是传入 `main()` 中 `makeHandler` 的参数,类型必须都与 `fn` 相同。
- `viewHandler` 尝试按标题读取文本文件,这是通过调用 `load()` 函数完成的,它会构建文件名并用 `ioutil.ReadFile` 读取内容。如果文件存在,其内容会存入字符串中。一个指向 `Page` 结构体的指针按字面量被创建:`&Page{Title: title, Body: body}`。
另外,该值和表示没有 error 的 nil 值一起返回给调用者。然后在 `renderTemplate` 中将该结构体与模板对象整合。
另外,该值和表示没有 error 的 `nil` 值一起返回给调用者。然后在 `renderTemplate` 中将该结构体与模板对象整合。
万一发生错误,也就是说 wiki 页面在磁盘上不存在,错误会被返回给 `viewHandler`,此时会自动重定向,跳转请求对应标题的编辑页面。