资讯专栏INFORMATION COLUMN

golang自定义路由控制实现(一)

番茄西红柿 / 2029人阅读

摘要:中的设计非常轻量,又兼具很高的扩展性,初学者都可以轻易的设计出自定义的路由功能,使用上十分简单这里来吐槽一下的,虽然我也对爱得深沉,下面请看的。一般网站的路由规则太多了,编写繁琐,可以通过的方法进行一种简化。

    由于本人之前一直是Java Coder,在Java web开发中其实大家都很依赖框架,所以当在学习Golang的时候,自己便想着在Go开发中脱离框架,自己动手造框架来练习。通过学习借鉴Java的思想还有部分框架的源码,在golang上面进行实现,从而达到对Java和Golang的同时学习目的,这就很美滋滋了。     Golang中http的设计非常轻量,又兼具很高的扩展性,初学者都可以轻易的设计出自定义的路由功能,使用上十分简单(这里……来吐槽一下Java的Servlet,虽然我也对Java爱得深沉),下面请看Go的Demo。

func HelloServer1(w http.ResponseWriter, req *http.Request) {

	fmt.Fprint(w,"hello world")
}
func main() {
	http.HandleFunc("/test", HelloServer1)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err.Error())
	}
}

    短短的几行代码便可以成功注册一个接口并跑起服务。但是原生的开发方式提供的功能是比较精简的目前几乎所有的Web应用路由实现都是基于http默认的路由器,但是Go自带的路由器有几个限制:

不支持参数设定,例如/user/:uid 这种泛类型匹配。

无法很好的支持REST模式,无法限制访问的方法,例如上面的例子中,用户访问/foo,可以用GET、POST、DELETE、HEAD等方式访问。

一般网站的路由规则太多了,编写繁琐,可以通过struct的方法进行一种简化。     Go有如此限制跟http提供的默认方式有关,我们先看下http两个关键的struct

type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
    explicit bool
    h        Handler
    pattern  string
}

    我们需要重点关键两个地方,一个是ServeMux 中的参数m,它的类型是 map[string]muxEntry ,这里我们自然而然可以想到,参数m负责路由分发。第二个重点则是muxEntry,muxEntry的h Handler 对应的就是我们编写的接口,而围绕这个接口,http并没有其他过多的功能,甚至连像Java中制定一套统一web开发标准都没有。因此http中只是提供最基础的功能,用户则需要以这些功能为基础,进而YY出自己想要的框架或者更丰富的功能。     首先我们问题,能够快速简单的设置Http Method,以方便日后支持RESTFUL的URL规范。有两种简单的做法,第一种做法是使用二维Map ,即map[string]map[string]http.HandlerFunc,其中一维的键String表示请求method比如post, get 等。二维的键string表示要匹配的URL地址, http.HandlerFunc当然就是处理URL请求的具体方法。第二种做法即是笔者采用的做法,其实是第一种做法演变而来的,HTTP 中Method的种类是固定的,其实我们完全可以用一个数组,而值为map[string]http.HandlerFunc来实现。

const (
	GET         = iota
	POST
	PUT
	DELETE
	CONNECTIBNG
	HEAD
	OPTIONS
	PATCH
	TRACE
)

    看完上面常量的设置,想必读者已经知道了我的意思,e.g:array[0]表示GET方法下所有的接口的集合,array[1]表示POST方法下所有的接口的集合基本原理其实也简单,把Get方法下的所有的接口都存储到array[0]的值中,以此推理其他方法。原理简单,但是一个框架的设计必须高内聚低耦合,一个Web框架中路由分发是基础,在该此处上需要建立更多的功能,比如说过滤器等。在初期设计的时候必须保证要有可扩展性,所以笔者认为难点在于此。下面直接上代码,对应的代码有充分的注释。

/**
odserver.go
*/
package odserver

import (
	"net/http"
)
//实现IOdServer的接口,以及http提供ServeHttp方法
type OdServer struct {
	router MethodMaps
}


