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 (与 ...
随机推荐
- Linux C 下的大文件操作
这里说的大文件指大小超过4G的文件. 在32位环境下,linux默认打开.读.写超过4G的文件会返回错误.定义如下宏可以突破这个限制,对read/write和fread/fwrite同时有效. 注意它 ...
- Android必知必会--使用shape制作drawable素材
前言 最近看到朋友制作的Android APP使用了极少的图片,但是图形却极其丰富,问了之后得知是使用shape绘制的,有很多优点. 下面是我整理的一些素材: 预览 下面是图片预览: 代码 布局文件 ...
- Mysql数据库安装和配置
http://blog.csdn.net/pipisorry/article/details/46773507 Mysql数据库安装和配置.mysql语法.特殊符号及正则表达式的使用.MySQL备份与 ...
- jsp中的tag与tld
转载自: http://www.cnblogs.com/fanzi2009/archive/2010/04/08/1707888.html 在jsp文件中,可以引用tag和tld文件. 1.对于ta ...
- FFMPEG结构体分析:AVIOContext
注:写了一系列的结构体的分析的文章,在这里列一个列表: FFMPEG结构体分析:AVFrame FFMPEG结构体分析:AVFormatContext FFMPEG结构体分析:AVCodecConte ...
- 【freeradius】使用radclient调试radius协议
freeradius 自带了非常好的客户端程序 radtest, radclient 用来模拟和调试设备和通信过程.radtest多用于认证,radclient更为强大一些,认证,计费,coa都可以模 ...
- Erlang cowboy 入门参考
Erlang cowboy 入门参考 cheungmine,2014-10-28 本文翻译自: http://ninenines.eu/docs/en/cowboy/HEAD/guide/gettin ...
- 在 Vim 中设置 Tab 为4个空格
缩进用 tab 制表符还是空格,这不是个问题,就像 python 用四个空格来缩进一样,这是要看个人喜好的.在 Vim 中可以很方便的根据不同的文件类型来设置使用 tab 制表符或者空格,还可以设置长 ...
- sqlserver2008中删除了windows用户,导致无法登陆的解决方案
打开管理工具中的"服务",找到并关闭SQL Server服务.进入命令进入SQL Server 2008的安装目录,找到sqlservr.exe文件,执行命令:sqlservr - ...
- winform编程设定listview选中行
在做项目中,需要用到listview显示数据.同时,项目要求,通过检索用户输入的数据,程序通过搜索,确定数据所在的行并通过程序设定为选中状态并高亮显示.同时,正常响应鼠标单击响应的效果,单击时,程序设 ...