golang自定义路由控制实现(一)
由于本人之前一直是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"的简析及自制简单路由框架
golang自定义路由控制实现(一)的更多相关文章
- golang自定义路由控制实现(二)-流式注册接口以及支持RESTFUL
先简单回顾一下在上一篇的文章中,上一篇我主要是结合了数组和Map完成路由映射,数组的大小为8,下标为0的代表Get方法,以此类推,而数组的值则是Map,键为URL,值则是我们编写对应的接口.但 ...
- web项目自定义路由_实现静态资源URL控制
前言: IIS会默认把:图片.JS.HTML.CSS这些文件当成静态资源处理,为了减少服务器压力,默认这些静态资源是不走URL路由规则控制的. 作为小白及初学者,本人对这些了解甚少,补充基础知识吧: ...
- golang web框架设计2:自定义路由
继续学习谢大的Go web框架设计 HTTP路由 http路由负责将一个http的请求交到对应的函数处理(或者一个struct的方法),路由在框架中相当于一个事件处理器,而这个时间包括 用户请求的路径 ...
- angularJs模块ui-router之路由控制
在你的应用中大多数状态都有与其相关联的 url,路由控制不是设计完成 state 之后的事后想法,而是开始开发时就应该考虑的问题. 这里是如何设置一个基本url. $stateProvider .st ...
- 阿里云容器服务--配置自定义路由服务应对DDOS攻击
阿里云容器服务--配置自定义路由服务应对DDOS攻击 摘要: 容器服务中,除了slb之外,自定义路由服务(基于HAProxy)也可以作为DDOS攻击的一道防线,本文阐述了几种方法来应对普通规模的DDO ...
- 以路由控制URL
至此为止,我们一直在使用ASP.NET MVC新项目随带的默认路由配置.现在我们将深入探讨路由系统,并学习如何创建应用程序的自定义路由,以确保URL既是用户友好又是搜索引擎可访问的. 路由的全部内容都 ...
- Django-restframework之路由控制、解析器及响应器
django-restframework之路由控制.解析器及响应器 一 前言 本篇博客介绍 restframework 框架的剩下几个组件,路由控制有三种:传统路由.半自动路由及全自动路由:解析器是用 ...
- $Django patch与put,视图组件,路由控制,响应器
1 patch与put(幂等?回顾) PATCH 与 PUT 属性上的一个重要区别还在于:PUT 是幂等的,而 PATCH 不是幂等的.幂等是一个数学和计算机学概念,在计算机范畴内表示一个操作执行任意 ...
- python 全栈开发,Day68(Django的路由控制)
昨日内容回顾 1 MVC和MTV MTV 路由控制层(分发哪一个路径由哪一个视图函数处理) V : views (逻辑处理) T : templates (存放html文件) M : model (与 ...
随机推荐
- (NO.00001)iOS游戏SpeedBoy Lite成形记(二十五)
每次压赌要打开弹出菜单还是让人略觉不爽,下面我们再添加一个随机押注的按钮:自动随机选择选手和下赌金额. 打开spriteBuilder,修改GameInterface.ccb的界面,在Run按钮旁边添 ...
- Django项目导入Eclipse运行调试
受不了没有调试的感觉. 前提是Eclipse插件已经安装完成并且可以成功运行例子.参考:http://blog.csdn.net/jerome_s/article/details/46340079 1 ...
- JQuery实战总结三 标签页效果图实现
在浏览网站时我们会看到当我们鼠标移到多个选项卡上时,不同的选项卡会出现自己对应的界面的要求,在同一个界面上表达了尽量多的信息.大大额提高了空间的利用率.界面的切换效果也是不错的哦,这次自己可以实现啦. ...
- 《java入门第一季》之类小案例(模拟用户登录)
首先是做一个用户登录的小案例.在此基础上加入其它逻辑. import java.util.Scanner; /* * 模拟登录,给三次机会,并提示还有几次.如果登录成功,就可以玩猜数字小游戏了. * ...
- Cocos2D物理碰撞不按预期工作的排查工作
如果该碰撞的节点不碰撞或反过来不该碰的碰撞了,你可以检查一下几个方面: 1.对应2个节点的分类和掩码必须匹配.如果它们应该碰撞则一个节点的分类应该在另一个节点的掩码中,反之亦然. 2.注意空的分类和掩 ...
- Java进阶(十一)部分数据类型取值范围
java整型byte,short,int,long取值范围大小 在项目开发中,需要用到随机数的生成方法,代码如下: public static String randomizeNumber(int n ...
- 客户信息全SQL
SELECT hp.party_name "客户名称", --客户名称 hca.account_number "客户编号", --客户编号 hca.cust_a ...
- struts ajax多级下拉菜单
先看项目截图: 看看要加入的jar包 除了struts核心的那个几个之外,我们还需要这两个 OK先看struts.xml <?xml version="1.0" encodi ...
- android 线程那点事
在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销,当系统中存在大量的线程时,系统会通过时间片轮转的方式调度每个 ...
- 嵌入式C快速翻转一个任何类型的数的二进制位
unsigned char reverse_bits(unsigned char value) { unsigned char answer , i ; answer = 0 ; for(i = 1 ...