type IOdServer interface {
	GET(url string, f HandlerFunc)
	POST(url string, f HandlerFunc)
	PUT(url string, f HandlerFunc)
	DELETE(url string, f HandlerFunc)
}

type HandlerMapped struct {
	f HandlerFunc
}
//接口函数单位,即我们编写代码逻辑的函数
type HandlerFunc func(w http.ResponseWriter, req *http.Request)

func Default() *OdServer {
	return &OdServer{
		router:NewRouter(),
	}
}

//实现Handler接口,匹配方法以及路径
func (o *OdServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	//转发给doHandler进行执行
	o.doHandler(w,req)
}
//判断需要执行的Http Method,从而查找对应的接口并且执行
func (o *OdServer) doHandler(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case http.MethodGet:
		{
			if hm, ok := o.router.GetMapping(req.URL.RequestURI()); ok {
				hm.f(w, req)
			}
		}
	case http.MethodPost:
		{
			if hm, ok := o.router.PostMapping(req.URL.RequestURI()); ok {
				hm.f(w, req)
			}

		}
	case http.MethodDelete:
		{
			if hm, ok := o.router.DeleteMapping(req.URL.String()); ok {
				hm.f(w, req)
			}
		}
	case http.MethodPut:
		{
			if hm, ok := o.router.PutMapping(req.URL.String()); ok {
				hm.f(w, req)
			}
		}
	default:
		{

		}
	}
}

func (o *OdServer) GET(url string, f HandlerFunc) {
	o.router.GetAdd(url, HandlerMapped{f: f})
}
func (o *OdServer) POST(url string, f HandlerFunc) {
	o.router.PostAdd(url, HandlerMapped{f: f})
}
func (o *OdServer) PUT(url string, f HandlerFunc) {
	o.router.PutAdd(url, HandlerMapped{f: f})
}
func (o *OdServer) DELETE(url string, f HandlerFunc) {
	o.router.DeleteAdd(url, HandlerMapped{f: f})
}

/**
router.go
*/
package odserver

/**
提供基本的路由功能,添加路由,查找路由
 */
const (
	GET         = iota
	POST
	PUT
	DELETE
	CONNECTIBNG
	HEAD
	OPTIONS
	PATCH
	TRACE
)

func NewRouter() MethodMaps {
	return []handler{
		GET:    make(handler),
		POST:   make(handler),
		PUT:    make(handler),
		DELETE: make(handler),
	}
}

type MethodMaps [] handler
type handler map[string]HandlerMapped
//映射路由,获取Get方法下对应的接口
func (m MethodMaps) GetMapping(url string) (HandlerMapped, bool) {
	if hm, ok := m[GET][url]; ok {
		return hm, true
	}
	return HandlerMapped{}, false
}
//映射路由,获取Post方法下对应的接口
func (m MethodMaps) PostMapping(url string) (HandlerMapped, bool) {
	if hm, ok := m[POST][url]; ok {
		return hm, true
	}
	return HandlerMapped{}, false
}
//映射路由,获取Delete方法下对应的接口
func (m MethodMaps) DeleteMapping(url string) (HandlerMapped, bool) {
	if hm, ok := m[DELETE][url]; ok {
		return hm, true
	}
	return HandlerMapped{}, false
}
//映射路由,获取Put方法下对应的接口
func (m MethodMaps) PutMapping(url string) (HandlerMapped, bool) {
	if hm, ok := m[PUT][url]; ok {
		return hm, true
	}
	return HandlerMapped{}, false
}
//增加Get方法下的接口
func (m MethodMaps) GetAdd(url string, mapped HandlerMapped) {
	if _, ok := m.GetMapping(url); ok {
		panic("duplicate url with get method")
	}
	m[GET].SetUrl(url,mapped)
}
//增加Post方法下的接口
func (m MethodMaps) PostAdd(url string, mapped HandlerMapped) {
	if _, ok := m.GetMapping(url); ok {
		panic("duplicate url with Post method")
	}
	m[POST].SetUrl(url,mapped)

}
//增加Put方法下的接口
func (m MethodMaps) PutAdd(url string, mapped HandlerMapped) {
	if _, ok := m.GetMapping(url); ok {
		panic("duplicate url with Put method")
	}
	m[PUT].SetUrl(url,mapped)

}
//增加Delete方法下的接口
func (m MethodMaps) DeleteAdd(url string, mapped HandlerMapped) {
	if _, ok := m.GetMapping(url); ok {
		panic("duplicate url with Delete method")
	}
	m[DELETE].SetUrl(url,mapped)
}
func (h handler) SetUrl(url string, mapped HandlerMapped) {
	h[url] = mapped
}

    如我所说,我觉得学习Golang比较有意思的是,可以将从Java里学到的东西,转之在Golang里尝试实现,不仅学习了Golang,还使得自己对Java的认识进一步提升。如果读者有更好的方法,不吝赐教 参考资料:# Golang学习笔记 - 标准库"net/http"的简析及自制简单路由框架

