手撕 Beego
Beego 是一个快速开发 Go 应用的 HTTP 框架,可以用来开发 API、WEB 以及后端服务等各个应用
代码地址:https://github.com/astaxie/beego
在使用 beego 之前呢,他和 http 包一样每一个 URL 对应的都是一个处理器函数:
-
绑定 URL 和处理函数之间的关系
-
绑定好了之后启动服务
beego路由设置 beego存在三种方式的路由:固定路由、正则路由、自动路由。
3.1 Beego 初体验
3.1.1 启动 beego
package main
import (
"github.com/astaxie/beego"
)
func main() {
// 默认监听 8080
beego.Run()
}
这个页面能够正常显示表示 beego web 服务器已经启动正常了
3.1.2 第一种固定路由
URL 和处理之间的关系在框架中叫做路由,路由的话也分为多种,这里我讲的基本路由。
基本路由可以给一个 URL 绑定某个请求方法的处理器路由函数
请求方式:
Get、Post、Delete、Head、PUT、OPTIONS、Patch
这些请求方式都可以给他们绑定对应的 URL ,在 beego 里面一定要注意绑定了什么方法就要通过什么方法请求,不然就会 404
3.1.2.1 GET 请求方式
比如这里我要给一个 / 路径的 GET 方法绑定函数
package main
import (
// 导入 beego 中 context
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
func main() {
// 绑定函数
// 在 beego 中他的输入和输出都在 context.Context 结构体中
// Context 是最重要的对象,我们获取的请求数据和响应数据都需要 Context 来获取
beego.Get("/", func(cxt *context.Context) {
// Output 类似于 http.response 对象
// Body 函数要传递参数是一个字节切片,这里会在浏览器页面输出 hi beego
cxt.Output.Body([]byte("hi beego"))
})
beego.Run()
}
3.1.2.2 Post 请求方式
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
func main() {
beego.Post("/", func(ctx *context.Context) {
ctx.Output.Body([]byte("post"))
})
beego.Run()
}
通过curl 发起 post 请求
[15:53:17 root@go ~]#curl -XPOST "http://127.0.0.1:8080/"
post
3.1.2.3 Delete 请求
package main
import (
// "dql/go/pkg/mod/github.com/astaxie/beego@v1.12.3"
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
func main() {
beego.Delete("/", func(ctx *context.Context) {
ctx.Output.Body([]byte("Delete"))
})
beego.Run()
}
[15:55:30 root@go ~]#curl -XDELETE "http://127.0.0.1:8080/"
Delete
3.1.2.4 Head 请求
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
func main() {
beego.Head("/", func(ctx *context.Context) {
ctx.Output.Body([]byte("Head"))
})
beego.Run()
}
[15:57:01 root@go ~]#curl -I "http://127.0.0.1:8080/"
HTTP/1.1 200 OK # 状态码 200
Content-Length: 6
Date: Thu, 05 Aug 2021 07:57:05 GMT
Content-Type: text/plain; charset=utf-8
3.1.2.5 Put 请求
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
func main() {
beego.Put("/", func(ctx *context.Context) {
ctx.Output.Body([]byte("Put"))
})
beego.Run()
}
[15:58:31 root@go ~]#curl -XPUT "http://127.0.0.1:8080/"
Put
# 这里通过请求 Head 方法
[15:59:06 root@go ~]#curl -I "http://127.0.0.1:8080/"
HTTP/1.1 404 Not Found # 报错 404
Date: Thu, 05 Aug 2021 08:02:41 GMT
Content-Length: 2001
Content-Type: text/html; charset=utf-8
我们可以发现上面的每种请求方式都需要对应的请求方法,才能够被请求到不然就会报 404,所以我们可以通过下面的 Beego.Any
方法来接收和处理所有的请求方式
3.1.2.6 Any 方法处理所有请求方式
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
func main() {
beego.Get("/", func(ctx *context.Context) {
ctx.Output.Body([]byte("get hi beego"))
})
// Beego.Any 方法来接收和处理所有的请求方式
beego.Any("/", func(ctx *context.Context) {
ctx.Output.Body([]byte("Any"))
})
beego.Run()
}
我们可以看到通过 Any 方法能够接收所有请求,也就是说当我们在进行 url 请求的时候,如果程序代码中有对应请求的处理器函数,那就会执行对应的请求方法如下面的 GET ,如果没有则匹配 Any
# head 请求
[16:13:28 root@go ~]#curl -I "http://127.0.0.1:8080/"
HTTP/1.1 200 OK
Content-Length: 3
Date: Thu, 05 Aug 2021 08:13:31 GMT
Content-Type: text/plain; charset=utf-8
# put 请求
[16:13:31 root@go ~]#curl -XPUT "http://127.0.0.1:8080/"
Any
# delete 请求
[16:13:34 root@go ~]#curl -XDELETE "http://127.0.0.1:8080/"
Any
# post 请求
[16:13:39 root@go ~]#curl -XPOST "http://127.0.0.1:8080/"
Any
# get 请求
[16:13:51 root@go ~]#curl "http://127.0.0.1:8080/"
get hi beego
3.1.3 第二种正则路由
什么是正则路由呢?
正则路由就是可以使用正则表达式来进行匹配的字符串验证,和查找字符串,如果验证成功就访问,验证失败就错误。
正则表达式写法如下:
数值类型:0-9 \d
任意多个: [0-9]*
至少一个:[0-9]+
n,m 位:[0-9]{n,m}
英文字母组成: 小写:a-z 大写:A-Z \w
正则路由是在路由里面增加一段正则匹配,所有满足条件的都会执行对应的方法,可以使用此功能来实现伪静态的效果
package main
import (
"github.com/astaxie/beego/context"
"github.com/astaxie/beego"
)
func main() {
// /delete/:id/ 也就是说我们在访问的 url delete 后面必须有值
// :id 可以理解为 URL 参数的名字,也就是当我们想要获取该 rul 数据的时候使用的一个名字
beego.Any("/delete/:id/", func(ctx *context.Context) {
ctx.Output.Body([]byte("delete"))
})
beego.Run()
}
浏览器访问,只要在 /delete/ 后面加如数值就可以访问
但是如果没有在 delete 后面加值的话,直接访问的话就会报 404 ,因为不满足我们在程序定义中的 rul 格式
3.1.3.1 获取 :id 的值
在 beego 中的 context 中有一个 inpu.Param 的方法能够获取 :id 的值
package main
import (
"github.com/astaxie/beego/context"
"github.com/astaxie/beego"
)
func main() {
// /delete/:id/ 也就是说我们在访问的 url delete 后面必须有值
// :id 可以理解为 URL 参数的名字,也就是当我们想要获取该 rul 数据的时候使用的一个名字
beego.Any("/delete/:id/", func(ctx *context.Context) {
// inpu.Param 的方法能够获取 :id 的值
ctx.Output.Body([]byte("delete:" + ctx.Input.Param(":id")))
})
beego.Run()
}
浏览器访问 delete/ URL 时就会将 :id 和 delete 进行拼接
但是这里我们发现我们请求的 aaaa 字符串也请求进去了,那么我们如何去限制我们想请求的格式呢
3.1.3.2 通过正则限制请求格式
一般在字符串中包含正则的时候我们就是用 ` 反引号
package main
import (
"github.com/astaxie/beego/context"
"github.com/astaxie/beego"
)
func main() {
// (\d{1,8}) 正则表达式,表示 1-8 之间的位数值,而且必须输入数字
beego.Any(`/delete/:id(\d{1,8})/`, func(ctx *context.Context) {
ctx.Output.Body([]byte("delete:" + ctx.Input.Param(":id")))
})
beego.Run()
}
浏览器访问 http://10.0.0.10:8080/delete/12345678
浏览器访问 http://10.0.0.10:8080/delete/1
浏览器访问 http://10.0.0.10:8080/delete/123456789 这里超过了 1-8 就会报 404
浏览器访问 http://10.0.0.10:8080/delete/1a 只能写入 1-8 的纯数字,中间加入了在字母也不行
3.1.3.3 通过 int 类型限制 URL 只访问 int 类型
package main
import (
"github.com/astaxie/beego/context"
"github.com/astaxie/beego"
)
func main() {
// /get/:id:int/ 只能够访问 int 类型为后缀的 RUL
beego.Any(`/get/:id:int/`, func(ctx *context.Context) {
ctx.Output.Body([]byte("get:" + ctx.Input.Param(":id")))
})
beego.Run()
}
浏览器访问 http://10.0.0.10:8080/get/23123123123123123 并且不会限制 int URL 长度,但是不推荐使用这种写法,还是推荐使用正则写法,因为这个没办法控制访问的 URL 长度
3.1.3.4 匹配以固定开头的 URL
比如我想访问 http://xxxxx/user 固定开头的 URL ,代码就需要写成这种
package main
import (
"github.com/astaxie/beego/context"
"github.com/astaxie/beego"
)
func main() {
// /user/* 只能匹配 user 开头的 URL 否则都为 404
beego.Any(`/user/*`, func(ctx *context.Context) {
ctx.Output.Body([]byte("user"))
})
beego.Run()
}
访问 http://10.0.0.10:8080/user/
访问 http://10.0.0.10:8080/user/12123aa 即使在 user URL 后面添加其他参数依旧匹配 user
访问 http://10.0.0.10:8080/ 就会报 404 因为我们没有通过 user 的 URL 进行访问
3.1.3.5 如何获取固定开头的 URL 的后面路径信息
那我们如何获取 user 这个 URL 后面的路径呢?通过 ctx.Input.Param(":splat")
package main
import (
"github.com/astaxie/beego/context"
"github.com/astaxie/beego"
)
func main() {
// /user/* 只能匹配 user 开头的 URL 否则都为 404
beego.Any(`/user/*`, func(ctx *context.Context) {
// ctx.Input.Param(":splat") 获取到 user 这个 URL 后面的 path 信息
ctx.Output.Body([]byte("user:" + ctx.Input.Param(":splat")))
})
beego.Run()
}
浏览器访问 http://10.0.0.10:8080/user/xxx/aaa 这个 URL 就会将 xxx/aaa
path 信息输出
3.1.3.6 在 URL 中获取文件名和文件后缀
package main
import (
"github.com/astaxie/beego/context"
"github.com/astaxie/beego"
)
func main() {
// 在处理器函数中添加 *.* 可以通过正则匹配获取文件名和该文件的后缀
beego.Any(`/file/*.*`, func(ctx *context.Context) {
// ctx.Input.Param(":path") + "." + ctx.Input.Param(":ext") 获取文件名和后缀并且以 . 为分隔符,当然 "." 可以有人定义
ctx.Output.Body([]byte("user:" + ctx.Input.Param(":path") + "." + ctx.Input.Param(":ext")))
})
beego.Run()
}
浏览器访问 http://10.0.0.10:8080/file/a.json
浏览器访问 http://10.0.0.10:8080/file/1/2/b.txt
3.2 beego web 处理器
beego web 处理器一个是面向对象(处理器),和 http 中的处理器一样,都是通过结构体来实现
package main
import (
"github.com/astaxie/beego"
)
// 路由控制器
type HomeController struct {
// 给 HomeController 的属性定义为 beego.Controller 匿名结构体
beego.Controller
}
// 定义 get 方法
func (c *HomeController) Get() {
}
func main() {
// 通过 beego.Router 路由函数绑定 /home/ URL,绑定的处理器时 HomeController 结构体
// 当我们访问 /home/ URL 就会将请求交给 HomeController 进行处理
beego.Router("/home/", &HomeController{})
beego.Run()
}
浏览器访问 http://10.0.0.10:8080/home 500 状态码错误
原因是 beego 在启动的时候默认是以发布的方式去启动,所以我们看不到他的一些错误信息,那么我们如何给他进行调试模式呢?
3.2.1 beego 调试模式
我们可以给 beego 代码的当前目录创建一个 conf 文件夹,并创建 app.conf
[18:17:57 root@go testbeego]#mkdir conf
[18:18:00 root@go testbeego]#touch conf/app.conf
[18:18:06 root@go testbeego]#cd conf/
[18:18:08 root@go conf]#ll
total 0
-rw-r--r-- 1 root root 0 Aug 5 18:18 app.conf
创建好了之后我们可以在 app.conf 写入如下内容
[18:18:48 root@go conf]#cat app.conf
runmode=dev
然后再运行我们的 beego 程序
[18:19:09 root@go conf]#cd ..
[18:19:23 root@go testbeego]#go run main.go
在刷新我们的 http://10.0.0.10:8080/home URL,提示:beego:在以下路径中找不到templatefile:views/homecontroller/get.tpl
这个文件
我们修改代码
package main
import (
"fmt"
"github.com/astaxie/beego"
)
// 路由控制器
type HomeController struct {
// 给 HomeController 的属性定义为 beego.Controller 匿名结构体
beego.Controller
}
// 定义 get 方法
func (c *HomeController) Get() {
// 修改之后在 get 方法中输出 HomeController get
fmt.Println("HomeController get")
}
func main() {
// 通过 beego.Router 路由函数绑定 /home/ URL,绑定的处理器时 HomeController 结构体
// 当我们访问 /home/ URL 就会将请求交给 HomeController 进行处理
beego.Router("/home/", &HomeController{})
beego.Run()
}
浏览器执行并且刷新了 http://10.0.0.10:8080/home URL 之后可以看到是进入到了 Get()
方法中的
但是为什么会报错呢?原因是在 beego 的处理控制器中如果没有指定响应,他就会默认去找 views 文件夹下的 homecontroller(当前控制器的名称)/get.tpl(文件)
,如果没有 get.tpl
文件的话就会报错
3.2.2 指定定义处理器响应 context 对象
因为在上面代码中我们的处理器没有响应是因为没有定义 context
对象,所以这次我们修改代码添加 context
对象来响应客户端请求
package main
import (
"github.com/astaxie/beego"
)
// 路由控制器
type HomeController struct {
// 给 HomeController 的属性定义为 beego.Controller 匿名结构体
beego.Controller
}
// 定义 get 方法
func (c *HomeController) Get() {
// 调用 Ctx 方法 ,该方法由 beego.Controller 匿名结构体中找出来的
// 响应客户端输出 HomeController get
c.Ctx.Output.Body([]byte("HomeController get"))
}
func main() {
// 通过 beego.Router 路由函数绑定 /home/ URL,绑定的处理器时 HomeController 结构体
// 当我们访问 /home/ URL 就会将请求交给 HomeController 进行处理
beego.Router("/home/", &HomeController{})
beego.Run()
}
浏览器访问 http://10.0.0.10:8080/home 而且是一个 get 方法的请求
但是我们并没有指定他请求的方法,那么为啥他会请求到 GET 方法上?因为HomeController
结构体定义的是 GET 方法,所以就能请求 GET ,当然我们可以通过 curl 浏览器来请求他的 POST 方法,验证是否正确,我们可以通过 curl 请求看到 状态码为 405 ,为什么是 405 而不是 404 呢?因为我们没有给 HomeController
控制器写其他的请求方式,所以这个 URL 的请求关系是有的大那是 Method 的处理函数是没有的所以这里报的是 405
[18:37:04 root@go ~]#curl -XPOST "http://127.0.0.1:8080/home/" -v
* About to connect() to 127.0.0.1 port 8080 (#0)
* Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /home/ HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:8080
> Accept: */*
>
< HTTP/1.1 405 Method Not Allowed # 我们可以看到请求的状态码为 405
< Content-Type: text/plain; charset=utf-8
< Server: beegoServer:1.12.3
< X-Content-Type-Options: nosniff
< Date: Thu, 05 Aug 2021 10:37:07 GMT
< Content-Length: 19
<
Method Not Allowed
* Connection #0 to host 127.0.0.1 left intact
3.2.3 指定处理器响应 post 请求
修改代码在 HomeController
控制器中添加 POST 方法,在通过 curl 命令通过 POST 请求来验证是否成功
package main
import (
"github.com/astaxie/beego"
)
// 路由控制器
type HomeController struct {
// 给 HomeController 的属性定义为 beego.Controller 匿名结构体
beego.Controller
}
// 添加 post 方法
func (c *HomeController) Post() {
c.Ctx.Output.Body([]byte("post post HomeController"))
}
func main() {
// 通过 beego.Router 路由函数绑定 /home/ URL,绑定的处理器时 HomeController 结构体
// 当我们访问 /home/ URL 就会将请求交给 HomeController 进行处理
beego.Router("/home/", &HomeController{})
beego.Run()
}
curl 浏览器访问时发现状态码为 200 请求成功,由此可以判断出在通过处理器来响应的时候,是通过控制器的方法来处理客户端的请求
[18:37:07 root@go ~]#curl -XPOST "http://127.0.0.1:8080/home/" -v
* About to connect() to 127.0.0.1 port 8080 (#0)
* Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /home/ HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:8080
> Accept: */*
>
< HTTP/1.1 200 OK # 状态码为 200 请求成功
< Content-Length: 24
< Server: beegoServer:1.12.3
< Date: Thu, 05 Aug 2021 10:41:22 GMT
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host 127.0.0.1 left intact
post post HomeController # post 请求到的数据