路由控制
beego
的路由设置有三种方式:固定路由、正则路由、自动路由。(具体可见:传送门)
下面以固定路由和自定义方法为例
修改视图
在views/index.tpl
里的<body>
标签修改如下(添加了一个form
表单和一个上传文件功能):
<body>
<header>
<h1 class="logo">Welcome to Beego</h1>
<div class="description">
Beego is a simple & powerful Go web framework which is inspired by tornado and sinatra.<br>
Username:{{.Username}}<br>
Age:{{.Age}}<br>
Email:{{.Eemail}}<br>
</div>
</header>
<div class="postform">
<form id="user" action="http://127.0.0.1:8080/" method="post">
名字:<input name="username" type="text" />
年龄:<input name="age" type="text" />
邮箱:<input name="Email" type="text" />
<input type="submit" value="提交" />
</form>
<form enctype="multipart/form-data" action="http://127.0.0.1:8080/upload" method="post">
<input type="file" name="uploadname" />
<input type="submit">
</form>
</div>
<footer>
<div class="author">
Official website:
<a href="http://{{.Website}}">{{.Website}}</a> /
Contact me:
<a class="email" href="mailto:{{.Email}}">{{.Email}}</a>
</div>
</footer>
<div class="backdrop"></div>
<script src="/static/js/reload.min.js"></script>
</body>
注意:
form
不指定method
属性的话默认是GET
,所以应该声明下method="post"
。不指定action
的话默认是发送请求至当前页面的url
,因此还是建议指定下action
。
在static/css/index.css
里修改如下(只是为了居中):
.description,
.postform {
text-align: center;
font-size: 16px;
}
然而可能有个问题,就是在重复启动项目的过程中,由于之前已经请求加载过一次css
文件了,被服务器保存在缓存中,所以我们本次修改后的css
文件不会被服务器更新,解决方法即在views/index.tpl
中修改如下:
<link href="../static/css/index.css?ver=1" rel="stylesheet" type="text/css" />
ver = n,n代表版本号,加一个版本号即可。
路由器
在routers/router.go
里添加如下代码:
func init() {
// 设置固定路由,指定url和对应的控制器
beego.Router("/", &controllers.MainController{})
beego.Router("/admin", &controllers.UserController{})
beego.Router("/admin/index", &controllers.ArticleController{})
beego.Router("/admin/addpkg", &controllers.AddController{})
// 设置自定义路由,指定发送到/upload的post请求对应控制器里的UploadFile方法
beego.Router("/upload", &controllers.MainController{}, `post:UploadFile`)
beego.Router("/TestPostJson", &controllers.MainController{}, `post:TestPostJson`)
beego.Router("/TestGetJson", &controllers.MainController{}, `get:TestGetJson`)
}
beego
的固定路由方式是典型的 RESTful
方式:一个固定的路由,一个控制器,然后根据用户请求方法不同请求控制器中对应的方法。比如url
是/admin
,根据上面代码,则会请求ArticleController
控制器的方法。(请求的 method
和函数名一致,例如 GET
请求执行 Get
函数,POST
请求执行 Post
函数,所以只需要在控制器里重写Get
方法和Post
方法)
而自定义路由方式,则多了beego.Router
的第三个参数。冒号前面部分表示发送到url
的http
请求的method
,如果是星号*
则表示所有。冒号后面表示对应匹配的方法,如post:UploadFile
表示传到该url
的post
请求指定对应控制器里的UploadFile
函数来处理,而*:UploadFile
表示所有传到该url
的请求都用UploadFile
函数来处理。
控制器
在controllers/default.go
里添加如下代码(这里MainController
还重写了Post
方法):
type MainController struct {
beego.Controller
}
type UserController struct {
beego.Controller
}
type ArticleController struct {
beego.Controller
}
type AddController struct {
beego.Controller
}
type user struct {
/*
定义 struct 时,字段名后如果有 form 这个 tag,则会以把 form 表单里的 name 和 tag 的名称一样的字段赋值给这个字段,
否则就会把 form 表单里与字段名一样的表单内容赋值给这个字段(通过form这个tag)。
如上面例子中,会把表单中的 username 和 age 分别赋值给 user 里的 Name 和 Age 字段,
而 Email 里的内容则会赋给 Email 这个字段(与字段名一样的表单内容,无需form)
*/
// 如果要忽略一个字段,有两种办法,一是:字段名小写开头,
// 二是:form 标签的值设置为"-"。这里明显是第二种
Id int `form:"-"`
Name interface{} `form:"username"`
Age int `form:"age"`
Email string
}
func (c *MainController) Get() {
// c.Data["XXX"]这个map数据结构可以直接被views视图以{{.XXX}}来获取
c.Data["Website"] = "MainController"
c.Data["Email"] = "MainController@gmail.com"
// 设置要渲染的模板
c.TplName = "index.tpl"
}
func (c *MainController) Post() {
// 新建一个user
u := user{}
// 接下来是为了验证以post提交的form的数据是否存储在c的Data这个map数据结构中,事实证明不是的。
beego.Debug("\n",
"Id:", 123, "\n",
"Name", c.Data["Username"], "\n",
"Age", c.Data["Age"], "\n",
"Email",c.Data["Eemail"])
// 将c,即MainController,里存储的数据转化到user格式的u变量,注意必须传入地址
if err := c.ParseForm(&u); err != nil {
log.Fatal("ParseForm err ", err)
}
// 现在u是个有数据的user了,取出来存到c里去
c.Data["Username"] = u.Name
c.Data["Age"] = u.Age
c.Data["Eemail"] = u.Email
// 输出u里的四个属性值
beego.Debug("\n",
"Id:", u.Id, "\n",
"Name", u.Name, "\n",
"Age", u.Age, "\n",
"Email", u.Email, "\n")
c.Data["Website"] = "MainController"
c.Data["Email"] = "MainController@gmail.com"
c.TplName = "index.tpl"
}
// 上传文件的post请求处理
func (c *MainController) UploadFile() {
// 获取控制器数据流里的文件
f, h, err := c.GetFile("uploadname")
if err != nil {
log.Fatal("getfile err ", err)
}
defer f.Close()
// 保存位置在 static/upload, 没有文件夹要先创建,不然文件保存失败
c.SaveToFile("uploadname", "static/upload/" + h.Filename)
c.Data["Website"] = "MainController"
c.Data["Email"] = "MainController@gmail.com"
c.TplName = "index.tpl"
}
func (c *UserController) Get() {
c.Data["Website"] = "UserController"
c.Data["Email"] = "UserController@gmail.com"
c.TplName = "index.tpl"
}
func (c *ArticleController) Get() {
c.Data["Website"] = "ArticleController"
c.Data["Email"] = "ArticleController@gmail.com"
c.TplName = "index.tpl"
}
func (c *AddController) Get() {
c.Data["Website"] = "AddController"
c.Data["Email"] = "AddController@gmail.com"
c.TplName = "index.tpl"
}
type User struct {
Id int
Username string
Password string
}
// POST JSON数据
func (c *MainController) TestPostJson() {
ob := &User{}
var err error
// POST过来的数据保存在requestBody里,由于是JSON格式,使用json.Unmarshal转为ob
if err = json.Unmarshal(c.Ctx.Input.RequestBody, &ob); err == nil {
// 输出ob
beego.Debug("\n",
"Unmarshal success", "\n",
"show Student :", "\n",
"Id", ob.Id, "\n",
"Username", ob.Username, "\n",
"Password", ob.Password, "\n",
)
// 再把ob转为JSON数据输出,注意传进Data["json"]的数据本身不能是JSON数据
c.Data["json"] = ob
c.ServeJSON()
}
c.Data["Website"] = "MainController"
c.Data["Email"] = "MainController@gmail.com"
c.TplName = "index.tpl"
}
// GET JSON数据
func (c *MainController) TestGetJson() {
ob := &User{520, "StellaChan", "19970227"}
// 将User类型的ob使用json.Marshal转为JSON格式
if b, err := json.Marshal(ob); err == nil {
// 输出ob
beego.Debug("\n",
b, "\n",
string(b), "\n",
ob, "\n",
)
// 再把ob转为JSON数据输出,注意传进Data["json"]的数据本身不能是JSON数据
c.Data["json"] = ob
c.ServeJSON()
}
}
首先我们声明了几个控制器,控制器里面内嵌了beego.Controller
,这就是 Go
的嵌入方式,也就是这些控制器都自动拥有了所有 beego.Controller
的方法。
而 beego.Controller
拥有很多方法,其中包括 Init
、Prepare
、Post
、Get
、Delete
、Head
等方法。我们可以通过重写的方式来实现这些方法,而我们上面的代码就是重写了 Get
和 Post
方法。
上述代码使用了固定路由和自定义路由两种。方法里面的代码是需要执行的逻辑,我们可以通过各种方式获取数据,然后赋值到 this.Data
中,这是一个用来存储输出数据的 map
数据结构,可以赋值任意类型的值。c.Data["XXX"]
这个map
数据结构可以直接被views
视图以{{.XXX}}
来获取,语法详见beego
模板语法指南:传送门。
最后一个就是需要去渲染的模板,this.TplName
就是需要渲染的模板,这里指定了 index.tpl
,如果用户不设置该参数,那么默认会去到模板目录的 Controller/<方法名>.tpl
查找,例如 maincontroller/get.tpl
(文件、文件夹必须小写)。
用户设置了模板之后系统会自动的调用 Render
函数(这个函数是在 beego.Controller
中实现的),所以无需用户自己来调用渲染。
当然也可以不使用模版,直接用 this.Ctx.WriteString
输出字符串,如:
func (this *MainController) Get() {
this.Ctx.WriteString("hello")
}
这样就会单纯返回一个字符串并显示。
上述代码还涉及文件上传以及一些http
请求的处理逻辑,下面我们会一一再详讲。
运行
现在先启动我们的项目,记得在main
函数里不要运行到上一节的数据库函数,只需要如下:
func main() {
beego.Run()
}
普通页面请求GET
输入http://127.0.0.1:8080/
界面显示如下:
后台显示如下:
请求的host
为127.0.0.1
,url
为“/”
,method
为GET
,请求响应结果状态码为200
,表示响应成功。
界面红框部分很明显是控制器的方法在作用:
func (c *MainController) Get() {
// c.Data["XXX"]这个map数据结构可以直接被views视图以{{.XXX}}来获取
c.Data["Website"] = "MainController"
c.Data["Email"] = "MainController@gmail.com"
// 设置要渲染的模板
c.TplName = "index.tpl"
}
我们在路由器还有其他的url
跟控制器的设置
输入 http://127.0.0.1:8080/admin
:
输入 http://127.0.0.1:8080/admin/index
:
输入 http://127.0.0.1:8080/admin/addpkg
:
可以看到输出与我们在控制器里的代码设置一致,程序运行正确。
POST
POST 表单
填写表单并提交:
后台输出结果如下:
第一部分的输出对应代码如下:
beego.Debug("\n",
"Id:", 123, "\n",
"Name", c.Data["Username"], "\n",
"Age", c.Data["Age"], "\n",
"Email",c.Data["Eemail"])
这里是为了验证以post
提交的表单数据,是否是存储在c
的Data
这个map
数据结构中,然而可以看到输出的结果为<nil>
,即为空,所以不能通过c.Data
直接取数据(实际上,提交的表单数据是保存在c.Ctx.Request.Form
中)
而后使用c.ParseForm(&u)
, 根据u
的结构体构造(form
这个 tag
),将表单数据保存在u
中,于是就可以通过u.XX
的方式提取出数据了。
之后代码如下:
// 现在u是个有数据的user了,取出来存到c里去
c.Data["Username"] = u.Name
c.Data["Age"] = u.Age
c.Data["Eemail"] = u.Email
可以看到页面的输出结果:
POST 文件
先在static
文件夹里新建一个文件夹,名为upload
选择文件并提交上传:
后台输出结果如下:
可以看到文件夹里多了刚上传的文件:
在beego
中,如果只是单纯的文件上传的话还是很方便处理的,有c.GetFile("")
和c.SaveToFile("")
两个函数分别负责获取上传的文件以及保存文件。
JSON数据请求
GET JSON
输入http://127.0.0.1:8080/TestGetJson
界面显示如下:
后台显示如下:
对应代码如下:
type User struct {
Id int
Username string
Password string
}
// GET JSON数据
func (c *MainController) TestGetJson() {
ob := &User{520, "StellaChan", "19970227"}
// 将User类型的ob使用json.Marshal转为JSON格式
if b, err := json.Marshal(ob); err == nil {
// 输出ob
beego.Debug("\n",
b, "\n",
string(b), "\n",
ob, "\n",
)
// 把ob转为JSON数据输出,注意传进Data["json"]的数据本身不能是JSON数据
c.Data["json"] = ob
c.ServeJSON()
}
}
先定义了个User
结构体,然后声明一个User
类型变量ob
并且初始化值。其实这个时候就可以通过赋值给c.Data["json"]
的方式将User
类型变量ob
以JSON
格式输出到页面(或者反馈给发送了GET
请求的客户端)。
中间展示的是通过json.Marshal
将User
类型的ob
转为JSON
格式的变量b
,此时b
直接输出是ASCII
码值,必须转为字符串string
类型才能正常显示。
POST JSON
正常网页没法做到POST
,因此本人此处借助Postman
来发送post
请求。
先在Headers
指定post
的数据格式为JSON
:
再在Body
中写入要Post
的JSON
数据,并且点击Send
:
后台显示如下(可以看到成功接收了JSON
数据并解析):
对应代码如下:
// POST JSON数据
func (c *MainController) TestPostJson() {
ob := &User{}
var err error
// POST过来的数据保存在requestBody里,由于是JSON格式,使用json.Unmarshal转为ob
if err = json.Unmarshal(c.Ctx.Input.RequestBody, &ob); err == nil {
// 输出ob
beego.Debug("\n",
"Unmarshal success", "\n",
"show Student :", "\n",
"Id", ob.Id, "\n",
"Username", ob.Username, "\n",
"Password", ob.Password, "\n",
)
// 再把ob转为JSON数据输出,注意传进Data["json"]的数据本身不能是JSON数据
c.Data["json"] = ob
c.ServeJSON()
}
c.Data["Website"] = "MainController"
c.Data["Email"] = "MainController@gmail.com"
c.TplName = "index.tpl"
}
与GET JSON
类似,先声明一个User
类型变量ob
,然后通过json.Unmarshal
将JSON
类型的c.Ctx.Input.RequestBody
(我们前文有提到POST
的数据保存在这里)转为User
类型的变量ob
。之后的步骤类似,最后我们再通过赋值给c.Data["json"]
的方式将User
类型变量ob
以JSON
格式输出到页面(或者反馈给发送了POST
请求的客户端)。