​ 作者:plz叫我红领巾

​ 出处:juejin.im/post/5ce244…

本博客欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7172.html

相关文章

  • golang定义路由控制实现

    摘要:中的设计非常轻量,又兼具很高的扩展性,初学者都可以轻易的设计出自定义的路由功能,使用上十分简单这里来吐槽一下的,虽然我也对爱得深沉,下面请看的。一般网站的路由规则太多了,编写繁琐,可以通过的方法进行一种简化。    由于本人之前一直是Java Coder,在Java web开发中其实大家都很依赖框架,所以当在学习Golang的时候,自己便想着在Go开发中脱离框架,自己动手造框架来练习。通过学...

    darkerXi 评论0 收藏0
  • 华尔街见闻基于istio的服务网格实践

    摘要:,托管于腾讯云容器平台容器编排工具。适配我们目前的服务部署在腾讯云托管,节点使用核的网络增强型机器,所有的后端服务都以部署,集群外部署高可用支持集群内服务发现,数据库以为主,消息队列采用。 距离2017年的见闻技术架构调整接近2年,随着业务线的发展,见闻技术部的项目数量、项目架构类型、基础设施规模、服务变更频率都在不断地增长,带给SRE的挑战是如何能更快地助力于开发人员更快更稳定地部署...

    stonezhu 评论0 收藏0
  • kubernetes容器网络接口(CNI) midonet网络插件的设计与实现

    摘要:相关原理概述先来讲讲什么是容器网络接口是一种操作容器网络规范,包含方法规范,参数规范等。只关心容器的网络连接,在容器创建时分配网络资源,并在删除容器时删除分配的资源。容器创建成功后具有一个网络空间,此时调用插件方法进行网络设置。 相关原理概述 先来讲讲什么是CNI? CNI(容器网络接口)是一种操作容器网络规范,包含方法规范,参数规范等。CNI只关心容器的网络连接,在容器创建时分配网络...

    OldPanda 评论0 收藏0
  • Rainbond设计分享系列(1)基于Midonet的多租户网络设计

    摘要:今天跟大家分享基于的多租户网络设计和思考。对多租户支持的实现基础是对多租户的网络支持,公有云要求每个租户之间网络必须隔离,形成相互安全的租户网络环境。 今天跟大家分享Rainbond基于Midonet的多租户网络设计和思考。 Rainbond对多租户支持的实现基础是对多租户的网络支持,Rainbond公有云要求每个租户之间网络必须隔离,形成相互安全的租户网络环境。对于不同的SDN网络,...

    QLQ 评论0 收藏0
  • 【Zinx第二章-初识Zinx框架】Golang轻量级并发服务器框架

    摘要:主要是存放一些框架的全部模块的抽象层接口类,框架的最基本的是服务类接口,定义在模块中。使用完成应用程序实际上,目前框架的对外接口并未改变,所以的测试依然有效。 【Zinx教程目录】Zinx源代码https://github.com/aceld/zinx (请拷贝网址,在浏览器打开[简书不让自动跳转])完整教程电子版(在线高清)-下载Zinx框架视频教程(框架篇)(完整版下载)链接在下面...

    rottengeek 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<