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 (与 ...
随机推荐
- UNIX环境高级编程——进程基本概述
一.什么是进程 从用户的角度来看进程是程序的一次执行过程.从操作系统的核心来看,进程是操作系统分配的内存.CPU时间片等资源的基本单位.进程是资源分配的最小单位.每一个进程都有自己独立的地址空间与执行 ...
- Scipy教程 - 距离计算库scipy.spatial.distance
http://blog.csdn.net/pipisorry/article/details/48814183 在scipy.spatial中最重要的模块应该就是距离计算模块distance了. fr ...
- 【一天一道LeetCode】#68. Text Justification
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...
- java的hashmap与hashtable说明,简单易理解
1. HashMap 1) hashmap的数据结构 Hashmap是一个数组和链表的结合体(在数据结构称"链表散列"),如下图示: 当我们往hashmap中put元素的时候,先根 ...
- (NO.00002)iOS游戏精灵战争雏形(一)
原本想做一个复杂点的平面动作游戏,可以觉得还是有点把握不了.还是先从简单的原型开始吧. 构思中的精灵战争(SpriteWar)是一个类似FC时代的小游戏,可以造兵,可以捕获敌兵.原本还想加上保卫老巢的 ...
- (NO.00001)iOS游戏SpeedBoy Lite成形记(二十八):增加排行榜功能
游戏大体上基本也就完成了,还差一个排行榜.否则如何激励各位选手创造新纪录呢? 排行榜功能也没什么难的,不过需要一点点排序的算法上的考虑. 这里我们把排行榜记录数据和排序都放在GameState类中,在 ...
- Android View底层到底是怎么绘制的
Android绘制链图: 网上很多讲Android view的绘制流程往往只讲到了Measure - Layout - Draw. 但是,这只是一个大体的流程,而我们需要探讨的是Android在我们 ...
- RabbitMQ安装使用详解
1.下载相应的版本安装:http://www.rabbitmq.com/download.htmleg:http://www.rabbitmq.com/releases/rabbitmq-server ...
- Windows CE Notification API的使用方法
1 引言 以Windows CE 为操作系统的掌上电脑(如PocketPC或HPC),除具备PC的功能外,还具备很强的自身控制能力.Windows CE API超越微软其他操作系统的 API ...
- Retinex图像增强算法
前一段时间研究了一下图像增强算法,发现Retinex理论在彩色图像增强.图像去雾.彩色图像恢复方面拥有很好的效果,下面介绍一下我对该算法的理解. Retinex理论 Retinex理论始于Land和M ...