欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang中HTTP路由設(shè)計的使用與實現(xiàn)

 更新時間:2023年05月10日 09:35:22   作者:未來誰可知  
這篇文章主要介紹了Golang中HTTP路由設(shè)計的使用與實現(xiàn),為什么要設(shè)計路由規(guī)則,因為路由規(guī)則是HTTP的請求按照一定的規(guī)則 ,匹配查找到對應(yīng)的控制器并傳遞執(zhí)行的邏輯,需要的朋友可以參考下

Golang之HTTP路由設(shè)計

為什么要設(shè)計路由規(guī)則,路由規(guī)則是HTTP的請求按照一定的規(guī)則 ,匹配查找到對應(yīng)的控制器并傳遞執(zhí)行的邏輯!

自己編寫路由的話需要注意一下一共有幾種路由!

  • 一種是支持原生的restful四種類型的訪問方法!Get,Post,Delete,Put
  • 需要支持自定義的路徑,也就是靜態(tài)路由
  • 批量通用前綴,也就是下面我們將講到的group
  • 動態(tài)路由匹配!

也就是像這樣我們在route.go去注冊

func registerRouter(core *framework.Core) {
	print(111)
	// 設(shè)置控制器
	core.Get("/foo", FooController)
	core.Get("/user/login", UserLoginController)
	subjectApi := core.Group("/subject")
	{
    // restful路由,根據(jù)請求類型區(qū)分了開,:id為動態(tài)路由
		subjectApi.Get("/list/all", SubjectListController)
		subjectApi.Post("/add", SubjectListController)
		subjectApi.Delete("/:id", SubjectListController)
		subjectApi.Put("/:id", SubjectListController)
		subjectApi.Get("/:id", SubjectListController)
	}
}

動手編寫自己的路由

在上一節(jié)中我們編寫了自己的請求處理器,對應(yīng)在里面加入我們的路由規(guī)則就好了!

framework/core.go

package framework
import (
	"net/http"
	"strings"
)
const (
	GET    = "GET"
	PUT    = "PUT"
	DELETE = "DELETE"
	POST   = "POST"
)
//map[string]map[string]ControllerHandler 前面存請求類型后面是路徑對應(yīng)執(zhí)行方法
type Core struct {
	router map[string]map[string]ControllerHandler
}
func (c Core) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	ctx:= NewContext(request, writer)
	router:=c.FindRouteByRequest(request)
	if router==nil{
		ctx.Json(404,"router not found ")
	    return
	}
	if err:=router(ctx);err!=nil{
		ctx.Json(500,"server Interval")
		return
	}
	//http.DefaultServeMux.ServeHTTP(writer, request)
}
func NewCore() *Core {
	getRouter := map[string]ControllerHandler{}
	postRouter := map[string]ControllerHandler{}
	putRouter := map[string]ControllerHandler{}
	deleteRouter := map[string]ControllerHandler{}
	core := &Core{
		router: make(map[string]map[string]ControllerHandler, 0),
	}
  // 初始化好四種類型的路由map
	core.router[GET] = getRouter
	core.router[POST] = postRouter
	core.router[PUT] = putRouter
	core.router[DELETE] = deleteRouter
	return core
}
// 注冊Get方法
func (c *Core) Get(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern)
	c.router[GET][url] = handler
}
// 注冊Post方法
func (c *Core) Post(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern) // 大小寫不敏感
	c.router[POST][url] = handler
}
// 注冊Put方法
func (c *Core) Put(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern)
	c.router[PUT][url] = handler
}
// 注冊Delete方法
func (c *Core) Delete(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern)
	c.router[DELETE][url] = handler
}
// 尋找http+靜態(tài)路由
func (c *Core) FindRouteByRequest(request *http.Request) ControllerHandler {
	uri := request.URL.Path    //請求處理器映射地址
	method := request.Method   // 請求類型
	upperMethod := strings.ToUpper(method) 
	upperURI := strings.ToUpper(uri)
  // 找到類型下的具體地址的映射地址的方法,這里還沒有實現(xiàn)動態(tài)什么的就固定有1個路徑key,但是先別急,后面我們再來動手改造
	if data, ok := c.router[upperMethod]; ok {
		if handler, ok1 := data[upperURI]; ok1 {
			return handler
		}
	}
	return nil
}

