時(shí)間:2023-06-02 02:12:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)
時(shí)間:2023-06-02 02:12:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)
GO實(shí)戰(zhàn)訓(xùn)練:如何快速搭建一個(gè)web應(yīng)用:GO實(shí)戰(zhàn)訓(xùn)練:如何快速搭建一個(gè)web應(yīng)用mkdir gowikicd gowikitouch wiki.go
在wiki.go中,我們先import兩個(gè)基本的包package mainimport ( "fmt" "io/ioutil")
Page
,包含兩個(gè)成員變量。type Page struct { Title string Body []byte}
[]byte
是一個(gè)byte的切片類(lèi)型。那為什么我們要使用切片而不是string
類(lèi)型來(lái)定義body呢?是因?yàn)槲覀兪褂玫膇o包使用的是這個(gè)切片類(lèi)型。Page
結(jié)構(gòu)體,我們能夠暫時(shí)將網(wǎng)頁(yè)的內(nèi)容保存在內(nèi)存中了。但是在用戶(hù)對(duì)wiki頁(yè)面進(jìn)行編輯后,怎么將內(nèi)容永久的保存下來(lái)呢?解決這個(gè)問(wèn)題的話,我們需要給Page
這個(gè)結(jié)構(gòu)體,定義一個(gè)save
的方法func (p *Page) save() error { filename := p.Title + ".txt" return ioutil.WriteFile(filename, p.Body, 0600)}
這個(gè)save
的方法,有個(gè)特殊的參數(shù),也叫做receiver,一個(gè)指向Page
實(shí)例的指針p
,意味著這個(gè)函數(shù)是Page
這個(gè)結(jié)構(gòu)體的成員函數(shù),可以直接通過(guò)dot訪問(wèn),比如p.save()
,并以error作為返回值。0600
是一個(gè)八進(jìn)制的數(shù)字,作為WriteFile
的參數(shù),表示創(chuàng)建一個(gè)有讀寫(xiě)權(quán)限的文件。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}
那么到目前為止,我們已經(jīng)能夠在本地加載并且保存相關(guān)的網(wǎng)頁(yè)了,讓我們先完成簡(jiǎn)單的main
函數(shù),func main() { p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")} p1.save() p2, _ := loadPage("TestPage") fmt.Println(string(p2.Body))}
簡(jiǎn)單的嘗試運(yùn)行,就可以看到下面的輸出了。$ go build wiki.go$ ./wikiThis is a sample Page.
那么,怎么將我們的代碼變成網(wǎng)站呢?Go的標(biāo)準(zhǔn)庫(kù),net/http
可以很輕松的幫我們做到這一點(diǎn)。net/http
這個(gè)包GO
語(yǔ)言中http
這個(gè)官方庫(kù),先來(lái)看一個(gè)簡(jiǎn)單的例子,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
函數(shù)中聲明了http.HandleFunc
這一行代碼,告訴http
handler函數(shù)將會(huì)處理web服務(wù)器根目錄下的所有請(qǐng)求。然后main
函數(shù)調(diào)用了http.ListenAndServe
這個(gè)函數(shù),指明我們將會(huì)一直監(jiān)聽(tīng)8080這個(gè)端口直到程序結(jié)束。handler
這個(gè)函數(shù),我們這里定義的handler函數(shù)其實(shí)是http.HandleFunc
的一種,它將http.ResponseWriter
以及http.Request
作為輸入?yún)?shù)。 http.ResponseWriter
會(huì)將HTTP服務(wù)器的回復(fù)組裝在一起,通過(guò)對(duì)它進(jìn)行寫(xiě)數(shù)據(jù),我們可以將服務(wù)器的數(shù)據(jù)發(fā)送給客戶(hù)端。http.Request
是保存客戶(hù)端請(qǐng)求的數(shù)據(jù)結(jié)構(gòu),r.URL.Path
是請(qǐng)求中的路徑數(shù)據(jù),索引[1:]
獲取了/
之后的所有字符。接下來(lái)我們直接運(yùn)行代碼,然后在瀏覽器中輸入以下網(wǎng)址,http://localhost:8080/monkeys
,我們可以看到以下的輸出Hi there, I love monkeys!
在初步了解了net/http
這個(gè)庫(kù)以后,那么怎么將它和我們的wiki網(wǎng)站制作結(jié)合在一起呢?net/http
來(lái)搭建wiki網(wǎng)站import ( "fmt" "io/ioutil" "net/http")
然后我們需要?jiǎng)?chuàng)建一個(gè)handler,viewHandler
主要處理用戶(hù)發(fā)來(lái)查看頁(yè)面的請(qǐng)求,這類(lèi)請(qǐng)求的URL會(huì)以/view/
為開(kāi)頭。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)}
這里我們暫時(shí)不去處理loadPage可能產(chǎn)生的錯(cuò)誤,之后會(huì)一步一步完善。簡(jiǎn)單講解一下這個(gè)函數(shù)的意思,首先viewHandler會(huì)從path中抽取出/view/
之后的字符,然后加載該名字的HTML的內(nèi)容,寫(xiě)到response里面返回給客戶(hù)端。test.txt
的文本,并在里面寫(xiě)入任意內(nèi)容,方便加載。然后我們?cè)跒g覽器中輸入http://localhost:8080/view/test,可以再瀏覽器頁(yè)面上看到文本中的內(nèi)容。func main() { http.HandleFunc("/view/", viewHandler) log.Fatal(http.ListenAndServe(":8080", nil))}
func main() { http.HandleFunc("/view/", viewHandler) http.HandleFunc("/edit/", editHandler) http.HandleFunc("/save/", saveHandler) log.Fatal(http.ListenAndServe(":8080", nil))}
editHandler
函數(shù)提供了編輯功能,如果頁(yè)面不存在的話,我們這里暫時(shí)先返回一個(gè)空頁(yè)面給用戶(hù)。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)}
目前的話,我們的函數(shù)能夠正常工作,但是硬編碼的HTML還是不推薦的,在這里我們介紹一種更好的方式,利用 html/template
的包來(lái)優(yōu)化這段代碼,讓我們的代碼看起來(lái)更優(yōu)雅。htmp/template
html/template
是GO的一個(gè)標(biāo)準(zhǔn)庫(kù)。我們可以使用這個(gè)包來(lái)將HTML的模板保存在另外的文件中,這樣的話,如果我們要修改頁(yè)面布局的話,可以單純的修改HTML的模板文件,而不用修改GO代碼。import ( "html/template" "io/ioutil" "net/http")
<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>
然后對(duì)應(yīng)修改editHandler的代碼,來(lái)避免硬編碼. template.ParseFiles
將會(huì)讀取保存在edit.html中的模板內(nèi)容,然后返回 *template.Template
類(lèi)型的數(shù)據(jù)。然后 t.Execute
將會(huì)執(zhí)行這個(gè)template,并將生成的HTML發(fā)送給http.ResponseWriter
, 模板中 .Title
和 .Body
將會(huì)分別持有p中對(duì)應(yīng)的內(nèi)容。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
的HTML進(jìn)行一個(gè)模板化處理<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
的代碼是由部分重復(fù)代碼的,為了防止”破窗效應(yīng)“,我們將模板這部分代碼抽象出來(lái)func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFiles(tmpl + ".html") t.Execute(w, p)}
對(duì)應(yīng)優(yōu)化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)}
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)}
這里有一點(diǎn)需要注意的是,r.FormValue
返回的值是string,我們需要將它強(qiáng)制轉(zhuǎn)換成[]byte
類(lèi)型。關(guān)鍵詞:訓(xùn)練,實(shí)戰(zhàn)
客戶(hù)&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
客戶(hù)&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
微信公眾號(hào)
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。