15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > GO实战训练:如何快速搭建一个web应用

GO实战训练:如何快速搭建一个web应用

时间:2023-06-02 02:12:01 | 来源:网站运营

时间:2023-06-02 02:12:01 来源:网站运营

GO实战训练:如何快速搭建一个web应用:GO实战训练:如何快速搭建一个web应用

简介

本次训练主要包括的内容

1. 创建一个可以load & save的数据结构

2. 学会利用 net/http 包去构建一个web应用

3. 学会利用 html/template 包去处理HTML模板

Go的安装

具体可以参考 https://golang.org/doc/install

开始

mkdir gowikicd gowikitouch wiki.go在wiki.go中,我们先import两个基本的包

package mainimport ( "fmt" "io/ioutil")

数据结构

让我们从定义网页的数据结构开始。一个wiki包括一系列相互连通的网页每个网页有title和body(网页内容)构成。在这里,我们定义结构体 Page,包含两个成员变量。

type Page struct { Title string Body []byte}[]byte是一个byte的切片类型。那为什么我们要使用切片而不是string 类型来定义body呢?是因为我们使用的io包使用的是这个切片类型。

现在,有了这个Page结构体,我们能够暂时将网页的内容保存在内存中了。但是在用户对wiki页面进行编辑后,怎么将内容永久的保存下来呢?解决这个问题的话,我们需要给Page这个结构体,定义一个save的方法

func (p *Page) save() error { filename := p.Title + ".txt" return ioutil.WriteFile(filename, p.Body, 0600)}这个save的方法,有个特殊的参数,也叫做receiver,一个指向Page实例的指针p,意味着这个函数是Page这个结构体的成员函数,可以直接通过dot访问,比如p.save(),并以error作为返回值。0600是一个八进制的数字,作为WriteFile的参数,表示创建一个有读写权限的文件。

另外,除了保存页面,我们还需要在用户对一个关键词进行查询时,load本地保存好的内容到网页上进行展示。同时我们还需要将读取过程中产生的错误返回出去,方便调用这个函数的地方对其进行处理。举个例子,如果用户请求的关键词不存在的话,我们可能需要将用户指引到编辑页面上去,让用户创建词条。

func loadPage(title string) (*Page, error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return &Page{Title: title, Body: body}, nil}那么到目前为止,我们已经能够在本地加载并且保存相关的网页了,让我们先完成简单的main函数,

func main() { p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")} p1.save() p2, _ := loadPage("TestPage") fmt.Println(string(p2.Body))}简单的尝试运行,就可以看到下面的输出了。

$ go build wiki.go$ ./wikiThis is a sample Page.那么,怎么将我们的代码变成网站呢?Go的标准库,net/http 可以很轻松的帮我们做到这一点。

简单介绍一下 net/http 这个包

这里我们简单来介绍一下GO语言中http这个官方库,先来看一个简单的例子,

package mainimport ( "fmt" "log" "net/http")func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])}func main() { http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe(":8080", nil))}main函数中声明了http.HandleFunc这一行代码,告诉httphandler函数将会处理web服务器根目录下的所有请求。然后main函数调用了http.ListenAndServe这个函数,指明我们将会一直监听8080这个端口直到程序结束。

接下来我们说明一下handler这个函数,我们这里定义的handler函数其实是http.HandleFunc的一种,它将http.ResponseWriter以及http.Request作为输入参数。 http.ResponseWriter会将HTTP服务器的回复组装在一起,通过对它进行写数据,我们可以将服务器的数据发送给客户端。http.Request是保存客户端请求的数据结构,r.URL.Path是请求中的路径数据,索引[1:]获取了/之后的所有字符。接下来我们直接运行代码,然后在浏览器中输入以下网址,

http://localhost:8080/monkeys,我们可以看到以下的输出

Hi there, I love monkeys!在初步了解了net/http这个库以后,那么怎么将它和我们的wiki网站制作结合在一起呢?

net/http来搭建wiki网站

查看页面

首先,我们要import这个包,

import ( "fmt" "io/ioutil" "net/http")然后我们需要创建一个handler,viewHandler主要处理用户发来查看页面的请求,这类请求的URL会以/view/为开头。

