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

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

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

Golang之HTTP路由設(shè)計(jì)

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

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

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

也就是像這樣我們?cè)?code>route.go去注冊(cè)

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

動(dòng)手編寫自己的路由

在上一節(jié)中我們編寫了自己的請(qǐng)求處理器,對(duì)應(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 前面存請(qǐng)求類型后面是路徑對(duì)應(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
}
// 注冊(cè)Get方法
func (c *Core) Get(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern)
	c.router[GET][url] = handler
}
// 注冊(cè)Post方法
func (c *Core) Post(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern) // 大小寫不敏感
	c.router[POST][url] = handler
}
// 注冊(cè)Put方法
func (c *Core) Put(pattern string, handler ControllerHandler) {
	url := strings.ToUpper(pattern)
	c.router[PUT][url] = handler
}
// 注冊(cè)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    //請(qǐng)求處理器映射地址
	method := request.Method   // 請(qǐng)求類型
	upperMethod := strings.ToUpper(method) 
	upperURI := strings.ToUpper(uri)
  // 找到類型下的具體地址的映射地址的方法,這里還沒有實(shí)現(xiàn)動(dòng)態(tài)什么的就固定有1個(gè)路徑key,但是先別急,后面我們?cè)賮韯?dòng)手改造
	if data, ok := c.router[upperMethod]; ok {
		if handler, ok1 := data[upperURI]; ok1 {
			return handler
		}
	}
	return nil
}

framework/group.go

給我們的注冊(cè)路由,加上分組,用group包裝,這樣對(duì)應(yīng)我們?cè)谑褂胓roup時(shí)就會(huì)對(duì)應(yīng)到不同的請(qǐng)求類型的方法了!并且在這一層給所有的注冊(cè)地址統(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)
}

如何實(shí)現(xiàn)動(dòng)態(tài)路由

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

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

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

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

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

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

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

下面是增加路由,以及提供給外部用的,找到對(duì)應(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, "/")
	// 對(duì)每個(gè)segment
	for index, segment := range segments {
		// 不是動(dòng)態(tài)路由的靜態(tài)節(jié)點(diǎn) 需要轉(zhuǎn)變大寫
		if !isWildSegment(segment) {
			segment = strings.ToUpper(segment)
		}
		isLast := index == len(segments)-1 // 判斷是否為最后一個(gè)節(jié)點(diǎn)
		var objNode *node
		childNodes := n.filterChildNodes(segment)
		if len(childNodes) > 0 {
			// 如果有segment相同的子節(jié)點(diǎn),則選擇這個(gè)子節(jié)點(diǎn)
			for _, node := range childNodes {
				if node.segment == segment {
					objNode = node
					break
				}
			}
		}
		// 如果沒有找到相同的子節(jié)點(diǎn),那么就自己構(gòu)造一個(gè)添加進(jìn)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
}
// 尋找對(duì)應(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

將實(shí)現(xiàn)了動(dòng)態(tài)路由的Tree替換進(jìn)來

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
}
// 注冊(cè)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)
	}
}
// 注冊(cè)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)部路由會(huì)去判斷非動(dòng)態(tài)會(huì)轉(zhuǎn)大寫
	if data, ok := c.router[upperMethod]; ok {
		return data.FindHandler(uri)
	}
	return nil
}

驗(yàn)證

編寫兩個(gè)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
}

啟動(dòng)運(yùn)行

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

相關(guān)文章

  • Go語(yǔ)言錯(cuò)誤處理異常捕獲+異常拋出

    Go語(yǔ)言錯(cuò)誤處理異常捕獲+異常拋出

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

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

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

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

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

    Go秒爬博客園100頁(yè)新聞

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

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

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論