4-分组控制(实现一个GoWeb框架Gee)
路由分组控制目标
实现路由分组控制
分组控制的意义分组控制(Group Control)是 Web 框架应提供的基础功能之一。所谓分组,是指路由的分组。如果没有路由分组,我们需要针对每一个路由进行控制。
真实的业务场景中,往往某一组路由需要相似的处理。例如:
以/post开头的路由匿名可访问。
以/admin开头的路由需要鉴权。
以/api开头的路由是 RESTful 接口,可以对接第三方平台,需要三方平台鉴权。
大部分情况下的路由分组,是以相同的前缀来区分的。因此,我们今天实现的分组控制也是以前缀来区分,并且支持分组的嵌套。例如/post是一个分组,/post/a和/post/b可以是该分组下的子分组。作用在/post分组上的中间件(middleware),也都会作用在子分组,子分组还可以应用自己特有的中间件。
中间件可以给框架提供无限的扩展能力,应用在分组上,可以使得分组控制的收益更为明显,而不是共享相同的路由前缀这么简单。例如/admin的分组,可以应用鉴权中间件;/分组应用日志中间件,/是默认的最顶层的分组,也就意味着给所有的路由,即整个框架增加了记录日志的能力。
分组嵌 ...
3-Trie树实现动态路由(实现一个GoWeb框架Gee)
Router目标
使用 Trie 树实现动态路由(dynamic route)解析。
支持两种模式:name和*filepath。
Trie树
前面使用map构建路由表,通过map索引只能实现简单的静态路由。但是动态路由就需要用另一种方法(trie树)。
在之前学习算法的时候每个节点存放的是一个字符,这个路由无非就是把每个节点存放路由对应的字符串(路由中的一部分)。并且添加两种模式参数匹配:name和通配*filepath。
trie.go每个节点结构体信息
123456type node struct { pattern string // 待匹配路由,例如 /p/:lang part string // 路由中的一部分,例如 :lang children []*node // 子节点,例如 [doc, tutorial, intro] isWild bool // 是否精确匹配,part 含有 : 或 * 时为true}
匹配函数
1234567891011121314151617181920// 第一个匹配成功的节点,用于插入func ...
2-上下文Context(实现一个GoWeb框架Gee)
上下文Context目标
将路由(router)独立出来,方便之后增强。
设计上下文(Context),封装 Request 和 Response ,提供对 JSON、HTML 等返回类型的支持。
设计Context
Web服务是根据请求*http.Request,构造响应http.ResponseWriter。
要构造一个完整的响应,需要考虑消息头(Header)和消息体(Body),而 Header 包含了状态码(StatusCode),消息类型(ContentType)等几乎每次请求都需要设置的信息。
若不进行有效的封装,那么框架的用户将需要写大量重复,繁杂的代码,而且容易出错。针对常用场景,能够高效地构造出 HTTP 响应是一个好的框架必须考虑的点。
栗子:
1234567891011121314151617// 封装之前obj = map[string]interface{}{ "name": "geektutu", "password": "1234&qu ...
1-HTTP基础(实现一个GoWeb框架Gee)
为什么要写这个项目 使用 Go 语言实现一个简单的 Web 框架,通过这个项目可以让我更好的去理解Go语言是如何实现一个简单的Web框架的。
当我们离开框架,使用基础库时,需要频繁手工处理的地方,就是框架的价值所在。但并不是每一个频繁处理的地方都适合在框架中完成。此时理解一个框架的特性是非常重要的,这也是我想要通过Go去实现一个框架的真正原因。
项目地址
HTTP基础一个简单的Web服务Go语言内置了 net/http库,封装了HTTP网络编程的基础的接口。
123456789101112131415package mainimport ( "fmt" "net/http")func index(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello World")}func main() { http.HandleFunc("/", index) http.L ...
4-gorm对象关系映射
gorm对象关系映射什么是ORM
ORM优缺点
优点
提高开发效率
缺点
牺牲执行性能
牺牲灵活性
弱化SQL能力
基础入门下载依赖1234567go get -u gorm.io/gormgo get -u gorm.io/driver/sqlite//或者go get -u github.com/jinzhu/gormgo get -u github.com/go-sql-driver/mysqlgo mod tidy
导入包1234import ( "gorm.io/gorm" "gorm.io/driver/sqlite")
一个栗子1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253package mainimport ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm ...
3-gin中间件
gin中间件首先感谢七米老师的讲解:视频地址这个视频让我更加清晰的了解了gin中间件。
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
定义中间件Gin中的中间件必须是一个gin.HandlerFunc类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。
12345678910111213141516// 定义一个中间件func authMiddleware(doCheck bool) gin.HandlerFunc { return func(c *gin.Context) { if doCheck { // 存放具体的逻辑 // 是否登录的判断 // if 是用户登录 // c.Next() // else // c.Abort() } else { c.Abort() } }}
注册中间件在gin框架中,我们可以为每个 ...
2-gin框架基础学习
配置配置代理和安装gin包
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.io,direct
安装 gin 包 go get -u github.com/gin-gonic/gin
将 gin 引入到代码中:
1import "github.com/gin-gonic/gin"
第一个Gin程序
123456789101112131415161718192021222324package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { // 创建一个默认的路由引擎 r := gin.Default() // GET请求方式;/hello:请求路径 // 客户端请求/hello路径是,会执行后面的的匿名函数 r.GET("/hello", func(c *gin.Context) { // c.J ...
1-模板(gin框架学习前置)
模板template模板基础html/template包实现了数据驱动的模板,用于生成可防止代码注入的安全的HTML内容。它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用html/template这个包。
模板文件通常定义为.tmpl和.tpl为后缀(也可以使用其他的后缀),必须使用UTF8编码。
模板文件中使用{{`和`}}包裹和标识需要传入的数据。
传给模板这样的数据就可以通过点号(.)来访问,如果数据是复杂类型的数据,可以通过{ { .FieldName }}来访问它的字段。
除{{`和`}}包裹的内容外,其他内容均不做修改原样输出。
模板使用三部曲
定义模板
解析模板
1func ParseFiles(filenames ...string) (*Template, error)
渲染模板
1func (t *Template) Execute(wr io.Writer, data interface{}) error
一个小栗子 ...
14-静态类型与动态类型(go基础)
静态类型与动态类型静态类型(static type)静态类型就是变量声明时候的类型。例如:
12// int 是静态类型var n int
动态类型(concrete type)动态类型是程序运行时系统才能看见的类型。例如:
123456// in 的静态类型为 interface{}var in interface{}// in 的静态类型为 interface{} 动态类型为 intin = 100// in 的静态类型为 interface{} 动态类型为 stringin = "jpc"
我们定义了一个空接口 in ,它的静态类型永远是 interface{} ,但它可以接受任何类型,接受整型数据时,它的动态类型就为 int ;接受字符串型数据时,它的动态类型就变为 string 。
接口组成每个接口变量实际上都是由一 pair 对组成,其中记录了实际变量的值和类型。
这里声明了一个类型为 int 的变量,变量名叫 number 值为 100 。
1 ...
13-错误与异常(go基础)
错误与异常错误Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义:
123type error interface { Error() string}
error 有了一个签名为 Error() string 的方法。所有实现该接口的类型都可以当作一个错误类型。Error() 方法给出了错误的描述。fmt.Println 在打印错误时,会在内部调用 Error() string 方法来得到该错误的描述。
123456789101112131415161718package mainimport ( "fmt" "os")func main() { // 尝试打开文件 file, err := os.Open("/a.txt") // 如果打开文件时发生错误 返回一个不等于 nil 的错误 if err != nil { fmt.Println(err) return ...