framework/group.go

給我們的注冊路由,加上分組,用group包裝,這樣對應(yīng)我們在使用group時就會對應(yīng)到不同的請求類型的方法了!并且在這一層給所有的注冊地址統(tǒng)一加上group前綴地址!

package framework
//IGroup 代表前綴分組
type IGroup interface {
	Get(string, ControllerHandler)
	Post(string, ControllerHandler)
	Delete(string, ControllerHandler)
	Put(string, ControllerHandler)
}
//
type Group struct {
	core   *Core //
	perfix string // 自身前綴
}
func (g Group) Get(s string, handler ControllerHandler) {
	url := g.perfix + s
	g.core.Get(url, handler)
}
func (g Group) Post(s string, handler ControllerHandler) {
	url := g.perfix + s
	g.core.Post(url, handler)
}
func (g Group) Delete(s string, handler ControllerHandler) {
	url := g.perfix + s
	g.core.Delete(url, handler)
}
func (g Group) Put(s string, handler ControllerHandler) {
	url := g.perfix + s
	g.core.Put(url, handler)
}
func NewGroup(core *Core, perfix string) *Group {
	return &Group{core: core, perfix: perfix}
}
func (c *Core)Group(prefix string)IGroup{
	return NewGroup(c,prefix)
}

如何實現(xiàn)動態(tài)路由

首先先定義好我們的動態(tài)路由數(shù)據(jù)結(jié)構(gòu)

// 實現(xiàn)動態(tài)路由匹配樹
type Tree struct {
	root *node // 根結(jié)點
}
// 代表節(jié)點
type node struct {
	isLast  bool              // 代表這個節(jié)點是否可以成為最終的路由規(guī)則。 該節(jié)點是否能成為一
	segment string            // url 中的字符串,代表這個節(jié)點表示的路由中某個段的字符串
	handler ControllerHandler // 代表這個節(jié)點中包含的控制器,用于最終加載調(diào)用
	childes []*node           // 代表這個節(jié)點下的子節(jié)點
}

我們要做的就是在每次注冊的時候去將對應(yīng)的路徑的東西將之前的map[string]map[string]ControllerHandler替換為新改造的這個Tree!

從node的結(jié)構(gòu)來看我們應(yīng)該判斷我們的segment去添加我們的childes的node在最后的節(jié)點的時候賦值一下處理方法

//matchNode 方法的參數(shù)是一個 URI,返回值是指向 node 的指針,它的實現(xiàn)思路是使用函數(shù)遞歸
// 判斷是否動態(tài)路由
func isWildSegment(segment string) bool {
	return strings.HasPrefix(segment, ":")
}

下面是我們需要的一些功能函數(shù),遞歸匹配路由和找到下一層的子節(jié)點

//過濾下一層滿足 segment 規(guī)則的子節(jié)點
func (n *node) filterChildNodes(segment string) []*node {
   if len(n.childes) == 0 {
      return nil
   }
   // 如果是動態(tài)路由則子節(jié)點直接滿足條件
   if isWildSegment(segment) {
      return n.childes
   }
   // 不是的話就從子節(jié)點里面找2
   nodes := make([]*node, 0, len(n.childes))
   for _, node := range n.childes {
      // 判斷所有子節(jié)點里面是否有動態(tài)路由或者唯一匹配的路由
      if isWildSegment(node.segment) || node.segment == segment {
         nodes = append(nodes, node)
      }
   }
   return nodes
}
// 匹配路由
func (n *node) matchNode(url string) *node {
   // 正序拆分路由第一個/
   segments := strings.SplitN(url, "/", 2)
   segment := segments[0] // 第一個路由節(jié)點
   //判斷如果不是動態(tài)路由,那么都統(tǒng)一大寫
   if !isWildSegment(segment) {
      segment = strings.ToUpper(segment)
   }
   // 找到下一層路由節(jié)點
   nodes := n.filterChildNodes(segment)
   // 錯誤返回
   if nodes == nil || len(nodes) <= 0 {
      return nil
   }
   //如果只有一個子節(jié)點了,是最后的話就返回最后的一個路由節(jié)點
   if len(segments) == 1 {
      for _, node := range nodes {
         if node.isLast {
            return node
         }
      }
      return nil
   }
   // 否則持續(xù)循環(huán)去判斷各個節(jié)點集合中的遞歸下一層
   for _, v := range nodes {
      toMatch := v.matchNode(segments[1])
      if toMatch != nil {
         return toMatch
      }
      return nil
   }
   return nil
}

