golang創(chuàng)建屬于自己的HttpServer萧豆,統(tǒng)一的權限驗證

場景說明
  • go 中使用官方的http server方法的話,缺少統(tǒng)一的方法調(diào)用邻寿,無法對用戶的權限等進行統(tǒng)一的驗證
  • http.HandleFunc("/ws", wsHandler) 官方的這種路由方式蝎土,無法靈活的進行應用
  • php 中可以使用 __construct 對訪問的方法進行統(tǒng)一的驗證视哑,而直接使用go的官方方法并沒有類似的
創(chuàng)建簡單的 http server
func main() {
    // /ws 是url路徑
    http.HandleFunc("/ws", wsHandler)
    http.HandleFunc("/test", testHandler)
    http.ListenAndServe("0.0.0.0:8000", nil)
}

// 用戶ws連接處理方法
func wsHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello world"))
}

func testHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("testHandler"))
}

瀏覽器訪問

這時候如果這兩個方法都要調(diào)用一個公共方法進行權限驗證的話都需要在每個方法內(nèi)部進行調(diào)用
這樣在方法越來越多的情況下,會造成非常嚴重的代碼冗余和難以維護

解決方法誊涯,重寫官方的方法

創(chuàng)建結構

// 定義結構挡毅,對這個結構綁定方法就可以創(chuàng)建屬于自己的http server
type application struct {
    // 路由部分,其中保存訪問路徑與方法的對應關系
    routes map[string]func(http.ResponseWriter, *http.Request) string
    // 白名單部分暴构,在白名單中的路徑我們不去驗證用戶的相關權限
    while map[string]bool
}

定義創(chuàng)建這個結構的方法

// 創(chuàng)建application
func Create() *application {
    return &application{
        routes: make(map[string]func(http.ResponseWriter, *http.Request) string),
        while:  make(map[string]bool),
    }
}

定義創(chuàng)建路由的方法

func (app *application) Router(path string, controller func(http.ResponseWriter, *http.Request) string) {
    app.routes[path] = controller
}

定義創(chuàng)建白名單的方法

// 在這個變量中的路徑跪呈,我們不去驗證權限
func (app *application) Exclude(path string) {
    app.while[path] = true
}

定義靜態(tài)文件目錄,注意這個目錄是以當前項目路徑為根目錄

const documentRoot = "public"
func isDocumentRoot(url string) (bool, string) {
    // 定義用戶訪問 / 根目錄的話 跳轉到靜態(tài)目錄
    if url == "/" {
        return true, "/" + documentRoot + "/"
    }
    // 判斷訪問的路徑是否是靜文件地址
    return strings.Contains(url, "/"+documentRoot), ""
}

重點來了取逾,定義啟動方法 和 ServeHTTP

// 啟動服務
func (app *application) Start(bindPort string) {
    // 使用自定義 handler
    err := http.ListenAndServe(bindPort, app)
    if err != nil {
        // 服務器創(chuàng)建失敗
        panic("服務器創(chuàng)建失敗")
    }
}

// 將此方法綁定到應用程序結構
// Handler 方法結構 {  ServeHTTP(ResponseWriter, *Request) }
func (app *application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 獲取訪問的url
    path := r.URL.Path
    // 如果是靜態(tài)路徑的話庆械,返回靜態(tài)資源給客戶端
    isFile, redirectPath := isDocumentRoot(path)
    if isFile == true {
        if redirectPath != "" {
            // set the redirect address and modify the original request address
            r.URL.Path = redirectPath
        }
        http.StripPrefix("/"+documentRoot+"/", http.FileServer(http.Dir(documentRoot))).ServeHTTP(w, r)
        return
    }

    // 如果訪問路徑不在白名單里,那么久驗證權限
    if app.while[path] != true {
        // 這里去驗證一下公共的東西菌赖,比如用戶是否登錄缭乘,是否有訪問路徑的權限
        message, err := controller.PermissionCheck(w, r)
        // 驗證不通過,返回失敗消息
        if err != nil {
            w.Write([]byte(message))
            return
        }
    }
    if _, function := app.routes[path]; function {
        // 返回數(shù)據(jù)給客戶端
        w.Write([]byte(app.routes[path](w, r)))
        return
    }
    // 404 未找到用戶訪問的地址
    w.Write([]byte(NotFind(w, r)))
    return
}

main方法調(diào)用

func main() {
    app := Create()
    app.Router("/test", Test)
    app.Router("/white", WhiteTest)
    app.Exclude("/white")
    app.Start("0.0.0.0:8000")
}

func Test(w http.ResponseWriter, r *http.Request) string {
    return "我是測試"
}

func WhiteTest(w http.ResponseWriter, r *http.Request) string {
    return "我白名單方法"
}

func PermissionCheck(w http.ResponseWriter, r *http.Request) (string, error) {
    // 我是公共驗證權限的,每個不在白名單中的請求都會訪問到我
    return "不允許通過", errors.New("不允許通過")
}

調(diào)用結果展示,未加入白名單的方法被公共方法攔截琉用,而白名單方法不受影響

image.png

完整代碼

package main

import (
    "errors"
    "net/http"
    "strings"
)

// 定義結構堕绩,對這個結構綁定方法就可以創(chuàng)建屬于自己的http server
type application struct {
    // 路由部分,其中保存訪問路徑與方法的對應關系
    routes map[string]func(http.ResponseWriter, *http.Request) string
    // 白名單部分邑时,在白名單中的路徑我們不去驗證用戶的相關權限
    while map[string]bool
}

// 創(chuàng)建application
func Create() *application {
    return &application{
        routes: make(map[string]func(http.ResponseWriter, *http.Request) string),
        while:  make(map[string]bool),
    }
}

