[Golang] 網頁程式設計:設置路由 (Route)

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    前言

    網頁程式基本的行為是對請求 (request) 進行相對應的回應 (response)。對不同的路徑有不同的行為。本文說明在 Golang 網頁程式中設置路由 (route) 的方法。

    再訪 Hello World

    我們另寫了一個和先前略有不同的 Hello World 程式,請讀者注意一下相異處:

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func main() {
    	// Create a new HTTP request multiplexer
    	mux := http.NewServeMux()
    
    	// Set routes to their handlers
    	mux.HandleFunc("/", handler)
    
    	// By creating a new http.Server object,
    	// we may tune its behaviors
    	server := http.Server{
    		Addr:    "0.0.0.0:8080",
    		Handler: mux,
    	}
    
    	// Run the server.
    	server.ListenAndServe()
    }
    
    func handler(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "Hello World")
    }
    

    我們在前文提過,mux 的用途是統整路徑和其相對應的處理器,將請求的路徑導向特定的處理器。我們先前直接用內建的 mux,在這個寫法中,將 mux 這個路由物件獨立出來。目前我們暫時先用內建的路由物件,但這個架構保留了日後使用其他相容的第三方路由物件的彈性。

    另外,我們建立 server (伺服器) 物件,在建立物件時,我們可以藉由配置不同的參數來微調 server 物件的行為,是這個寫法帶來的好處。

    使用動態路由

    到目前為止,我們的範例程式都使用內建的 mux 物件;不過,Go 的 net/http 套件保留了擴充的彈性,我們可以用第三方的路由物件來取代內建的路由物件。像 httprouter 就是一個常見的路由套件。以下簵例用 httprouter 物件取代內建的 mux 物件:

    package main
    
    import (
        "fmt"
        "github.com/julienschmidt/httprouter"
        "net/http"
    )
    
    func main() {
        // Create a new HTTP request multiplexer
        mux := httprouter.New()
    
        // Set routes to their handlers
        mux.GET("/hello/:name", hello)
    
        // Create a HTTP server
        server := http.Server{
            Addr: "127.0.0.1:8080",
            Handler: mux,
        }
    
        // Run the server.
        server.ListenAndServe()
    }
    
    func hello(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
        fmt.Fprintf(w, "Hello, %s!", p.ByName("name"))
    }
    

    在這個範例中,這一段是新的語法:

    mux.GET("/hello/:name", hello)
    

    這個語法有兩個意義,一個告訴路由物件我們的 HTTP 動作 (action)GET,另一個則是在路徑中加入變數 :name,而內建的路由物件無法加入變數。

    另外,我們的路徑處理器 (handler) 的參數也和先前不同:

    func hello(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
        fmt.Fprintf(w, "Hello, %s!", p.ByName("name"))
    }
    

    除了原本的兩個參數,這個處理器另外加入 httprouter.Params 這個新的參數,這個參數用來處理路徑的變數。

    使用 HTTP 動作 (Actions)

    原先的 HTTP 協定只有 GET 一種 HTTP 動作,後來則新增數種 HTTP 動作,以符合多樣化的需求,常見的動作有以下數種:

    • GET:取得資源,瀏覽器預設的動作
    • POST:建立資源
    • PUT:修改資源
    • DELETE:刪除資源

    除了這四種動作,還有一些較少用的動作,此處不一一列出,有興趣的讀者可以參考這裡。在網頁程式中時常利用 HTTP 動作搭配路徑來封裝資料庫的 CRUD (增 查 改 刪) 行為。

    在網頁程式中處理錯誤

    在先前的程式中,我們都假設程式沒有錯誤,但我們不能一廂情願地認定使用者會按照我們設計的方式使用程式,而要針對可能的錯誤情境去撰寫相對應的程式碼。

    網頁程式透過 HTTP 狀態碼 (status code) 回傳錯誤訊息,但使用者不會接觸到這個代碼,我們需另外撰寫錯誤頁面來告知使用者。

    如果我們不寫錯誤頁面來告知使用者,網頁伺服器 (如 Apache 或 Nginx) 或網頁函式庫 (或框架) 本身可能會內建通知頁面;然而,這些通知頁面會無意間暴露過多的技術細節,成為駭客攻擊的依據。因此,真正上線的網頁程式應該要提供自己的錯誤頁面。

    一般來說,常見的錯誤情境有以下數種:

    • Not Found (HTTP 404):該頁面不存在
    • Forbidden (HTTP 403):使用者沒有權限存取特定內容
    • Internal Server Error (HTTP 500):網頁程式內部發生錯誤

    建議至少針對這三種錯誤情境去做相對應的頁面。

    在本範例中,我們加入 HTTP 404 和 HTTP 500 的頁面:

    package main
    
    import (
            "fmt"
            "github.com/julienschmidt/httprouter"
            "net/http"
    )
    
    func main() {
            // Set a new HTTP request multiplexer
            mux := httprouter.New()
    
            // Listen to root path
            mux.GET("/", index)
    
            // Custom 404 page
            mux.NotFound = http.HandlerFunc(notFound)
    
            // Custom 500 page
            mux.PanicHandler = errorHandler
    
            // Set the parameters for a HTTP server
            server := http.Server{
                    Addr:    "0.0.0.0:8080",
                    Handler: mux,
            }
    
            // Run the server.
            server.ListenAndServe()
    }
    
    func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    	fmt.Fprintln(w, "Hello World")
    }
    
    func notFound(w http.ResponseWriter, r *http.Request) {
    	w.WriteHeader(http.StatusNotFound)
    	fmt.Fprintln(w, "Page Not Found")
    }
    
    func errorHandler(w http.ResponseWriter, r *http.Request, p interface{}) {
    	w.WriteHeader(http.StatusInternalServerError)
    	fmt.Fprintln(w, "Internal Server Error")
    }
    

    這個程式的關鍵在於指定 mux.NotFoundmux.PanicHandler 兩項事件的處理器 (handler),在網頁程式發生錯誤時,就會連到我們預先撰寫的特定頁面,而不會連到網頁伺服器內建的頁面。

    在預設情形下,路徑處理器皆回傳 HTTP 200 的狀態碼。如果要更改回傳的 HTTP 狀態碼,就必需要在程式碼中明確設置。在本範例中,我們使用 WriteHeader() 函式寫入 HTTP 狀態碼。

    在 Golang 中,有許多代表狀態碼的常數 可用,不用去記憶 HTTP 狀態碼的數字。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk