位元詩人 [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 狀態碼的數字。

關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。