前言
在本文中,我們透過極簡的 Hello World 程式來熟悉如何用 Go 撰寫網頁程式。根據網頁是否要加密,程式的寫法略有不同,讀者可相互比較一下。
一般版的 Hello World
所謂的一般版是指透過未加密的 HTTP 協定傳輸的網頁程式。透過未加密的 HTTP 協定來傳輸頁面和資料時,不需要做額外的配置,過程較簡單;日後網站要上線時,再將其轉為有加密的 HTTPS 協定即可。
範例程式碼如下:
package main
import (
"fmt"
"net/http"
)
func main() {
// Listen to the root path of the web app
http.HandleFunc("/", handler)
// Start a web server.
http.ListenAndServe(":8080", nil)
}
// The handler for the root path.
func handler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Hello, World")
}
在 Go 語言中,撰寫網頁後端程式的主要套件是 net/http。我們會陸續撰寫一些範例,建議讀者搭配該套件的 API 文件來學習,不要直接死記本系列文章中範例的寫法。
http.HandleFunc 的目的是登記網站路徑的處理器,其函式原型如下:
http.HandleFunc(path, handler)
以本例 http.HandleFunc("/", handler)
來說,我們在根目錄 /
中登記 handler
這個處理器,網站使用者在向該路徑發出請求 (request) 時,我們的程式即會使用 handler
所實做的內容來回應 (reponse)。在語法上來說,handler
是一個函式,我們這個程式把 handler
當成參數傳入 http
物件中,用到函數式程式的寫法。
http.ListenAndServe 會啟動一個網頁伺服程式,其函式原型如下:
http.ListenAndServe(addr, handler)
addr
是用來表示網址的字串,handler
則傳入 ServeMux
。所謂的 ServeMux
是 HTTP request multiplexer (mux),mux 統籌處理該網頁程式有登記的路徑及其處理器 (handler),將傳入的路徑指向相對應的處理器,也就是整個網頁程式的路由 (router)。在本例中我們傳入 nil
,代表使用內建的 DefaultServeMux
。
註:本文的處理器 (handler) 是指處理特定的 HTTP 請求 (request) 的程式碼區塊 (函式或物件),不要和電腦的中央處理器 (CPU) 搞混。
處理器 (handler) 的寫法如下:
func handler(w http.ResponseWriter, req *http.Request) {
// Write something here.
}
處理器以 *http.Request
和 http.ResponseWriter
為參數,該參數分別代表網頁的請求 (request) 和回應 (response)。以本例來說,我們忽略請求的部分,直接把 "Hello World"
字串輸出到回應。
使用 Go 主程式執行該程式即可:
$ go run main.go
(選擇性) 加密版的 Hello World
在先前的版本中,我們的程式是走未加密的 HTTP 協定,一般情形下是夠用了;必要時,我們仍然可走有加密的 HTTPS 協定。本節說明如何建立走 HTTPS 的 Hello World 程式。
我們要先安裝 OpenSSL,在類 Unix 系統上使用系統提供的套件即可,若在 Windows 上,可透過 Chocolatey 安裝該套件。利用 OpenSSL 的終端機工具輸入以下指令產生認證和金鑰:
$ openssl req -newkey rsa:4096 -nodes -sha512 -x509 -days 3650 -nodes -out cert.pem -keyout key.pem
透過此指令,會產生 cert.pem 及 key.pem,待會會用到。
撰寫主程式的部分:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", handler)
// Start a secure (but not trusted) web server.
http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", nil)
}
func handler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Hello, World")
}
可以看得出來程式碼和先前差不多,主要是啟動時的函式換成 http.ListenAndServeTLS。該函式的原型如下:
http.ListenAndServeTLS(addr, cert, key, handler)
addr
和先前一樣,填入網址。cert
和 key
分別是認證和金鑰的路徑,按照實際情形填入即可。handler
和先前一樣,是 ServeMux
,填入 nil
代表使用預設的 mux。
使用 https://localhost:8080/ 就可以連到該站台,但使用較新的瀏覽器連線到該站台時,會發現瀏覽器警告我們該站台不安全。這是因為我們的認證和金鑰沒有經過第三方機構公證。由於在早期網站只是要內部使用,直接忽視該警告訊息即可,不用一開始就去進行公證。
現在有新的工具可以產生受信任的認證和金鑰,有需要的讀者可看下一節。
(選擇性) 製作受信賴的憑證
我們上一節談的方法是開發早期需走 HTTPS 時所用的一般性方法,其實應該夠用了。不過,透過 mkcert 這套工具,我們可以不需額外的設定,直接產生受信任的認證和金鑰,簡化開發時程。
輸入以下指令以產生憑證:
$ mkcert -install
接著,輸入以下指令以產生適用於 localhost
和 127.0.0.1
的認證和金鑰:
$ mkcert localhost 127.0.0.1
Using the local CA at "C:\Users\cwchen\AppData\Local\mkcert" ✨
Created a new certificate valid for the following names 📜
- "localhost"
- "127.0.0.1"
The certificate is at "./localhost+1.pem" and the key at "./localhost+1-key.pem" ✅
在啟動伺服器時指定上述檔案即可:
// Start a secure and trusted web server.
http.ListenAndServeTLS("localhost:8080", "localhost+1.pem", "localhost+1-key.pem", nil)
連到該站台時,會發現瀏覽器不再吐警告訊息了。
要注意這個軟體憑證只適用於開發階段,不能用在真正要上線的網頁程式,會有安全上的問題。我們會在發布程式 (deployment) 的章節中,說明如何使用 Let's Encrypt 來産生可用在實際上線的環境的憑證。
結語
雖然這個網頁程式很簡單,但這是了解 Golang 網頁程式的第一步,請讀者還是要實際練習一下。
在撰寫網頁程式的初期,先用無加密的網頁程式其實無妨;但程式要上線前,最好還是改成加密的版本。現在 HTTPS 可說是網站和網頁程式的標配,不應該再把沒加密的網頁程式直接上線。