func (app *application) Router(path string, controller func(http.ResponseWriter, *http.Request) string) {
    app.routes[path] = controller
}

// 在這個變量中的路徑奴紧,我們不去驗證權限
func (app *application) Exclude(path string) {
    app.while[path] = true
}

const documentRoot = "public"

func isDocumentRoot(url string) (bool, string) {
    // 定義用戶訪問 / 根目錄的話 跳轉到靜態(tài)目錄
    if url == "/" {
        return true, "/" + documentRoot + "/"
    }
    // 判斷訪問的路徑是否是靜文件地址
    return strings.Contains(url, "/"+documentRoot), ""
}

// 啟動服務
func (app *application) Start(bindPort string) {
    // 使用自定義 handler
    err := http.ListenAndServe(bindPort, app)
    if err != nil {
        // 服務器創(chuàng)建失敗
        panic("服務器創(chuàng)建失敗")
    }
}

// 將此方法綁定到應用程序結構
// Handler 方法結構 {  ServeHTTP(ResponseWriter, *Request) }
func (app *application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 獲取訪問的url
    path := r.URL.Path
    // 如果是靜態(tài)路徑的話,返回靜態(tài)資源給客戶端
    isFile, redirectPath := isDocumentRoot(path)
    if isFile == true {
        if redirectPath != "" {
            // set the redirect address and modify the original request address
            r.URL.Path = redirectPath
        }
        http.StripPrefix("/"+documentRoot+"/", http.FileServer(http.Dir(documentRoot))).ServeHTTP(w, r)
        return
    }

    // 如果訪問路徑不在白名單里晶丘,那么久驗證權限
    if app.while[path] != true {
        // 這里去驗證一下公共的東西黍氮,比如用戶是否登錄,是否有訪問路徑的權限
        message, err := PermissionCheck(w, r)
        // 驗證不通過,返回失敗消息
        if err != nil {
            w.Write([]byte(message))
            return
        }
    }
    if _, function := app.routes[path]; function {
        // 返回數(shù)據(jù)給客戶端
        w.Write([]byte(app.routes[path](w, r)))
        return
    }
    // 404 未找到用戶訪問的地址
    w.Write([]byte(NotFind(w, r)))
    return
}

func main() {
    app := Create()
    app.Router("/test", Test)
    app.Router("/white", WhiteTest)
    app.Exclude("/white")
    app.Start("0.0.0.0:8000")
}

func Test(w http.ResponseWriter, r *http.Request) string {
    return "我是測試"
}

func WhiteTest(w http.ResponseWriter, r *http.Request) string {
    return "我白名單方法"
}

func PermissionCheck(w http.ResponseWriter, r *http.Request) (string, error) {
    // 我是公共驗證權限的浅浮,每個不在白名單中的請求都會訪問到我
    return "不允許通過", errors.New("不允許通過")
}

// 404 未找到
func NotFind(w http.ResponseWriter, r *http.Request) string {
    return "未找到訪問的內(nèi)容"
}

這時候我們就可以很輕松的對每個訪問都進行統(tǒng)一的處理不需要重復調(diào)用摸一個方法,完成如用戶的登錄校驗沫浆,權限校驗

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市滚秩,隨后出現(xiàn)的幾起案子专执,更是在濱河造成了極大的恐慌,老刑警劉巖郁油,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件本股,死亡現(xiàn)場離奇詭異,居然都是意外死亡桐腌,警方通過查閱死者的電腦和手機拄显,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來案站,“玉大人躬审,你說我怎么就攤上這事。” “怎么了盒件?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵蹬碧,是天一觀的道長。 經(jīng)常有香客問我炒刁,道長恩沽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任翔始,我火速辦了婚禮罗心,結果婚禮上,老公的妹妹穿的比我還像新娘城瞎。我一直安慰自己渤闷,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布脖镀。 她就那樣靜靜地躺著飒箭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜒灰。 梳的紋絲不亂的頭發(fā)上弦蹂,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音强窖,去河邊找鬼凸椿。 笑死,一個胖子當著我的面吹牛翅溺,可吹牛的內(nèi)容都是我干的脑漫。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼咙崎,長吁一口氣:“原來是場噩夢啊……” “哼优幸!你這毒婦竟也來了?” 一聲冷哼從身側響起叙凡,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤劈伴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后握爷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡严里,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年新啼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刹碾。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡燥撞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情物舒,我是刑警寧澤色洞,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站冠胯,受9級特大地震影響火诸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜荠察,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一置蜀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悉盆,春花似錦盯荤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至脚翘,卻和暖如春航缀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背堰怨。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工芥玉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人备图。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓灿巧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親揽涮。 傳聞我的和親對象是個殘疾皇子抠藕,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,093評論 1 32
  • 因為要結局swift3.0中引用snapKit的問題,看到一篇介紹Xcode8,swift3變化的文章,覺得很詳細...
    uniapp閱讀 4,408評論 0 12
  • 《名人傳》讀后感 大寨一中 高元節(jié) 本書作者是法國...
    元元有話說閱讀 214評論 0 0
  • 日子帶了些秋天的味道,慵懶愜意蒋困。 躺在安靜的小屋盾似,聆聽時光滴答滴答前行的步音,任由心緩緩舒展雪标,流露出靜靜的歡喜零院。立...
    一只來自霍爾沃滋的鯨魚閱讀 359評論 0 0
  • 淺水灣里沉睡著張乃瑩 呼蘭河里埋著她的祖父 當剛讀完矛盾所寫的序言 我感受到了她當時的內(nèi)心 她寫呼蘭河傳時內(nèi)心無疑...
    珞珈_3667閱讀 297評論 0 0