func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/view/"):] p, _ := loadPage(title) fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body)}这里我们暂时不去处理loadPage可能产生的错误,之后会一步一步完善。简单讲解一下这个函数的意思,首先viewHandler会从path中抽取出/view/之后的字符,然后加载该名字的HTML的内容,写到response里面返回给客户端。

接下来,我们重写main函数,初始化一个handler去处理view的请求,为了让代码跑起来,我们需要在gowiki的目录下新建一个test.txt的文本,并在里面写入任意内容,方便加载。然后我们在浏览器中输入http://localhost:8080/view/test,可以再浏览器页面上看到文本中的内容。

func main() { http.HandleFunc("/view/", viewHandler) log.Fatal(http.ListenAndServe(":8080", nil))}

编辑页面

一个完整的wiki网站,除了查看功能以外,我们还需要提供对已存在页面的编辑功能,并且保存该编辑到服务器,以便之后加载的一个保存功能。因此,我们需要在main函数中提供两个额外的handler,

func main() { http.HandleFunc("/view/", viewHandler) http.HandleFunc("/edit/", editHandler) http.HandleFunc("/save/", saveHandler) log.Fatal(http.ListenAndServe(":8080", nil))}editHandler函数提供了编辑功能,如果页面不存在的话,我们这里暂时先返回一个空页面给用户。

func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } fmt.Fprintf(w, "<h1>Editing %s</h1>"+ "<form action=/"/save/%s/" method=/"POST/">"+ "<textarea name=/"body/">%s</textarea><br>"+ "<input type=/"submit/" value=/"Save/">"+ "</form>", p.Title, p.Title, p.Body)}目前的话,我们的函数能够正常工作,但是硬编码的HTML还是不推荐的,在这里我们介绍一种更好的方式,利用 html/template的包来优化这段代码,让我们的代码看起来更优雅。

简单介绍一下 htmp/template

html/template 是GO的一个标准库。我们可以使用这个包来将HTML的模板保存在另外的文件中,这样的话,如果我们要修改页面布局的话,可以单纯的修改HTML的模板文件,而不用修改GO代码。

同样,我们先import这个包

import ( "html/template" "io/ioutil" "net/http")

edit模板

接下来我们创建一个名为edit.html的模板文件,并在文件中添加以下内容

<h1>Editing {{.Title}}</h1><form action="/save/{{.Title}}" method="POST"><div><textarea name="body" rows="20" cols="80">{{printf "%s" .Body}}</textarea></div><div><input type="submit" value="Save"></div></form>然后对应修改editHandler的代码,来避免硬编码. template.ParseFiles将会读取保存在edit.html中的模板内容,然后返回 *template.Template 类型的数据。然后 t.Execute 将会执行这个template,并将生成的HTML发送给http.ResponseWriter, 模板中 .Title.Body 将会分别持有p中对应的内容。

func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } t, _ := template.ParseFiles("edit.html") t.Execute(w, p)}

view模板

相应的,其实我们也可以对view的HTML进行一个模板化处理

<h1>{{.Title}}</h1><p>[<a href="/edit/{{.Title}}">edit</a>]</p><div>{{printf "%s" .Body}}</div>同样,我们修改viewHandler的代码

func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/view/"):] p, _ := loadPage(title) t, _ := template.ParseFiles("view.html") t.Execute(w, p)}

使用模板后的代码优化

在使用模板后,其实viewHandler 以及 editHandler的代码是由部分重复代码的,为了防止”破窗效应“,我们将模板这部分代码抽象出来

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFiles(tmpl + ".html") t.Execute(w, p)}对应优化handler的代码

func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] p, err := loadPage(title) if err != nil { p = &Page{Title: title} } renderTemplate(w, "edit", p)}

保存页面

在用户对页面进行编辑后,我们需要对用户在edit页面提交的form进行保存,因此我们实现以下函数

func saveHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/save/"):] body := r.FormValue("body") p := &Page{Title: title, Body: []byte(body)} p.save() http.Redirect(w, r, "/view/"+title, http.StatusFound)}这里有一点需要注意的是,r.FormValue返回的值是string,我们需要将它强制转换成[]byte类型。




(未完待续)

关键词:训练,实战

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