最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)文章

  • Golang中的Interface詳解

    Golang中的Interface詳解

    本文詳細(xì)講解了Golang中的Interface,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • 如何用golang運(yùn)行第一個(gè)項(xiàng)目

    如何用golang運(yùn)行第一個(gè)項(xiàng)目

    這篇文章主要介紹了如何用golang運(yùn)行第一個(gè)項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Golang中時(shí)間相關(guān)操作合集

    Golang中時(shí)間相關(guān)操作合集

    這篇文章主要為大家介紹了Golang中的各種時(shí)間相關(guān)操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • Golang為什么占用那么多的虛擬內(nèi)存原理解析

    Golang為什么占用那么多的虛擬內(nèi)存原理解析

    這篇文章主要介紹了Golang為什么占用那么多的虛擬內(nèi)存原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Redis?BloomFilter布隆過濾器原理與實(shí)現(xiàn)

    Redis?BloomFilter布隆過濾器原理與實(shí)現(xiàn)

    你在開發(fā)或者面試過程中,有沒有遇到過?海量數(shù)據(jù)需要查重,緩存穿透怎么避免等等這樣的問題呢?下面這個(gè)東西超棒,好好了解下,面試過關(guān)斬將,凸顯你的不一樣
    2022-10-10
  • Go語(yǔ)言網(wǎng)站使用異步編程和Goroutine提高Web的性能

    Go語(yǔ)言網(wǎng)站使用異步編程和Goroutine提高Web的性能

    作為一門現(xiàn)代化編程語(yǔ)言,Go語(yǔ)言提供了強(qiáng)大的異步編程能力,使得程序員可以以更高效的方式處理并發(fā)任務(wù),在Go語(yǔ)言中,使用Goroutine在單個(gè)進(jìn)程中實(shí)現(xiàn)多任務(wù)并行處理,以及如何使用協(xié)程池來進(jìn)一步提高Web服務(wù)器的處理能力,
    2024-01-01
  • Go 語(yǔ)言 JSON 標(biāo)準(zhǔn)庫(kù)的使用

    Go 語(yǔ)言 JSON 標(biāo)準(zhǔn)庫(kù)的使用

    今天通過本文給大家介紹Go 語(yǔ)言 JSON 標(biāo)準(zhǔn)庫(kù)的使用小結(jié),包括序列化和反序列化的相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧
    2021-10-10
  • Golang使用Gin框架實(shí)現(xiàn)路由分類處理請(qǐng)求流程詳解

    Golang使用Gin框架實(shí)現(xiàn)路由分類處理請(qǐng)求流程詳解

    Gin是一個(gè)golang的微框架,封裝比較優(yōu)雅,具有快速靈活,容錯(cuò)方便等特點(diǎn),這篇文章主要介紹了Golang使用Gin框架實(shí)現(xiàn)路由分類處理請(qǐng)求,感興趣的同學(xué)可以參考下文
    2023-05-05
  • Golang使用gorm實(shí)現(xiàn)分頁(yè)功能的示例代碼

    Golang使用gorm實(shí)現(xiàn)分頁(yè)功能的示例代碼

    在提供列表接口時(shí)一般要用到分頁(yè),對(duì)于存儲(chǔ)在某些數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行分頁(yè)起來非常的方便,下文給出一個(gè)通過gorm進(jìn)行分頁(yè)并通過http返回?cái)?shù)據(jù)的例子,感興趣的小伙幫跟著小編一起來看看吧
    2024-10-10
  • GoLang函數(shù)棧的使用詳細(xì)講解

    GoLang函數(shù)棧的使用詳細(xì)講解

    這篇文章主要介紹了GoLang函數(shù)棧的使用,我們的代碼會(huì)被編譯成機(jī)器指令并寫入到可執(zhí)行文件,當(dāng)程序執(zhí)行時(shí),可執(zhí)行文件被加載到內(nèi)存,這些機(jī)器指令會(huì)被存儲(chǔ)到虛擬地址空間中的代碼段,在代碼段內(nèi)部,指令是低地址向高地址堆積的
    2023-02-02

最新評(píng)論

梁山县| 大洼县| 措勤县| 弋阳县| 桂林市| 榆中县| 新田县| 江口县| 西藏| 潞西市| 通江县| 富源县| 教育| 崇仁县| 庆安县| 澎湖县| 仙居县| 库尔勒市| 安福县| 武穴市| 靖江市| 威信县| 弥勒县| 文成县| 通城县| 抚宁县| 乐业县| 神农架林区| 建宁县| 光山县| 郸城县| 界首市| 佳木斯市| 玉田县| 长白| 确山县| 黎平县| 永平县| 新余市| 托克逊县| 镇坪县|