【Go入门学习】golang自定义路由控制实现(一)
本文内容纲要:
由于本人之前一直是JavaCoder,在Javaweb开发中其实大家都很依赖框架,所以当在学习Golang的时候,自己便想着在Go开发中脱离框架,自己动手造框架来练习。通过学习借鉴Java的思想还有部分框架的源码,在golang上面进行实现,从而达到对Java和Golang的同时学习目的,这就很美滋滋了。
Golang中http的设计非常轻量,又兼具很高的扩展性,初学者都可以轻易的设计出自定义的路由功能,使用上十分简单(这里……来吐槽一下Java的Servlet,虽然我也对Java爱得深沉),下面请看Go的Demo。
funcHelloServer1(whttp.ResponseWriter,req*http.Request){
fmt.Fprint(w,"helloworld")
}
funcmain(){
http.HandleFunc("/test",HelloServer1)
err:=http.ListenAndServe(":8080",nil)
iferr!=nil{
log.Fatal("ListenAndServe:",err.Error())
}
}
短短的几行代码便可以成功注册一个接口并跑起服务。但是原生的开发方式提供的功能是比较精简的目前几乎所有的Web应用路由实现都是基于http默认的路由器,但是Go自带的路由器有几个限制:
-
不支持参数设定,例如/user/:uid这种泛类型匹配。
-
无法很好的支持REST模式,无法限制访问的方法,例如上面的例子中,用户访问/foo,可以用GET、POST、DELETE、HEAD等方式访问。
-
一般网站的路由规则太多了,编写繁琐,可以通过struct的方法进行一种简化。
Go有如此限制跟http提供的默认方式有关,我们先看下http两个关键的structtypeServeMuxstruct{ musync.RWMutex mmap[string]muxEntry hostsbool//whetheranypatternscontainhostnames }
typemuxEntrystruct{ explicitbool hHandler patternstring }
我们需要重点关键两个地方,一个是ServeMux中的参数m,它的类型是map[string]muxEntry,这里我们自然而然可以想到,参数m负责路由分发。第二个重点则是muxEntry,muxEntry的hHandler
对应的就是我们编写的接口,而围绕这个接口,http并没有其他过多的功能,甚至连像Java中制定一套统一web开发标准都没有。因此http中只是提供最基础的功能,用户则需要以这些功能为基础,进而YY出自己想要的框架或者更丰富的功能。
首先我们问题,能够快速简单的设置HttpMethod,以方便日后支持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框架中路由分发是基础,在该此处上需要建立更多的功能,比如说过滤器等。在初期设计的时候必须保证要有可扩展性,所以笔者认为难点在于此。下面直接上代码,对应的代码有充分的注释。
packageodserver
import(
"net/http"
)
//实现IOdServer的接口,以及http提供ServeHttp方法
typeOdServerstruct{
routerMethodMaps
}
typeIOdServerinterface{
GET(urlstring,fHandlerFunc)
POST(urlstring,fHandlerFunc)
PUT(urlstring,fHandlerFunc)
DELETE(urlstring,fHandlerFunc)
}
typeHandlerMappedstruct{
fHandlerFunc
}
//接口函数单位,即我们编写代码逻辑的函数
typeHandlerFuncfunc(whttp.ResponseWriter,req*http.Request)
funcDefault()*OdServer{
return&OdServer{
router:NewRouter(),
}
}
//实现Handler接口,匹配方法以及路径
func(o*OdServer)ServeHTTP(whttp.ResponseWriter,req*http.Request){
//转发给doHandler进行执行
o.doHandler(w,req)
}
//判断需要执行的HttpMethod,从而查找对应的接口并且执行
func(o*OdServer)doHandler(whttp.ResponseWriter,req*http.Request){
switchreq.Method{
casehttp.MethodGet:
{
ifhm,ok:=o.router.GetMapping(req.URL.RequestURI());ok{
hm.f(w,req)
}
}
casehttp.MethodPost:
{
ifhm,ok:=o.router.PostMapping(req.URL.RequestURI());ok{
hm.f(w,req)
}
}
casehttp.MethodDelete:
{
ifhm,ok:=o.router.DeleteMapping(req.URL.String());ok{
hm.f(w,req)
}
}
casehttp.MethodPut:
{
ifhm,ok:=o.router.PutMapping(req.URL.String());ok{
hm.f(w,req)
}
}
default:
{
}
}
}
func(o*OdServer)GET(urlstring,fHandlerFunc){
o.router.GetAdd(url,HandlerMapped{f:f})
}
func(o*OdServer)POST(urlstring,fHandlerFunc){
o.router.PostAdd(url,HandlerMapped{f:f})
}
func(o*OdServer)PUT(urlstring,fHandlerFunc){
o.router.PutAdd(url,HandlerMapped{f:f})
}
func(o*OdServer)DELETE(urlstring,fHandlerFunc){
o.router.DeleteAdd(url,HandlerMapped{f:f})
}
packageodserver
/**
提供基本的路由功能,添加路由,查找路由
*/
const(
GET=iota
POST
PUT
DELETE
CONNECTIBNG
HEAD
OPTIONS
PATCH
TRACE
)
funcNewRouter()MethodMaps{
return[]handler{
GET:make(handler),
POST:make(handler),
PUT:make(handler),
DELETE:make(handler),
}
}
typeMethodMaps[]handler
typehandlermap[string]HandlerMapped
//映射路由,获取Get方法下对应的接口
func(mMethodMaps)GetMapping(urlstring)(HandlerMapped,bool){
ifhm,ok:=m[GET][url];ok{
returnhm,true
}
returnHandlerMapped{},false
}
//映射路由,获取Post方法下对应的接口
func(mMethodMaps)PostMapping(urlstring)(HandlerMapped,bool){
ifhm,ok:=m[POST][url];ok{
returnhm,true
}
returnHandlerMapped{},false
}
//映射路由,获取Delete方法下对应的接口
func(mMethodMaps)DeleteMapping(urlstring)(HandlerMapped,bool){
ifhm,ok:=m[DELETE][url];ok{
returnhm,true
}
returnHandlerMapped{},false
}
//映射路由,获取Put方法下对应的接口
func(mMethodMaps)PutMapping(urlstring)(HandlerMapped,bool){
ifhm,ok:=m[PUT][url];ok{
returnhm,true
}
returnHandlerMapped{},false
}
//增加Get方法下的接口
func(mMethodMaps)GetAdd(urlstring,mappedHandlerMapped){
if_,ok:=m.GetMapping(url);ok{
panic("duplicateurlwithgetmethod")
}
m[GET].SetUrl(url,mapped)
}
//增加Post方法下的接口
func(mMethodMaps)PostAdd(urlstring,mappedHandlerMapped){
if_,ok:=m.GetMapping(url);ok{
panic("duplicateurlwithPostmethod")
}
m[POST].SetUrl(url,mapped)
}
//增加Put方法下的接口
func(mMethodMaps)PutAdd(urlstring,mappedHandlerMapped){
if_,ok:=m.GetMapping(url);ok{
panic("duplicateurlwithPutmethod")
}
m[PUT].SetUrl(url,mapped)
}
//增加Delete方法下的接口
func(mMethodMaps)DeleteAdd(urlstring,mappedHandlerMapped){
if_,ok:=m.GetMapping(url);ok{
panic("duplicateurlwithDeletemethod")
}
m[DELETE].SetUrl(url,mapped)
}
func(hhandler)SetUrl(urlstring,mappedHandlerMapped){
h[url]=mapped
}
如我所说,我觉得学习Golang比较有意思的是,可以将从Java里学到的东西,转之在Golang里尝试实现,不仅学习了Golang,还使得自己对Java的认识进一步提升。如果读者有更好的方法,不吝赐教
参考资料:#Golang学习笔记-标准库"net/http"的简析及自制简单路由框架
本文内容总结:
原文链接:https://www.cnblogs.com/xxzhuang/p/9022941.html