下面是增加路由,以及提供給外部用的,找到對應(yīng)執(zhí)行邏輯的控制器方法!

// 增加路由
func (tree *Tree) AddRoute(url string, handler ControllerHandler) error {
	n := tree.root
	// 確認(rèn)路由是否已存在
	if n.matchNode(url) != nil {
		return errors.New(fmt.Sprintf("add router %v error", url))
	}
	segments := strings.Split(url, "/")
	// 對每個segment
	for index, segment := range segments {
		// 不是動態(tài)路由的靜態(tài)節(jié)點 需要轉(zhuǎn)變大寫
		if !isWildSegment(segment) {
			segment = strings.ToUpper(segment)
		}
		isLast := index == len(segments)-1 // 判斷是否為最后一個節(jié)點
		var objNode *node
		childNodes := n.filterChildNodes(segment)
		if len(childNodes) > 0 {
			// 如果有segment相同的子節(jié)點,則選擇這個子節(jié)點
			for _, node := range childNodes {
				if node.segment == segment {
					objNode = node
					break
				}
			}
		}
		// 如果沒有找到相同的子節(jié)點,那么就自己構(gòu)造一個添加進tree里面
		if objNode == nil {
			objNode = &node{
				isLast:  isLast,
				segment: segment,
				handler: nil,
				childes: make([]*node, 0),
			}
			if isLast {
				objNode.handler = handler
			}
			n.childes = append(n.childes, objNode)
		}
		n = objNode
	}
	return nil
}
// 尋找對應(yīng)的映射控制器處理方法
func (tree *Tree) FindHandler(url string) ControllerHandler {
	// 直接復(fù)用
	matchNode := tree.root.matchNode(url)
	if matchNode == nil {
		return nil
	}
	return matchNode.handler
}

改造一下core.go

將實現(xiàn)了動態(tài)路由的Tree替換進來

package framework
import (
	"log"
	"net/http"
	"strings"
)
const (
	GET    = "GET"
	PUT    = "PUT"
	DELETE = "DELETE"
	POST   = "POST"
)
type Core struct {
	router map[string]*Tree
}
func (c Core) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	ctx := NewContext(request, writer)
	router := c.FindRouteByRequest(request)
	if router == nil {
		ctx.Json(404, "router not found ")
		return
	}
	if err := router(ctx); err != nil {
		ctx.Json(500, "server Interval")
		return
	}
	//http.DefaultServeMux.ServeHTTP(writer, request)
}
func NewCore() *Core {
	getRouter := NewTree()
	postRouter := NewTree()
	putRouter := NewTree()
	deleteRouter := NewTree()
	core := &Core{
		router: make(map[string]*Tree, 0),
	}
	core.router[GET] = getRouter
	core.router[POST] = postRouter
	core.router[PUT] = putRouter
	core.router[DELETE] = deleteRouter
	return core
}
// 注冊Get方法
func (c *Core) Get(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern)
	if err := c.router[GET].AddRoute(url, handler); err != nil {
		log.Fatal("add router error:", err)
	}
}
// 注冊Post方法
func (c *Core) Post(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern) // 大小寫不敏感
	if err := c.router[POST].AddRoute(url, handler); err != nil {
		log.Fatal("add router error:", err)
	}
}
func (c *Core) Put(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern)
	if err := c.router[PUT].AddRoute(url, handler); err != nil {
		log.Fatal("add router error:", err)
	}
}
func (c *Core) Delete(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern)
	if err := c.router[DELETE].AddRoute(url, handler); err != nil {
		log.Fatal("add router error:", err)
	}
}
// 尋找http+靜態(tài)路由
func (c *Core) FindRouteByRequest(request *http.Request) ControllerHandler {
	uri := request.URL.Path
	method := request.Method
	upperMethod := strings.ToUpper(method)
	// upperURI := strings.ToUpper(uri)  內(nèi)部路由會去判斷非動態(tài)會轉(zhuǎn)大寫
	if data, ok := c.router[upperMethod]; ok {
		return data.FindHandler(uri)
	}
	return nil
}

