已經介紹過基本的 Web 知識與安裝好相關環境了,接下來我們就使用 golang 原生提供的 net/http
package 來建立一個簡單的網頁吧!
目標
我們預計建立一個簡單的網站,網站要符合以下幾個條件
- 存取 url 為
http://127.0.0.1:8888
- path 為
/
或是/index
的時候存取首頁 - 首頁必須要是
html
格式
目標設定好就讓我們開始吧!
建立第一支網頁程式
透過 golang 建立網頁程式一點都不能,就讓我們一步一步的進行吧!
import 相關 package
在最一開始,我們 import 兩個 package 分別為 log
與 net/http
import (
"log"
"net/http"
)
log
就是用來輸出程式目前執行狀態的,log 的輸出可以分成不同的等級,這個我們後續會特別抽出來談net/http
就是用來將網頁運行起來的關鍵,包含伺服器監聽與運行的方法與 request handler 都使用此 package 來實作
建立一個基本的 request handler
在 test
這個方法中我們可以看到有兩個參數分別為 http.ResponseWriter
與 http.Request
,這兩個參數分別對應到我們昨天所提到的 response
與 request
func test(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`my first website`))
}
這個範例中,我們在方法中寫了兩行
- 第一行就是在
header
寫入了http.StatusOK
這個值,這個數值也是net/http
這個 package 所提供的,他其實就是把status code
給封裝起來,讓我們比較方便呼叫,因此,http.StatusOK
就是等於200
- 第二行就是寫入
response
的內容,回傳的型態是[]byte
刑事,因此我們寫入[]byte(`my first website`)
加入 routing
routing 的部分在此範例中我們寫在 main
方法中,範例如下
http.HandleFunc("/", test)
我們實作了 net/http
package 中的 HandleFunc
方法,將上一部所寫的 test
方法與 /
這個 routing 進行綁定,讓 server 知道當進來的 traffic 的 routing 為 /
時要執行 test
方法
開始運行伺服器
直接實作 net/http
中提供的 ListenAndServe
方法,一樣是寫在 main
方法中
err := http.ListenAndServe(":8888", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
ListenAndServe
有兩個參數分別為 address
與 handler
address
為存取的url
與port
,因為沒有指定url
所以沒有填寫,只單純寫port
- 本範例中沒有實作
handler
,因此將它填寫為nil
,
綜合上述步驟程式碼最終會像是這樣
package mainimport (
"log"
"net/http"
)func test(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`my first website`))
}func main() {
http.HandleFunc("/", test)
err := http.ListenAndServe(":8888", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
將程式碼儲存成 main.go
後,在 command line
中透過 go run main.go
指令將程式運行起來後,會看到以下畫面,程式會進入一個無窮迴圈不會停止,這樣就對了!
之後在瀏覽器輸入 http://127.0.0.1:8888
看到以下畫面就代表我們第一支網頁程式就完成拉!
你心裡一定想說「什麼?!就這麼簡單嗎?今天就這麼結束了嗎!」
想得太美了!我們今天的目標還沒達成呢!
接下來我們會製作 html 畫面
製作首頁與登入頁
寫完上面的第一支程式之後,想必大家對網頁開發有一定的感覺了,因此我們就順著這個感覺走,在製作首頁與登入頁的部分就是兩個方向
- 在 request handler 中回傳
html
畫面 - 在
routing
的部分加入/index
並綁定request handler
在 request handler 中回傳 html
畫面
要回傳 html 還不簡單,只要在 response 中寫入 html 就可以了吧!
修改上面的範例如下
func test(w http.ResponseWriter, r *http.Request) {
str := `<!DOCTYPE html>
<html>
<head><title>首頁</title></head>
<body><h1>首頁</h1><p>我的第一個首頁</p></body>
</html>
`
w.Write([]byte(str))
}
重新運行 go run main.go
後存取 http://127.0.0.1:8888
會出現以下的畫面
是的沒錯,這樣的確是成功的顯示 html 的網頁,但每次要修改 html 的時候就要重新修改程式,html 的內容也不能是動態的!
因此,我們可以使用 html/template
package 來解決這個問題!
template
首先,我們先定義好 template 內所要的參數
type IndexData struct {
Title string
Content string
}
我們可以在 import 的部分將 html/template
進行導入
import (
"log"
"html/template"
"net/http"
)
之後將原先的 html 內容抽出來,建立與放入與 main.go
同一個目錄底下名為 index.html
的檔案,{{}}
內的參數對應到上方 IndexData
結構內的變數
<!DOCTYPE html>
<html>
<head><title>{{.Title}}</title></head>
<body><h1>{{.Title}}</h1><p>{{.Content}}</p></body>
</html>
最後我們就可以修改原先的 request handler 如下
func test(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles("./index.html"))
data := new(IndexData)
data.Title = "首頁"
data.Content = "我的第一個首頁"
tmpl.Execute(w, data)
}
完成後程式碼如下
package mainimport (
"html/template"
"log"
"net/http"
)type IndexData struct {
Title string
Content string
}func test(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles("./index.html"))
data := new(IndexData)
data.Title = "首頁"
data.Content = "我的第一個首頁"
tmpl.Execute(w, data)
}
func main() {
http.HandleFunc("/", test)
err := http.ListenAndServe(":8888", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
重新運行 go run main.go
後存取 http://127.0.0.1:8888
的畫面與上方相同,但這樣的寫法更為動態,不但讓我們可以單獨維護 html
檔案,也可以在 html 內動態放入程式產出的資料,這個設計真的讚讚。
在 routing
的部分加入 /index
並綁定相關的 request handler
這一步就相對簡單很多了,基本上就是再實作一次 HandleFunc
方法,將 /index
與 test
方法進行綁定即可
http.HandleFunc("/index", test)
最終程式如下
package mainimport (
"html/template"
"log"
"net/http"
)type IndexData struct {
Title string
Content string
}func test(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles("./index.html"))
data := new(IndexData)
data.Title = "首頁"
data.Content = "我的第一個首頁"
tmpl.Execute(w, data)
}
func main() {
http.HandleFunc("/", test)
http.HandleFunc("/index", test)
err := http.ListenAndServe(":8888", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
小結
今天主要就是透過 golang 內建的 package 建立基本的網頁,大家可以發現 golang 非常適合寫網頁,整體編譯與回應的速度都令人滿意,最重要的是不用關聯到外來的 package 這件事情就贏了!
有種越寫越上手的感覺,明天就讓我們來介紹 gin
這個 web framework 吧!