我是靠谱客的博主 忧伤大侠,最近开发中收集的这篇文章主要介绍跟Gin一块搭建自己的web框架(三),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

在上一篇的基础之上继续实现前缀路由的功能。

首先说明一下这边说的前缀路由的概念。在上一篇的Web服务的基础上,我们对于路由可以有两种处理方式:

  1. 一种是所有的路由都挂载到根路径 ‘/’ 上边,举例说明,如果有一个登录和登出功能,我们可以这么来写

    router.GET("/user/login", loginHandler)
    router.GET("/user/logout", logoutHandler)
    

    当然这也是最为直观和容易理解的方式。

  2. 另一种是将有共通路径前缀的路由集合在一块,例如按照Gin的写法:

    user := route.Group("/user")
    {
    	user.GET("/login", loginHandler)
    	user.GET("/logout", logoutHandler)
    }
    

    当然各有各的好处了, 这种方式的方便之处在于,如果要对 “/user” 前缀的路由统一做处理(比如增加鉴权功能),就只需要在最上层加上处理逻辑,无需对每一个路由重复添加。尤其是在Gin里边,搭配后边会介绍的中间件一块使用,会显得更为方便。

接下来就介绍Gin里边如何实现的这一功能。同时我们慢慢的构建一个框架应该有的样子。

使用框架的目的是什么,我的理解是隐藏重复和底层的细节,使其作为一个可重复的抽象,让开发更为快速和便捷。前两篇都是直接用一个文件搭建了Web服务,现在我们开始真正的做一个抽象。

我们先抽象出来一个引擎 Engine,那么这个Engine里边应该有什么东西,很直观的,我们需要上一篇讲到的路由功能,以及这一篇讲到的路由前缀。定义如下:

Engine struct {
		*RouterGroup
		router *httprouter.Router
	}

假设我们已经完成了这个 Engine, 我们需要一个启动函数

func (engine *Engine) Run(addr string) {
	http.ListenAndServe(addr, engine)
}

看起来很简单,就是把上一篇中最后的启动函数 http.ListenAndServe 放到了Run里边而已。 区别只是把 engine 当作了第二个参数传入。而第二个参数其实只是 interface, 里边有一个函数

ServeHTTP(ResponseWriter, *Request)

所以只要 engine 也实现了这个函数,就能作为第二个参数了,那我们就实现一个。

// ServeHTTP makes the router implement the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	engine.router.ServeHTTP(w, req)
}

也是很简单,只是调用了 engine里边 httprouter.Router 的 ServeHTTP。

我们已经有了启动函数,接下去看看初始化的过程。先看engine中RouterGroup的定义

	// HandlerFunc .
	HandlerFunc func(*Context)

	// Context .
	Context struct {
		Req     *http.Request
		Writer  http.ResponseWriter
		Params  httprouter.Params
		handler HandlerFunc
		engine  *Engine
	}

	// RouterGroup .
	RouterGroup struct {
		Handler HandlerFunc
		prefix  string
		parent  *RouterGroup
		engine  *Engine
	}

这边定义了一个Context,这个是gin中最重要的数据结构之一。它把request的Req、Write以及Params都包在了里边。所以初始化的过程也就明确了

func New() *Engine {
	engine := &Engine{}
	engine.RouterGroup = &RouterGroup{nil, "", nil, engine}
	engine.router = httprouter.New()
	return engine
}

接下去定义group的时候,创建一个新的 RouterGroup

// Group .
func (group *RouterGroup) Group(component string) *RouterGroup {
	prefix := path.Join(group.prefix, component)
	return &RouterGroup{
		Handler: nil,
		parent:  group,
		prefix:  prefix,
		engine:  group.engine,
	}
}

然后调用

user.GET("/login", loginHandler)

的时候需要把对应的loginHandler加入到router的路由配置中

// GET is a shortcut for router.Handle("GET", path, handle)
func (group *RouterGroup) GET(path string, handler HandlerFunc) {
	group.Handle("GET", path, handler)
}

// Handle .
func (group *RouterGroup) Handle(method, p string, handler HandlerFunc) {
	p = path.Join(group.prefix, p)
	group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		group.createContext(w, req, params, handler).Next()
	})
}

func (c *Context) Next() {
	c.handler(c)
}

从上边的代码可以看出, user.GET 实际上就是先把routerGroup的前缀路径组合起来,然后将对应的路径和handler加入到httprouter里边。

同样的,类似GET方法的处理, 我们能定义其他的比如POST、DELETE的方法实现:

// POST is a shortcut for router.Handle("POST", path, handle)
func (group *RouterGroup) POST(path string, handler HandlerFunc) {
	group.Handle("POST", path, handler)
}

// GET is a shortcut for router.Handle("GET", path, handle)
func (group *RouterGroup) GET(path string, handler HandlerFunc) {
	group.Handle("GET", path, handler)
}

// DELETE is a shortcut for router.Handle("DELETE", path, handle)
func (group *RouterGroup) DELETE(path string, handler HandlerFunc) {
	group.Handle("DELETE", path, handler)
}

// PATCH is a shortcut for router.Handle("PATCH", path, handle)
func (group *RouterGroup) PATCH(path string, handler HandlerFunc) {
	group.Handle("PATCH", path, handler)
}

// PUT is a shortcut for router.Handle("PUT", path, handle)
func (group *RouterGroup) PUT(path string, handler HandlerFunc) {
	group.Handle("PUT", path, handler)
}

这样一个最简单的框架其实就已经成形了, 那么怎么使用这个框架呢, 我们把上一篇的main.go文件进行一下修改。

package main

import (
	"github.com/harleylau/myGin/v0.1/gin"
)

func v1IndexLoginfunc(c *gin.Context) {
	c.String(200, "login")
}

func v1IndexSubmitfunc(c *gin.Context) {
	c.String(200, "submit")
}

func main() {
	r := gin.New()

	// Simple group: v1
	v1 := r.Group("/v1")
	{
		v1.GET("/login", v1IndexLoginfunc)
		v1.GET("/submit", v1IndexSubmitfunc)
	}

	// Listen and server on 0.0.0.0:8080
	r.Run(":8080")
}

具体的代码实现可以参见github: https://github.com/harleylau/myGin/tree/master/v0.1

下一篇介绍Web框架中的中间件技术。

最后

以上就是忧伤大侠为你收集整理的跟Gin一块搭建自己的web框架(三)的全部内容,希望文章能够帮你解决跟Gin一块搭建自己的web框架(三)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(37)

评论列表共有 0 条评论

立即
投稿
返回
顶部