驗證

編寫兩個Controller

func UserLoginController(ctx *framework.Context) error {
	ctx.Json(200, "ok,UserLoginController")
	return nil
}
func SubjectListController(ctx *framework.Context) error {
	ctx.Json(200, "ok,SubjectListController")
	return nil
}

啟動運行

到此這篇關(guān)于Golang中HTTP路由設(shè)計的使用與實現(xiàn)的文章就介紹到這了,更多相關(guān)Golang HTTP路由設(shè)計內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言錯誤處理異常捕獲+異常拋出

    Go語言錯誤處理異常捕獲+異常拋出

    這篇文章主要介紹了Go語言錯誤處理異常捕獲和異常拋出,Go語言的作者認(rèn)為java等語言的錯誤處理底層實現(xiàn)較為復(fù)雜,就實現(xiàn)了函數(shù)可以返回錯誤類型以及簡單的異常捕獲,雖然簡單但是也非常精妙,大大的提高了運行效率,下文需要的朋友可以參考一下
    2022-02-02
  • GO語言(golang)基礎(chǔ)知識

    GO語言(golang)基礎(chǔ)知識

    這篇文章主要介紹了GO語言(golang)基礎(chǔ)知識,需要的朋友可以參考下
    2015-01-01
  • Go語言單元測試基礎(chǔ)從入門到放棄

    Go語言單元測試基礎(chǔ)從入門到放棄

    這篇文章主要介紹了Go單元測試基礎(chǔ)從入門到放棄為大家開啟Go語言單元測試第一篇章,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • Go秒爬博客園100頁新聞

    Go秒爬博客園100頁新聞

    利用go語言的協(xié)程并發(fā)優(yōu)勢爬取網(wǎng)頁速度相當(dāng)之快,博客園100頁新聞標(biāo)題只需一秒即可全部爬取,跟著小編一起去看看如何實現(xiàn)的,希望大家可以從中受益
    2018-09-09
  • Goland的設(shè)置與配置全過程

    Goland的設(shè)置與配置全過程

    這篇文章主要介紹了Goland的設(shè)置與配置全過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 淺析Go中函數(shù)的健壯性,panic異常處理和defer機制

    淺析Go中函數(shù)的健壯性,panic異常處理和defer機制

    這篇文章主要為大家詳細(xì)介紹了Go中函數(shù)的健壯性,panic異常處理和defer機制的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2023-10-10
  • Go整合captcha實現(xiàn)驗證碼功能

    Go整合captcha實現(xiàn)驗證碼功能

    最近在使用Go語言搞一個用戶登錄&注冊的功能,我們油然會產(chǎn)生一種增加驗證碼的想法。后來在GitHub上找到了這個名叫captcha的插件,于是就利用文檔進行了初步的學(xué)習(xí),并融入到自己的項目中,整個過程下來感覺這個插件的設(shè)計非常巧妙
    2023-03-03
  • golang實現(xiàn)簡單的tcp數(shù)據(jù)傳輸

    golang實現(xiàn)簡單的tcp數(shù)據(jù)傳輸

    這篇文章主要為大家介紹了golang實現(xiàn)簡單的tcp數(shù)據(jù)傳輸,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • go?tar包歸檔文件處理操作全面指南

    go?tar包歸檔文件處理操作全面指南

    這篇文章主要為大家介紹了使用go?tar包歸檔文件處理操作全面指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • Golang 實現(xiàn) Redis系列(六)如何實現(xiàn) pipeline 模式的 redis 客戶端

    Golang 實現(xiàn) Redis系列(六)如何實現(xiàn) pipeline 模式的 redis 客戶端

    pipeline 模式的 redis 客戶端需要有兩個后臺協(xié)程負(fù)責(zé) tcp 通信,調(diào)用方通過 channel 向后臺協(xié)程發(fā)送指令,并阻塞等待直到收到響應(yīng),本文是使用 golang 實現(xiàn) redis 系列的第六篇, 將介紹如何實現(xiàn)一個 Pipeline 模式的 Redis 客戶端。
    2021-07-07

最新評論