设为首页 收藏本站
查看: 4477|回复: 0

[经验分享] golang自定义路由控制实现(一)

[复制链接]

尚未签到

发表于 2018-9-20 08:10:32 | 显示全部楼层 |阅读模式
      由于本人之前一直是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框架中路由分发是基础,在该此处上需要建立更多的功能,比如说过滤器等。在初期设计的时候必须保证要有可扩展性,所以笔者认为难点在于此。下面直接上代码,对应的代码有充分的注释。
  

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})
  
}
  

  

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"的简析及自制简单路由框架



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-598353-1-1.html 上篇帖子: 关于golang中包(package)的二三事儿 下篇帖子: Golang 效率初(粗)测
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表