Golang-学习笔记-魂宗[一]

一、Beego初识

1.1、Beego概述

出处,作者在写书《Go Web 编程》的时候想写一个框架。设计灵感,来源于tornado,sinatra和flask 是一个http的框架。适用于各种服务:api服务、后端服务、游戏开发...

BeeGo特点:

  • 高性能,是目前最快的go框架?
  • 快速开发
  • 文档完整
  • 使用简单,适合新手
  • 国人开发,国内最火的框架,社区活跃,文档齐全
  • 模块化,典型的mvc框架,提供的模块众多。比如session、orm、日志、cache、性能监测等
  • 智能化、智能监控、智能router、可监控cpu、memory、goroutine
  • ...

课程大纲:

  • 入门:
    • 环境搭建
    • 第一个beego项目
    • 运行逻辑介绍
    • controller 运行原理
    • 结构体详解
  • Beego数据交互
    • 字符串渲染
    • 模板渲染简介
    • 其他数据类型数据渲染
    • 静态文件使用
    • ajax数据交互
    • form解析到结构体
    • 多种格式数据输出
    • flash数据传递
  • controller模块
    • 配置文件详解
    • 路由配置
    • controller方法
    • 跨站请求伪造xsrf
    • 文件上传
    • session
    • 自定义过滤器
    • urlfor过滤器
    • 表单验证
    • 错误处理
  • views模块
    • 模板语法
    • 模板中的基本函数
    • 自定义模板函数
    • 模板处理
    • layout设计
    • renderform
    • 静态文件处理
  • model模块
    • orm使用
    • 模型定义
    • CRUD
    • exper表达式
    • QueryTable接口
    • 原生sql
    • 命令行自动建表
    • 一对一
    • 一对多
    • 多对多
    • 构造查询
    • 关联查询
    • 子查询
  • 日志模块:
    • 日志模块介绍
    • beego自带日志处理
    • 日志规范
    • 日志模块的使用
    • 常用日志模板
    • 日志模板封装
  • cache模块
    • 缓存介绍
    • cache模块的使用
    • 引擎设置
    • 开发自己的引擎
  • httplib模块
    • httplib的使用
    • 请求参数
    • 发送大片数据
    • 设置header信息
    • 设置transport
  • 实战:
    • 涉及内容:
      • 登录、验证码
      • 用户管理
      • 报表录入
      • 消息通知
      • 内容发布
      • 申请、审核
      • 到期提醒、已阅读等
      • 角色管理
      • 权限管理、权限树
      • 菜单动态显示
      • 图表(Echarts)
    • 项目部署
      • 独立部署
      • supervisor部署
      • Nginx部署
      • Apache部署
  • git版本控制
    • gitlab介绍
    • 在开发工具中使用git
    • 拉取提交代码
    • 解决冲突
    • 查看提交日志
    • 分支

1.2、环境准备

  1. Go 安装和环境变量配置:GOROOT/GOPATH/PATH/
  2. git安装
  3. bee工具,管理beego项目 go get github.com/beego/bee
  4. 安装beego go get github.com/beego/bee/v2GOBIN 目录下会有一个bee.exe
  5. 安装goland IDE

1.3、第一个beego项目

执行:bee new beego_project

D:\Program_language\Project1\src>bee new beego_project
2021/10/22 14:59:46 INFO     ▶ 0001 generate new project support go modules.
2021/10/22 14:59:46 INFO     ▶ 0002 Creating application...
        create   D:\Program_language\Project1\src\beego_project\go.mod
        create   D:\Program_language\Project1\src\beego_project\
        create   D:\Program_language\Project1\src\beego_project\conf\
        create   D:\Program_language\Project1\src\beego_project\controllers\
        create   D:\Program_language\Project1\src\beego_project\models\
        create   D:\Program_language\Project1\src\beego_project\routers\
        create   D:\Program_language\Project1\src\beego_project\tests\
        create   D:\Program_language\Project1\src\beego_project\static\
        create   D:\Program_language\Project1\src\beego_project\static\js\
        create   D:\Program_language\Project1\src\beego_project\static\css\
        create   D:\Program_language\Project1\src\beego_project\static\img\
        create   D:\Program_language\Project1\src\beego_project\views\
        create   D:\Program_language\Project1\src\beego_project\conf\app.conf
        create   D:\Program_language\Project1\src\beego_project\controllers\default.go
        create   D:\Program_language\Project1\src\beego_project\views\index.tpl
        create   D:\Program_language\Project1\src\beego_project\routers\router.go
        create   D:\Program_language\Project1\src\beego_project\tests\default_test.go
        create   D:\Program_language\Project1\src\beego_project\main.go
2021/10/22 14:59:46 SUCCESS  ▶ 0003 New application successfully created!

主要以在当前路径下生成go项目文件,可能是因为开了go mod模式,如果不开可能只会在src/目录下生成,待测试

启动:

D:\Program_language\Project1\src\beego_project>bee run
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v2.0.2
2021/10/22 15:03:53 INFO     ▶ 0001 Using 'beego_project' as 'appname'
2021/10/22 15:03:53 INFO     ▶ 0002 Initializing watcher...
go: downloading golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
go: downloading golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58
go: downloading github.com/pkg/errors v0.9.1
go: downloading golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f
go: downloading golang.org/x/net v0.0.0-20201021035429-f5854403a974
go: downloading golang.org/x/text v0.3.3
go: downloading golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
go: downloading golang.org/x/mod v0.3.0
github.com/beego/beego/v2
golang.org/x/sys/internal/unsafeheader
golang.org/x/mod/semver
golang.org/x/xerrors/internal
golang.org/x/text/transform
github.com/pkg/errors
github.com/beego/beego/v2/server/web/session
github.com/beego/beego/v2/server/web/grace
golang.org/x/sys/windows
golang.org/x/crypto/acme
golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm
golang.org/x/tools/go/internal/gcimporter
github.com/beego/beego/v2/core/admin
github.com/beego/beego/v2/core/logs
github.com/beego/beego/v2/server/web/context
github.com/prometheus/client_golang/prometheus
golang.org/x/tools/internal/event/label
golang.org/x/text/secure/bidirule
golang.org/x/tools/internal/typesinternal
golang.org/x/tools/go/gcexportdata
golang.org/x/xerrors
github.com/beego/beego/v2/core/config
github.com/beego/beego/v2/server/web/context/param
github.com/prometheus/client_golang/prometheus/promhttp
golang.org/x/tools/internal/event/keys
golang.org/x/net/idna
golang.org/x/tools/internal/event/core
golang.org/x/crypto/acme/autocert
golang.org/x/tools/internal/event
golang.org/x/tools/internal/gocommand
golang.org/x/tools/internal/packagesinternal
golang.org/x/tools/go/internal/packagesdriver
golang.org/x/tools/go/packages
github.com/beego/beego/v2/server/web
beego_project/controllers
beego_project/routers
beego_project
2021/10/22 15:04:37 SUCCESS  ▶ 0003 Built Successfully!
2021/10/22 15:04:37 INFO     ▶ 0004 Restarting 'beego_project.exe'...
2021/10/22 15:04:37 SUCCESS  ▶ 0005 './beego_project.exe' is running...
2021/10/22 15:04:44.703 [I] [parser.go:413]  generate router from comments

2021/10/22 15:04:44.724 [I] [server.go:241]  http server Running on http://:8080

浏览器打开:http://127.0.0.1:8080

或者在项目工作目录内使用: go run main.go 运行

目录解析:

D:\Program_language\Project1\src\beego_project>tree /F
D:.
│  go.mod
│  go.sum
│  main.go  #程序主入口
├─conf
│      app.conf   #配置文件信息
├─controllers 
│      default.go  #业务逻辑
├─models  #模型,和orm相关
├─routers  #路由
│      router.go
├─static  #静态文件
│  ├─css
│  ├─img
│  └─js
│          reload.min.js
├─tests
│      default_test.go  #测试文件,自动化测试等
└─views 
        index.tpl   #代码视图

1.4、controller概述

>>>>>>>>>>>>>>>>>>>>>>>>router.GO
package routers

import (
	"beego_project/controllers"
	beego "github.com/beego/beego/v2/server/web"
)

func init() {
    beego.Router("/", &controllers.MainController{})  //这俩的"/"对应http的路径,可以修改为其他的,则对应访问的url也要变
}  
//针对 访问 ”/"的请求,由 ontrollers.MainController 处理
>>>>>>>>>>>>>>>>>>>>>> controller/default.go
package controllers

import (
	beego "github.com/beego/beego/v2/server/web"
)

type MainController struct {
	beego.Controller
}

func (c *MainController) Get() {  //对前端浏览器访问为get请求,则对应该函数处理
	c.Data["Website"] = "icewake"
	c.Data["Email"] = "icewake@xx.com"
	c.TplName = "index.tpl"
}

>>>>>>>>>>>>>>>controller.go文件
type Controller struct {  //这个是 MainController 引入的beego的匿名字段,beego.Controller 有很多方法
	// context data
	Ctx  *context.Context
	Data map[interface{}]interface{}

	// route controller info
	controllerName string
	actionName     string
	methodMapping  map[string]func() //method:routertree
	AppController  interface{}

	// template data
	TplName        string
	ViewPath       string
	Layout         string
	LayoutSections map[string]string // the key is the section name and the value is the template name
	TplPrefix      string
	TplExt         string
	EnableRender   bool

	// xsrf data
	_xsrfToken string
	XSRFExpire int
	EnableXSRF bool

	// session
	CruSession session.Store
}


func (c *Controller) Get() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}

func (c *Controller) Post() {
	http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
}
...


第一个controller编写

步骤1:创建controller ,文件 controllers/default.go
type UserController struct {  //新增 UserController
	beego.Controller  //必须要添加的匿名字段
}

func (u *UserController) Get(){
	u.TplName = "user.html"
	u.Data["user"] = "小明"
	u.Data["age"] = 18
}

步骤2:创建router,文件routers/router.go
func init() {
    beego.Router("/", &controllers.MainController{})
    beego.Router("/user", &controllers.UserController{})  //新增
}

步骤3:创建视图文件,文件views/user.html
<!DOCTYPE html>
<html lange="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
用户页面
用户名: {{ .user }}
年龄:{{ .age }}

</body>
</html>

二、数据交互

2.1、数据类型渲染

设置view路径:

  • 方法1在main.go中设置:beego.SetViewsPath("静态文件存放路径") ,注意要在beego.Run()之前设置
  • 方法2在conf/app.conf中设置:viewpath="存放路径"

beego采用了Go内置的模板引擎

  • 指定模板:u.TplName = 'user.html'
    • 默认支持tplhtml
    • beego.AddTemplateExt设置其他后缀
      • beego.AddTemplateExt("后缀名")
    • 如果不设置该参数,那么默认会去到模板目录的 Controller/<方法名>.tpl 查找,例如上面的没有指定 u.TplName = "user.html" 则默认会找 views/usercontroller/get.tpl

2.1.1、字符串渲染

func (u *UserController) Get(){  //controller中编写
	u.TplName = "user.html"
	u.Data["user"] = "小明"
	u.Data["age"] = 18
}

<!DOCTYPE html>  //在user.html中渲染
<html lange="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
用户页面
用户名: {{ .user }}
年龄:{{ .age }}

</body>
</html>

2.1.2、结构体渲染

>>>>>>>>>>>>>>>> controllers/default.go中添加用户的结构体
type UserStruct struct {
	Id int
	Name string
	Age int
}

func (u *UserController) Get(){
	u.TplName = "user.html"
	user1 := UserStruct{Id:1,Age:18,Name:"令狐冲"}
	u.Data["user"] = user1
}

>>>>>>>>>>>>>>> 修改user.html
<!DOCTYPE html>
<title >Icewake</title>
<html lange="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
用户页面
{{/*ID : {{ .user.Id }}*/}}
用户名: {{ .user.Name }}
年龄:{{ .user.Age }}

</body>
</html>

2.1.3、数组渲染

>>>>>>>>>>>>>>>> controllers/default.go中添加数组
func (u *UserController) Get(){
	u.TplName = "user.html"
	//u.Data["user"] = "小明"
	//u.Data["age"] = 18
	user1 := UserStruct{Id:1,Age:18,Name:"令狐冲"}
	ary := [...]int{7,8,10,11}

	u.Data["user"] = user1
	u.Data["arry"] = ary
}

>>>>>>>>>>>>>>> 修改user.html
<!DOCTYPE html>
<title >Icewake</title>
<html lange="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
用户页面
{{/*ID : {{ .user.Id }}*/}}
用户名: {{ .user.Name }}
年龄:{{ .user.Age }}

<br>
====================================<br>
// 遍历方式1
{{ range .arry}}
    {{ .}}
{{ end}}
<br>
====================================<br>
// 遍历方式2
{{ range $i,$v := .arry}}
    <br>
    {{$i}}={{$v}}
{{ end}}

</body>
</html>


>>>>>>>>>>>>>>>>>>>>>>> 效果如下:
用户页面 用户名: 令狐冲 年龄:18
====================================
// 遍历方式1 7 8 10 11
====================================
// 遍历方式2
0=7
1=8
2=10
3=11

2.1.4、结构体数组渲染

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go中添加数组
func (u *UserController) Get(){
	u.TplName = "user.html"
	user1 := UserStruct{Id:1,Age:18,Name:"令狐冲"}
	ary := [...]int{7,8,10,11}
	aryStruct := [3]UserStruct{{Id:1,Age:55,Name:"岳不群"},{Id:2,Name:"岳灵珊",Age:20}}

	u.Data["aryStruct"]	 = aryStruct
	u.Data["user"] = user1
	u.Data["arry"] = ary
}


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 修改user.html
<!DOCTYPE html>
<title >Icewake</title>
<html lange="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
用户页面
{{/*ID : {{ .user.Id }}*/}}
用户名: {{ .user.Name }}
年龄:{{ .user.Age }}

<br>
====================================<br>
// 遍历方式1
{{ range .arry}}
    {{ .}}
{{ end}}
<br>
====================================<br>
// 遍历方式2
{{ range $i,$v := .arry}}
    <br>
    {{$i}}={{$v}}
{{ end}}

====================================<br>
// 结构体数组遍历1
{{ range .aryStruct}}
    <br>
    {{.}}
{{ end}}
====================================<br>
// 结构体数组遍历2
{{ range .aryStruct}}
    {{.Id}}
    {{.Name}}
    {{.Age}}
{{end}}
====================================<br>
// 结构体数组遍历3,会自动判别是一个参数还是两个参数接收
{{/*{{ range $i,$v := .aryStruct}}*/}}
{{ range $v := .aryStruct}}
    <br>
    {{ $v.Id}}     {{ $v.Name}}     {{ $v.Age}}
{{ end}}

</body>
</html>


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 效果
用户页面 用户名: 令狐冲 年龄:18
====================================
// 遍历方式1 7 8 10 11
====================================
// 遍历方式2
0=7
1=8
2=10
3=11
====================================
// 结构体数组遍历1
{1 岳不群 55}
{2 岳灵珊 20}
{0 0}
====================================
// 结构体数组遍历2 1 岳不群 55 2 岳灵珊 20 0 0
====================================
// 结构体数组遍历3,会自动判别是一个参数还是两个参数接收
1 岳不群 55
2 岳灵珊 20
0 0

2.1.5、map渲染

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go中添加数组

func (u *UserController) Get(){
	u.TplName = "user.html"
	...
    
	//map渲染
	map01 := map[string]string{"name":"iceman","age":"18","email":"aa@icemail.com"}
	u.Data["map01"] = map01
 
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 修改user.html 增加如下内容
// map 遍历 方法1
{{ .map01.name }}
{{ .map01.age }}
{{ .map01.email }}
<br>
// map 遍历 方法2
{{ range  .map01}}
    {{.}}
{{ end }}
<br>
// map 遍历 方法3
{{ range $k,$v := .map01}}
    {{$k}}={{$v}}
{{ end }}


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 效果
// map 遍历 方法1 iceman 18 aa@icemail.com
// map 遍历 方法2 18 aa@icemail.com iceman
// map 遍历 方法3 age=18 email=aa@icemail.com name=iceman

2.1.6、结构体渲染

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go中添加数组
	//结构体map渲染
	map02 := make(map[int]UserStruct)
	map02[1] = UserStruct{101,"萧峰",28}
	map02[2] = UserStruct{102,"段誉",20}
	map02[3] = UserStruct{103,"虚竹",26}
	u.Data["map02"] = map02

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 修改user.html 增加如下内容
// 数组map渲染1 - 这种方法是不行的
{{/*{{ .map02.1.Id}}*/}}
{{/*{{ .map02.2.Name}}*/}}
{{/*{{ .map02.3.Age}}*/}}

{{ range .map02 }}
    {{.}}
{{ end}}
<br>
{{ range $k,$v := .map02 }}
    {{$k}}={{$v}}
    {{$v.Name}}
{{ end}}



>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 效果
// 数组map渲染1 - 这种方法是不行的 {101 萧峰 28} {102 段誉 20} {103 虚竹 26}
1={101 萧峰 28} 萧峰 2={102 段誉 20} 段誉 3={103 虚竹 26} 虚竹

2.1.7、切片渲染

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go中添加数组
	//切片渲染
	slices := []int{9,8,7,6,11,23}
	u.Data["slices"] = slices
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 修改user.html 增加如下内容
// 切片渲染
{{ range .slices }}
    {{.}}
{{end}}
<br>
{{ range $k,$v := .slices }}
    {{$v}}
{{end}}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 效果
// 切片渲染 9 8 7 6 11 23 9 8 7 6 11 23

2.1.8、小结

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
package controllers

import (
	beego "github.com/beego/beego/v2/server/web"
)

type MainController struct {
	beego.Controller
}

type UserStruct struct {
	Id int
	Name string
	Age int
}

type UserController struct {  //新增 UserController
	beego.Controller  //必须要添加的匿名字段
}

func (c *MainController) Get() {
	c.Data["Website"] = "icewake"
	c.Data["Email"] = "icewake@xx.com"
	c.TplName = "index.tpl"
}

func (u *UserController) Get(){
	u.TplName = "user.html"
	//u.Data["user"] = "小明"
	//u.Data["age"] = 18
	user1 := UserStruct{Id:1,Age:18,Name:"令狐冲"}
	ary := [...]int{7,8,10,11}
	aryStruct := [3]UserStruct{{Id:1,Age:55,Name:"岳不群"},{Id:2,Name:"岳灵珊",Age:20}}

	u.Data["aryStruct"]	 = aryStruct
	u.Data["user"] = user1
	u.Data["arry"] = ary

	//map渲染
	map01 := map[string]string{"name":"iceman","age":"18","email":"aa@icemail.com"}
	u.Data["map01"] = map01

	//结构体map渲染
	map02 := make(map[int]UserStruct)
	map02[1] = UserStruct{101,"萧峰",28}
	map02[2] = UserStruct{102,"段誉",20}
	map02[3] = UserStruct{103,"虚竹",26}
	u.Data["map02"] = map02

	//切片渲染
	slices := []int{9,8,7,6,11,23}
	u.Data["slices"] = slices
}
  • user.html
<!DOCTYPE html>
<title >Icewake</title>
<html lange="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
用户页面
{{/*ID : {{ .user.Id }}*/}}
用户名: {{ .user.Name }}
年龄:{{ .user.Age }}

<br>
====================================<br>
// 遍历方式1
{{ range .arry}}
    {{ .}}
{{ end}}
<br>
====================================<br>
// 遍历方式2
{{ range $i,$v := .arry}}
    <br>
    {{$i}}={{$v}}
{{ end}}
<br>
====================================<br>
// 结构体数组遍历1
{{ range .aryStruct}}
    <br>
    {{.}}
{{ end}}
<br>
====================================<br>
// 结构体数组遍历2
{{ range .aryStruct}}
    {{.Id}}
    {{.Name}}
    {{.Age}}
{{end}}
<br>
====================================<br>
// 结构体数组遍历3,会自动判别是一个参数还是两个参数接收
{{/*{{ range $i,$v := .aryStruct}}*/}}
{{ range $v := .aryStruct}}
    <br>
    {{ $v.Id}}     {{ $v.Name}}     {{ $v.Age}}
{{ end}}

<br>
====================================<br>
// map 遍历 方法1

{{ .map01.name }}
{{ .map01.age }}
{{ .map01.email }}
<br>
// map 遍历 方法2
{{ range  .map01}}
    {{.}}
{{ end }}
<br>
// map 遍历 方法3
{{ range $k,$v := .map01}}
    {{$k}}={{$v}}
{{ end }}

<br>
====================================<br>
// 数组map渲染1 - 这种方法是不行的
{{/*{{ .map02.1.Id}}*/}}
{{/*{{ .map02.2.Name}}*/}}
{{/*{{ .map02.3.Age}}*/}}

{{ range .map02 }}
    {{.}}
{{ end}}
<br>
{{ range $k,$v := .map02 }}
    {{$k}}={{$v}}
    {{$v.Name}}
{{ end}}
<br>
====================================<br>
// 切片渲染
{{ range .slices }}
    {{.}}
{{end}}
<br>
{{ range $k,$v := .slices }}
    {{$v}}
{{end}}


</body>
</html>
  • 效果
用户页面 用户名: 令狐冲 年龄:18
====================================
// 遍历方式1 7 8 10 11
====================================
// 遍历方式2
0=7
1=8
2=10
3=11
====================================
// 结构体数组遍历1
{1 岳不群 55}
{2 岳灵珊 20}
{0 0}
====================================
// 结构体数组遍历2 1 岳不群 55 2 岳灵珊 20 0 0
====================================
// 结构体数组遍历3,会自动判别是一个参数还是两个参数接收
1 岳不群 55
2 岳灵珊 20
0 0
====================================
// map 遍历 方法1 iceman 18 aa@icemail.com
// map 遍历 方法2 18 aa@icemail.com iceman
// map 遍历 方法3 age=18 email=aa@icemail.com name=iceman
====================================
// 数组map渲染1 - 这种方法是不行的 {101 萧峰 28} {102 段誉 20} {103 虚竹 26}
1={101 萧峰 28} 萧峰 2={102 段誉 20} 段誉 3={103 虚竹 26} 虚竹
====================================
// 切片渲染 9 8 7 6 11 23 9 8 7 6 11 23

2.2、静态文件

2.2.1、css使用

>>>>>>>>>>>>>>>>>>>>>>>>>>>> router.go
func init() {
    beego.Router("/", &controllers.MainController{})
	beego.Router("/static01", &controllers.StaticStruct{})  //新增,注意这里不能直接为 "/static"(可能是和内置的冲突) 会报错"403 forbiden
    beego.Router("/user", &controllers.UserController{})
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/router.go
...
type StaticStruct struct {
	beego.Controller
}

func (s *StaticStruct) Get(){
	s.TplName = "static02.html"
}
...

>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/static02.html
<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/static02.css">
</head>
<body>

<span style="color: #0000FF"> 测试静态文件-蓝色</span>
<span class="sy"> 试静态文件-红色</span>
<span id="syt"> 试静态文件-红色</span>

</body>
</html>

>>>>>>>>>>>>>>>>>>>>>>>>>>>> static/css/static_test.css #通过新建StyleSheet新建
.sy {
    color: #FD482C;
}

#syt {
    color: #FD482C;
}


<link rel="stylesheet" href="/static/css/static02.css"> 引入css文件,class标签对应 css的".sy",id标签,对应css文件中"#syt"标签

css更多用法:https://www.runoob.com/css/css-tutorial.html

2.2.2、js使用

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> static/js/static_test.js
alert("你好,世界")


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/static02.css">
    <script src="/static/js/static_test.js"></script>
</head>
<body>

<span style="color: #0000FF"> 测试静态文件-蓝色</span>
<span class="sy"> 试静态文件-红色</span>

</body>
</html>

<script src="/static/js/static_test.js"></script> 引用js文件,效果为一个弹窗提示“你好,世界”

更多js用法:https://www.runoob.com/js/js-tutorial.html

2.2.3、img使用

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> static/css/static02.css
.sy {
    color: #FD482C;
}

#syt {
    color: #FD482C;
}

.img_style {
    height: 300px;
    width: 400px;
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> static/img/xxxx.png
存放照片

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/static02.html
<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/static02.css">
    <script src="/static/js/static_test.js"></script>
</head>
<body>

<span style="color: #0000FF"> 测试静态文件-蓝色</span>
<span class="sy"> 试静态文件-红色</span>
<span id="syt"> 试静态文件-红色</span>
<br>


<img src="/static/img/Dingtalk_20211026100143.jpg" class="img_style">
<br>
<img src="/static/img/Dingtalk_20211026100143.jpg" class="img_style" style="width: 300px;height: 300px">

</body>
</html>

除了引用css样式调整图片外,也可以在html中设置属性

注意:如果修改后不能立即生效,就使用shift + f5 多是浏览器缓存。强制刷新缓存就可以看到效果了

设置static路径:

  • 方法1在main.go中设置: beego.SetStaticPath() ,注意要在beego.Run()之前设置
  • 方法2在conf/app.conf中设置:viewpath="存放路径"
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> main.go 
func main() {
	beego.SetStaticPath("/static","front")
    beego.SetStaticPath("/aaa","front")
 	beego.Run()
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> routers/router.go
func init() {
	beego.Router("/static01", &controllers.StaticStruct{})  //注意这里不能直接为 "/static"(可能是和内置的冲突) 会报错"403 forbiden
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  controller/default.go
type StaticStruct struct {
	beego.Controller
}

func (s *StaticStruct) Get(){
	s.TplName = "static02.html"
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/static02.html
<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/css/static02.css">
    <script src="/static/js/static_test.js"></script>
</head>
<body>

<span style="color: #0000FF"> 测试静态文件-蓝色</span>
<span class="sy"> 试静态文件-红色</span>
<span id="syt"> 试静态文件-红色</span>
<br>

<img src="/static/img/Dingtalk_20211026100143.jpg" class="img_style">
<br>
<img src="/static/img/Dingtalk_20211026100143.jpg" class="img_style" style="width: 300px;height: 300px">

</body>
</html>


注意:这里虽然 src="/static/js/static_test.js" 和href="/static/css/static02.css" 但是beego.SetStaticPath("/static","front") ,但是$src/beego_project/ 不存在 front目录,所以css和js不会展示出效果。

右键“static“目录,ctrl+c,ctrl+v 修改目录名为"front"就可以展示出来效果了

beego.SetStaticPath("/static","front")  //对于引用静态路径为 /static的映射到 front目录下
beego.SetStaticPath("/aaa","static")  //对于引用静态路径为 /aaa的映射到 static 路径下

2.3、前后端数据交互

Get请求的两种方式:http://127.0.0.1:8080/param?name=arg1http://127.0.0.1:8080/param/arg1

  • 方式1
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> routers/router.go
func init() {
	...
    beego.Router("/param", &controllers.ParamController{})
}


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
//新建controller
type ParamController struct {
	beego.Controller
}

func (p *ParamController) Get(){
	//针对 http://127.0.0.1:8080/param?name=令狐冲

	name := p.GetString("name")
	fmt.Println("===============================",name)  //获取方式1

	name1,_ := p.Input()

	v := name1.Get("name")
	fmt.Println("===============================",v)  //获取方式2

	p.TplName = "param_test.html"

}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
请求:http://127.0.0.1:8080/param/name=令狐冲 在日志中会打印出来结果

  • 方式2
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> routers/router.go
func init() {
	...
    beego.Router("/param/?:id", &controllers.ParamController{})
}


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
//新建controller
type ParamController struct {
	beego.Controller
}

func (p *ParamController) Get(){
	//针对 http://127.0.0.1:8080/param/id 方式 request2
	v1 := p.GetString(":id")
	fmt.Println("v1===============================",v1)
	v2,_ := p.Input()
	v3 := v2.Get(":name")
	fmt.Println("v3===============================",v3)  //获取方式3,获取不到值

	v4 := p.Ctx.Input.Param(":id")
	fmt.Println("v4===============================",v4)  //获取方式4,建议使用

	p.TplName = "param_test.html"

}


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
请求:http://127.0.0.1:8080/param/name=令狐冲 获取到的结果是
v1=============================== name=令狐冲
v3===============================
v4=============================== name=令狐冲


请求:http://127.0.0.1:8080/param/123456,结果
v1=============================== 123456
v3===============================
v4=============================== 123456
  • 混合请求
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  controller/default.go
type ParamController struct {
	beego.Controller
}

func (p *ParamController) Get(){
	//针对 http://127.0.0.1:8080/param/id 方式 request2
	v1 := p.GetString(":provience")
	v2 := p.GetString(":city")
	v3 := p.GetString("num")

	fmt.Printf("%v省%v市%v号\n",v1,v2,v3)
	p.TplName = "param_test.html"
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> routers/router.go
func init() {
    beego.Router("/param/?:provience/?:city", &controllers.ParamController{})
    //beego.Router("/param/?:id:int", &controllers.ParamController{})
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 请求
http://127.0.0.1:8080/param/河南7/郑州%?num=12306
打印:河南省郑州市12306号

2.4、Post请求

2.4.1、简单实现

>>>>>>>>>>>>>>>>>>>>>>> routers/router.go
func init() {
	...
    beego.Router("/param", &controllers.ParamController{})
}


>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
type ParamController struct {
	beego.Controller
}

func (p *ParamController) Get(){
	p.TplName = "param_test.html"
}

func (p *ParamController) Post(){
	//1、获取数据方法1
	input1 := p.GetString("username")
	input2 := p.GetString("userage")
	fmt.Printf("用户名: %v 年龄: %v\n",input1,input2)
	p.TplName = "success.html"

	//2、获取数据方法2
	v,_ := p.Input()
	s1 := v.Get("username")
	s2 := v.Get("userage")
	fmt.Printf("用户名: %v 年龄: %v\n",s1,s2)

}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/param_test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    测试页面

<form action="/param" method="post">
    用户名:&nbsp<input type="text" name="username"> <br>
    年&nbsp;&nbsp;&nbsp龄:&nbsp<input type="text" name="userage"> <br>
    <input type="submit" value="提交">
</form>

</body>
</html>


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/success.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    提交成功
</body>
</html>

效果,提交后会提示“提交成功”,
注意,这里写 <form action="/param" method="post"> 中的method为get或者post都可以解析

2.4.2、其他类型数据

>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
type ParamController struct {
	beego.Controller
}

func (p *ParamController) Get(){
	p.TplName = "param_test.html"
}

func (p *ParamController) Post(){
	input1 := p.GetString("username")
	users := p.GetStrings("username")  //匹配多个"username"

	input2,_ := p.GetInt8("userage")
	input3,_ := p.GetFloat("price")
	input4,_ := p.GetBool("is_true")
	fmt.Printf("用户名: %v 年龄: %v 价格: %v 已婚:%v\n",input1,input2,input3,input4)
	fmt.Println(users)

	p.TplName = "success.html"
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/param_test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    测试页面

<form action="/param" method="post">
    用户名:&nbsp<input type="text" name="username"> <br>
    用户名:&nbsp<input type="text" name="username"> <br>
    用户名:&nbsp<input type="text" name="username"> <br>
    年&nbsp;&nbsp;&nbsp龄:&nbsp<input type="text" name="userage"> <br>
    价&nbsp;&nbsp;&nbsp格:&nbsp<input type="text" name="price"> <br>
    已婚:&nbsp;&nbsp;&nbsp 是:<input type="radio" name="isTrue" value="true">
    否:<input type="radio" name="isTrue" value="false"><br>

    <input type="submit" value="提交">
</form>

</body>
</html>
注意,如果提交的数据类型不匹配会使用零值代替。

2.4.3、解析表单到结构体

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
type UserStruct struct {
	Id int
	Name string `form:"username"`
	Age int `form:"userage"`
	Married bool `form:"isTrue"`
}

//新建controller
type ParamController struct {
	beego.Controller
}

func (p *ParamController) Get(){
	p.TplName = "param_test.html"
}

func (p *ParamController) Post(){
	user := UserStruct{}
	input1 := p.GetStrings("price")
	if err := p.ParseForm(&user) ;err == nil {
		fmt.Printf("用户名: %v 年龄: %v 价格: %v 已婚:%v\n",user.Name,user.Age,input1,user.Married)
	} else {
		fmt.Println(err)
	}
	p.TplName = "success.html"
}


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/param_test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    测试页面

<form action="/param" method="post">
    用户名:&nbsp<input type="text" name="username"> <br>
    年&nbsp;&nbsp;&nbsp龄:&nbsp<input type="text" name="userage"> <br>
    价&nbsp;&nbsp;&nbsp格:&nbsp<input type="text" name="price"> <br>
    已婚:&nbsp;&nbsp;&nbsp 是:<input type="radio" name="isTrue" value="true">
    否:<input type="radio" name="isTrue" value="false"><br>

    <input type="submit" value="提交">
</form>

</body>
</html>

:html书写技巧,输入关键字,按住tab会自动补全格式,注意不需要手动写<

2.5、Ajax(有问题username一直渲染不出来,不知道哪里问题)

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行

前提config/app.conf 设置,copyrequestbody = true

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
package controllers

import (
	"encoding/json"
	"fmt"
	beego "github.com/beego/beego/v2/server/web"
)

type MainController struct {
	beego.Controller
}

type UserStruct struct {
	Id int
	Name string `form:"username"`
	Age int `form:"age"`
	Married bool `form:"isTrue"`
}

type UserController struct {  //新增 UserController
	beego.Controller  //必须要添加的匿名字段
}

func (c *MainController) Get() {
	c.Data["Website"] = "icewake"
	c.Data["Email"] = "icewake@xx.com"
	c.TplName = "index.tpl"
}


type StaticStruct struct {
	beego.Controller
}

func (s *StaticStruct) Get(){
	s.TplName = "static02.html"
}


//新建controller
type ParamController struct {
	beego.Controller
}

func (p *ParamController) Get(){
	p.TplName = "param_test.html"
}

func (p *ParamController) Post(){
	//user := UserStruct{}
	//input1 := p.GetStrings("price")
	//if err := p.ParseForm(&user) ;err == nil {
	//	fmt.Printf("用户名: %v 年龄: %v 价格: %v 已婚:%v\n",user.Name,user.Age,input1,user.Married)
	//} else {
	//	fmt.Println(err)
	//}
	//

	//p.TplName = "success.html"  //使用ajax提交不能再使用模板
	//获取ajax数据
	var user UserStruct
	body := p.Ctx.Input.RequestBody  //二进制的json数据
	err := json.Unmarshal(body,&user)
	fmt.Println("错误信息为:",err)

	fmt.Println(user)
	result := map[string]string{"code":"200","message":"解析成功"}

	p.Data["json"] = result
	p.ServeJSON()  //返回json格式
}



>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/param_test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/jquery.min.js"></script>
</head>
<body>
    测试页面

{{/*<form action="/param" method="post">*/}}
<form>
    用户名:&nbsp<input type="text" name="username" id="username"> <br>
    年&nbsp;&nbsp;&nbsp龄:&nbsp<input type="text" name="userage" id="userage"> <br>
    价&nbsp;&nbsp;&nbsp格:&nbsp<input type="text" name="price"> <br>
    已&nbsp;&nbsp;&nbsp婚:&nbsp;&nbsp;&nbsp;&nbsp 是<input type="radio" name="isTrue" value="true">
    否<input type="radio" name="isTrue" value="false"><br>

    <input type="button" value="提交" id="bt1">
</form>




<script>
    var bt = document.getElementById("bt1");
    bt.onclick = function (ev) {
        var username = document.getElementById("username").value;
        var age = document.getElementById("userage").value;
        alert(username)
        alert(userage)
        $.ajax({
            url:"/param",
            type:"POST",
            data:JSON.stringify({
                "username":username,
                "age":Number(age)
            }),
            dataType:"JSON",
            success:function (data) {
                alert("处理成功")
            },
            error:function (data) {
                alert("处理异常")
            }
        })
    }
</script>

</body>
</html>

2.6、其他格式数据传输

  • json
>>>>>>>>>>>>>>>>>>>>>>> routers/router.go
func init() {
	...
    beego.Router("/other", &controllers.OtherType{})
}


>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
type UserStruct struct {
	Id int
	Name string `form:"username"`
	Age int `form:"age"`
	Married bool `form:"isTrue"`
}

type OtherType struct {
	beego.Controller
}


func (o *OtherType) Get(){
	user := UserStruct{Id:1,Name:"令狐冲",Age:28}

	//json 方式
	//o.Data["json"] = &user
	//o.ServeJSON()

	//xml方式
	//o.Data["xml"] = &user
	//o.ServeXML()

	//jsonp格式
	//o.Data["jsonp"] = &user
	//o.ServeJSONP()

	//yaml格式
	o.Data["yaml"] = &user
	o.ServeYAML()

}

>>>>>>>>>>>>>>>>>>>>>>>>> web界面 http://127.0.0.1:8080/other
{
  "Id": 1,
  "Name": "令狐冲",
  "Age": 28,
  "Married": false
}
  • xml
web访问:http://127.0.0.1:8080/other

<UserStruct>
<Id>1</Id>
<Name>令狐冲</Name>
<Age>28</Age>
<Married>false</Married>
</UserStruct>
  • yaml
web访问:http://127.0.0.1:8080/other  会下载一个文件

id: 1
name: 令狐冲
age: 28
married: false

2.7、flash动作

需求:用户在控制台传递用户名和密码,判断用户名和密码,成功则跳转到成功页面,失败则跳转到错误页面,并给出提示信息。使用flash实现

>>>>>>>>>>>>>>>>>>>>>>> routers/router.go
func init() {
	...
    beego.Router("/flashdata", &controllers.FlashController{})
}

  • controller代码
type FlashController struct {
	beego.Controller
}

func (f *FlashController) Get(){
	flash := beego.ReadFromRequest(&f.Controller)
	notice := flash.Data["notice"]
	err := flash.Data["error"]

	if len(notice) != 0 {
		f.TplName = "success.html"
	} else if len(err) != 0  {
		f.TplName = "error.html"
	} else {
		f.TplName = "flash.html"
	}
}

func (f *FlashController) Post(){
	//如果判断用户名和密码不对,如果在前端提示错误?
	flash := beego.NewFlash() //初始化flash

	username := f.GetString("username")
	pwd := f.GetString("pwd")
	fmt.Println(username,pwd)

	/*
	flash对象有三个级别,
		Notice 提示信息,对应模板中 {{.flash.notici }}
		Warning 警告 对应模板中 {{ .flash.warining }}
		Error 错误信息 对应模板中 {{ .flash.error }}
	 */

	if strings.Contains(username," ") || len(username) == 0 {
		fmt.Println("用户名不能为空或者包含空格")
		flash.Error("用户为不能为空或者包含空格")
		flash.Store(&f.Controller)
		f.Redirect("/flashdata",302)
	} else  if pwd != "123456" {
		fmt.Println("密码错误")
		flash.Error("密码错误")
		flash.Store(&f.Controller)
		f.Redirect("/flashdata",302)
	} else {
		flash.Notice("登录成功")
		flash.Store(&f.Controller)  //保存数据
		f.Redirect("/flashdata",302)
	}

}
  • 前端页面
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/succeess.html
package controllers

import (
	"encoding/json"
	"fmt"
	beego "github.com/beego/beego/v2/server/web"
	"strings"
)

type MainController struct {
	beego.Controller
}

type UserStruct struct {
	Id int
	Name string `form:"username"`
	Age int `form:"age"`
	Married bool `form:"isTrue"`
}

type UserController struct {  //新增 UserController
	beego.Controller  //必须要添加的匿名字段
}

func (c *MainController) Get() {
	c.Data["Website"] = "icewake"
	c.Data["Email"] = "icewake@xx.com"
	c.TplName = "index.tpl"
}

type StaticStruct struct {
	beego.Controller
}

func (s *StaticStruct) Get(){
	s.TplName = "static02.html"
}


//新建controller
type ParamController struct {
	beego.Controller
}

func (p *ParamController) Get(){
	p.TplName = "param_test.html"
}

func (p *ParamController) Post(){
	//user := UserStruct{}
	//input1 := p.GetStrings("price")
	//if err := p.ParseForm(&user) ;err == nil {
	//	fmt.Printf("用户名: %v 年龄: %v 价格: %v 已婚:%v\n",user.Name,user.Age,input1,user.Married)
	//} else {
	//	fmt.Println(err)
	//}
	//

	//p.TplName = "success.html"  //使用ajax提交不能再使用模板
	//获取ajax数据
	var user UserStruct
	body := p.Ctx.Input.RequestBody  //二进制的json数据
	err := json.Unmarshal(body,&user)
	fmt.Println("错误信息为:",err)

	fmt.Println(user)
	result := map[string]string{"code":"200","message":"解析成功"}

	p.Data["json"] = result
	p.ServeJSON()  //返回json格式
}


type OtherType struct {
	beego.Controller
}


func (o *OtherType) Get(){
	user := UserStruct{Id:1,Name:"令狐冲",Age:28}

	//json 方式
	//o.Data["json"] = &user
	//o.ServeJSON()

	//xml方式
	//o.Data["xml"] = &user
	//o.ServeXML()

	//jsonp格式
	//o.Data["jsonp"] = &user
	//o.ServeJSONP()

	//yaml格式
	user2 := struct   {
		id int
		address string
		info UserStruct
	}{10001,"河南省郑州市",user}

	o.Data["yaml"] = &user2
	o.ServeYAML()

}

type FlashController struct {
	beego.Controller
}


func (f *FlashController) Get(){
	flash := beego.ReadFromRequest(&f.Controller)
	notice := flash.Data["notice"]
	err := flash.Data["error"]

	if len(notice) != 0 {
		f.TplName = "success.html"  //成功页面
	} else if len(err) != 0  {
		f.TplName = "error.html"  //错误页面
	} else {
		f.TplName = "flash.html"  //默认GET 界面
	}
}

func (f *FlashController) Post(){
	//如果判断用户名和密码不对,如果在前端提示错误?
	flash := beego.NewFlash() //初始化flash

	username := f.GetString("username")
	pwd := f.GetString("pwd")
	fmt.Println(username,pwd)

	/*
	flash对象有三个级别,
		Notice 提示信息,对应模板中 {{.flash.notici }}
		Warning 警告 对应模板中 {{ .flash.warining }}
		Error 错误信息 对应模板中 {{ .flash.error }}
	 */

	if strings.Contains(username," ") || len(username) == 0 {
		fmt.Println("用户名不能为空或者包含空格")
		flash.Error("用户为不能为空或者包含空格")
		flash.Store(&f.Controller)
		f.Redirect("/flashdata",302)
	} else  if pwd != "123456" {
		fmt.Println("密码错误")
		flash.Error("密码错误")
		flash.Store(&f.Controller)
		f.Redirect("/flashdata",302)
	} else {
		flash.Notice("登录成功")
		flash.Store(&f.Controller)  //保存数据
		f.Redirect("/flashdata",302)
	}

}


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/error.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{.flash.error}}
</body>
</html>


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> views/flash.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="/flashdata" method="post">
    <input type="text" name="username"><br>
    <input type="text" name="pwd"><br>
    <input type="submit" name="提交">
</form>

</body>
</html>

三、Controller

3.1、配置文件读取

app.conf中设置的参数,可以在项目中使用类似port,_ := beego.AppConfig.Int("httpport")的方式读取。

>>>>>>>>>>>>>>>>>>>> confg/app.conf
appname = beego_project
httpport = 8080
runmode = dev
copyrequestbody = true  //用户区分运行模式,可以对不同的运行模式配置不同的参数值
include "mysql.conf" //引入其他配置文件
mysql_port = "3333"

[dev]
username = dev
passwd = dev

[test]
username = test
passwd = test

>>>>>>>>>>>>>>>>>>>> confg/msyql.conf ,和主配置中参数重复时,采用主配置文件的
mysql_host = "127.0.0.1"
mysql_port = "3306"
>>>>>>>>>>>>> main.go代码
package main

import (
	_ "beego_project/routers"
	"fmt"
	beego "github.com/beego/beego/v2/server/web"
)

func main() {
	port,_ := beego.AppConfig.Int("httpport")
	uname,_ := beego.AppConfig.String("username")
	mode,_ := beego.AppConfig.String("runmode")
	msyql_port,_ := beego.AppConfig.String("mysql_port")
	msyql_host,_ := beego.AppConfig.String("msyql_host")

	fmt.Println("监听端口是:",port,"用户名:",uname,"运行环境:",mode)  //结果为: 监听端口是: 8080 用户名: dev 运行环境: dev
	fmt.Printf("mysql地址:%v 端口:%v\n",msyql_host,msyql_port) //mysql地址:127.0.0.1 端口:3333
    
	beego.Run()
}

更多的配置

3.2、Routers配置

  • 固定路由
    beego.Router("/", &controllers.MainController{})
	beego.Router("/static01", &controllers.StaticStruct{})  //注意这里不能直接为 "/static"(可能是和内置的冲突) 会报错"403 forbiden
    beego.Router("/user", &controllers.UserController{})

  • 正则路由
beego.Router("/param/?:provience/?:city", &controllers.ParamController{}) //对于http://127.0.0.1:8080/param/河南/郑州,会把 河南保存到proviece上,把 郑州保存到city上。 ?的含义是出现1或者0次,对于该条http路由条目,加上? 对于http://127.0.0.1:8080/param的也会匹配,不加? 则对对于http://127.0.0.1:8080/param 提示 404

/param/?:id:int //限制为int类型,如果不是int则提示 404,其他类型同理

:id[0-9]+ 或者 :id([\d]+) 或者 :id:int 都表示匹配int类型
:username([\w]+) 或者 :username:string  表示string

获取:this.GetString(":username"),this.Ctx.Input.Param(":id")

. 匹配除换行符的任意字符
\w 匹配字母数字或下划线 等价于[^A-Za-z0-9_]
\d 等价于数字
  • 自动路由

注册路由时不需要指定url,只需要注册控制器即可,自动匹配

>>>>>>>>>>>>>>>>>>>>>>>>>>>> controllers/default.go
beego.AutoRouter( &controllers.ParamController{})  //注册自动路由

使用的时候需要按照规则来。
	/控制器名/方法名/后面的都是参数 ,比如: http://127.0.0.1:8080/param/get
	对于 controllers.ParamController{} 则 控制器名为 param ,方法名看后端定义了几种方法
  • 自定义路由

    注册自定义路由的时候可以指定第三个参数,这个参数就是用来自定义路由的。

    • 用法:method,函数名
      • post:Login post请求的时候访问Login函数
      • get:User get请求的时候访问User函数
      • *.LoginOut 所有请求方法都访问LoginOut函数
      • put:UpdateFile put请求的时候访问UpdateFile函数
      • get,post:Login get和post请求的时候访问Login函数 和 get:Login,post:Login 同义
    • 可用的http方法
      • get,post,put,delete,patch
beego.Router("/param",&controllers.ParamController{},"get:Get1")  //对于前端的get请求,映射到后端的Get1方法

3.3、获取请求方法和终止逻辑

>>>>>>>>>>>>>>>>>>>>>>>> routers/router.go
func init() {
	...
	beego.Router("/test_router/?:id:int",&controllers.RouterControllers{},"get,post:Get")
	//把get和post请求都映射到 get方法中
}

>>>>>>>>>>>>>>>>>>>>>>>> views/test_router_post.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    test_router 测试界面

<form action="/test_router" method="post">
    <input type="submit" value="提交">
</form>

</body>
</html>

>>>>>>>>>>>>>>>>>>>>>>>> views/test_routert.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    test_router post 请求 测试界面
</body>
</html>

>>>>>>>>>>>>>>>>>>>>>>>> controllers/test_router.go
package controllers

import (
	"fmt"
	beego "github.com/beego/beego/v2/server/web"
)

type RouterControllers struct {
	beego.Controller
}


func (r *RouterControllers) Get() {
	if  r.Ctx.Request.Method == "POST" {
		//r.StopRun()  //不继续往下执行,加上 get界面的提交按钮,会 白屏,一般用在用户认证失败...
		r.TplName = "test_router_post.html"

	} else {
		r.TplName = "test_router.html"
	}

	id := r.Ctx.Input.Param(":id")
	fmt.Println("========================>")
	fmt.Println(id)
}

3.4、跨站请求伪造XSRF

防护XSRF的方式:post请求之类伪造请求

  • 定义:每一个用户一个cookie,所有请求都需要验证这个cookie,如果没有这个cookie,则被认为跨站请求伪造
  • 挟持用户在当前已经登录的Web应用程序上执行非本意的操作的攻击方法
  • XSRF利用的是网站对用户网页浏览器的信任

配置文件配置:

enablexsrf = true
xsrfkey = jdijfiblnlxmljodksjiDj
xsfrexpire = 3600 //单位秒,默认1h

代码中配置

beego.BConfig.WebConfig.EnableXSRF = true
beego.BConfig.WebConfig.XSRFKey = "djfidjsbcmJbdcAZZZjdf"
beego.BConfig.WebConfig.XSRFExpire = 360 

3.4.1、开启效果展示

  • Controller
package controllers

import beego "github.com/beego/beego/v2/server/web"

type XsfrController struct {
	beego.Controller
}


func (x *XsfrController) Get(){
	x.TplName = "test_xsrf.html"
}

func (x *XsfrController) Post(){
	x.TplName = "test_xsrf_success.html"
}
  • 页面
>>>>>>>>>>>>>>>>>>>>>> test_xsrf_success.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
xsfr 成功页面
</body>
</html>


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> test_xsrf.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    xsrf测试界面
<form  action="/xsrf" method="post">
    <input type="submit" value="提交">
</form>
</body>
</html>
  • router
func init() {

	beego.Router("/xsrf",&controllers.XsfrController{})
}

  • main.go
func main() {
	...
	beego.BConfig.WebConfig.EnableXSRF = true
	beego.Run()
}

页面点击提交后,报错

这里没有传递 xsrf,所以会报错

3.4.2、form表单的xsrf

  • controller 修改
type XsfrController struct {
	beego.Controller
}

func (x *XsfrController) Get(){
	x.Data["xsrfdata"] = template.HTML(x.XSRFFormHTML())  //传递数据到form表单中
	x.TplName = "test_xsrf.html"
}

func (x *XsfrController) Post(){
	x.TplName = "test_xsrf_success.html"
}
  • html页面调整
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    xsrf测试界面
<form  action="/xsrf" method="post">
    {{.xsrfdata}}
    <input type="submit" value="提交">
</form>
</body>
</html>

3.4.3、controller级别关闭xsrf

  • 全局开启
// main.go 
func main() {
	beego.BConfig.WebConfig.EnableXSRF = true
	beego.BConfig.WebConfig.XSRFKey = "ABCDEFGabcdefghijkmz" //默认key beegoxsrf, 基于key进行加密
	beego.BConfig.WebConfig.XSRFExpire = 10                  //默认3600s
	beego.Run()

}

  • controller级别关闭
package controllers

import (
	beego "github.com/beego/beego/v2/server/web"
	"html/template"
)

type XsfrController struct {
	beego.Controller
}

func (x *XsfrController) Get(){
	x.Data["xsrfdata"] = template.HTML(x.XSRFFormHTML())  //传递数据到form表单中
	x.TplName = "test_xsrf.html"
}

func (x *XsfrController) Post(){
	x.TplName = "test_xsrf_success.html"
}

func (x *XsfrController) Prepare(){  //controller实现Prepare方法,和实现GET或者POST方法一样
	x.EnableXSRF = false //controller级别关闭
	x.TplName = "test_xsrf_success.html"
}

3.5、文件上传

3.5.1、form表单方式上传

  • router
func init() {
	...
	beego.Router("/upload",&controllers.UploadController{})
	
}
  • main.go
func main() {
	...
    beego.BConfig.WebConfig.EnableXSRF = true
	beego.BConfig.WebConfig.XSRFKey = "ABCDEFGabcdefghijkmz" //默认key beegoxsrf, 基于key进行加密
	beego.BConfig.WebConfig.XSRFExpire = 30                  //默认3600s
	beego.Run()
}
  • upload controller
package controllers

import (
	"fmt"
	beego "github.com/beego/beego/v2/server/web"
	"html/template"
	"strconv"
	"time"
)

type UploadController struct {
	beego.Controller
}

func (u *UploadController) Get(){
	u.Data["xsrfdata"] = template.HTML(u.XSRFFormHTML())  //记得传递xsrfdata
	u.TplName = "upload.html"
}

func (u *UploadController) Post(){
	//获取请求数据 ,f是文件的fd,h对应文件的描述信息
	f,h,err := u.GetFile("upload_file")
	defer f.Close()

	if err != nil {
		fmt.Println("ERROR,发生了错误",err)
		return
	}
	fileName := h.Filename
	fmt.Println("文件名:",fileName)
	//保存到本地

	//文件名添加时间戳,方式文件名上传重复覆盖
	t1 := time.Now().Unix()
	t2 := strconv.FormatInt(t1,10)
	u.SaveToFile("upload_file","static/upload/"+(t2+"_"+fileName))  //upload为新建目录

	u.TplName = "success.html"
}
  • upload.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
上传页面

<form action="/upload" method="post" enctype="multipart/form-data">
    {{.xsrfdata}}
    <input type="file" name="upload_file"><br>
    <input type="submit" value="提交">
</form>


</body>
</html>

3.5.2、ajax上传文件

需要使用Fromdata,可以通过js用一些键值来模拟一系列表单控件

  • var formData = new FormData();

  • 此时可以调用append()方法来添加数据

  • main.go

package main

import (
	_ "beego_project/routers"
	beego "github.com/beego/beego/v2/server/web"
)

func main() {
	...
	beego.BConfig.WebConfig.EnableXSRF = true
	beego.BConfig.WebConfig.XSRFKey = "ABCDEFGabcdefghijkmz" //默认key beegoxsrf, 基于key进行加密
	beego.BConfig.WebConfig.XSRFExpire = 30                  //默认3600s
	beego.Run()
}
  • controller
package controllers

import (
	"fmt"
	beego "github.com/beego/beego/v2/server/web"
	"html/template"
	"strconv"
	"time"
)

type UploadController struct {
	beego.Controller
}

func (u *UploadController) Get(){
	u.Data["xsrfdata"] = template.HTML(u.XSRFFormHTML())  //记得传递xsrfdata
	u.TplName = "upload.html"
	//u.TplName = "ajax_upload.html"
}

func (u *UploadController) Post(){
	//获取请求数据 ,f是文件的fd,h对应文件的描述信息
	f,h,err := u.GetFile("upload_file")
	defer f.Close()

	if err != nil {
		fmt.Println("ERROR,发生了错误",err)
		return
	}
	fileName := h.Filename
	fmt.Println("文件名:",fileName)
	//保存到本地

	//文件名添加时间戳,方式文件名重复上传
	t1 := time.Now().Unix()
	t2 := strconv.FormatInt(t1,10)
	u.SaveToFile("upload_file","static/upload/"+(t2+"_"+fileName))  //upload为新建目录

	//u.TplName = "success.html"
	u.Data["json"]  = map[string]string{"code":"200","meesage":"操作成功"}
	u.ServeJSON()
}

func (u *UploadController)  Prepare() {  //这里不直到为何必须得关闭,开启后上传就会http 422,可能是html页面发现的问题
	u.EnableXSRF = false
}
  • upload.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/jquery.min.js"></script>
</head>
上传页面
<body>

<form>
    {{.xsrfdata}}  <!-- 获取get请求时后端传过来的xsrf防护的key -->
    <input type="file" name="upload_file" id="upload_file"><br>
    <input type="button" value="提交" id="btn">
</form>


<script>
    var btn = document.getElementById("btn");
    btn.onclick = function (ev) {  //在btn这个事件发生后,执行的动作
        console.log($("upload_file"));  //
        var formData = new FormData();
        formData.append("upload_file",$("#upload_file")[0].files[0]);  //#根据id获取,
         $.ajax({
                 url:"/upload",
                 type:"POST",
                 data:formData,
                 contentType:false,
                 processData:false,
             success:function (data) {  //回调函数,等待服务端返回
                 if (data["code"] == "200") {
                     alert(data["meesage"])
                 }
             },
             error:function (data) {
                 alert(data["meesage"])
             }
         })
    }
</script>

</body>
</html>

ajax暂不做下关系了解,有时再操作。

3.5.3、其他

文件上传之后一般是放在内存中的,可以设置缓存中内存大小;

  • main.go beego.BConfig.MaxMemory = 1 <22 为1GB
  • 配置文件中配置:maxmemmory= 1<22 单位是B
  • 默认的缓存内存是64M

3.6、session和cookie

Session:

  • sessions是在无状态的HTTP协议下,服务端记录用户状态时用于标识具体用户的机制
  • 它是在服务端保存的用来跟踪用户的状态的数据结构,可以保存在文件,数据库或者集群中
  • 在浏览器关闭后这次的session就消失了,下次打开就不再拥有session,其实并不是Session消失了,而是session id变了

Cookie:

  • Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息
  • 每次HTTP请求时,客户端都会发送相应的Cookie信息到服务端,它的过期时间可以任意设置,如果你不主动清除它,在很长一段时间都可以保存着,即便中间把电脑关机了

session和cookie

  • Cookie在客户端(浏览器),session在服务端
  • Cookie的安全性一般,他人可以通过分析存放在本地的cookie并进行cookie欺骗。在安全性第一的前提下,选择session更优,重要交互信息比如权限等要放在session中,一般的信息放在cookie就好了
  • 单个cookie的保存的数据大小不能超过4k,很多浏览器限制一个站点最多保存20个cookie
  • Session可以放在文件、数据库或内存中
  • 用户验证这种场合一般会用Session。因此,维持一个会话的核心就是客户端的唯一标识,即session id
  • session的运行机制依赖Session id。而session id是存放在Cookie中的,也就是说。如果浏览器用了Cookie,session也会失效(但是可以通过其他方式实现,比如在url中传递Session id)

beego中使用的session:

  • 开启方式:
    • main.go: beego.BConfig.WebConfig.Session.SessionOn = true
    • 配置文件中:sessionon = true
  • 设置Sesion:
    • beego.BConfig.WebConfig.Session.SessionCookieLifeTime 超时Cookie过期时间
    • beego.BConfig.WebConfig.Session.SessionGCMaxLifetime 设置session过期时间,默认3600s
    • beego.BConfig.WebConfig.Session.SessionName 设置cookie名字,session默认是在浏览器cookie里的,默认名是beegosessionID,配置文件对应的参数名是:sessionName
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> UploadController 负责设置session
type UploadController struct {
	beego.Controller
}

func (u *UploadController) Get(){
	//设置session
	u.SetSession("username","icewake")
	
	u.TplName = "upload.html"
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> XsfrController 负责查看和删除session
type XsfrController struct {
	beego.Controller
}

func (x *XsfrController) Get(){
	//打印session
	fmt.Println("删除前============================>",x.GetSession("username"))
	x.DelSession("username")
	fmt.Println("删除后============================>",x.GetSession("username"))

	x.TplName = "test_xsrf.html"
}

测试的时候,先访问UploadController,然后再访问XsrfController看效果

3.7、过滤器(作业没有实现)

对没有满足条件的请求进行拦截

func InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt) *HttpServer { return BeeApp.InsertFilter(pattern, pos, filter, opts...) }

  • 第一个参数:匹配的路由规则,支持通配符
  • 第二个参数:过滤器的未知,beego支持的5种
    • BeforeStatic: 静态地址之前
    • BeforeRouter: 寻找路由之前
    • BeforeExec: 找到路由之后,开始执行相应的Controller之前
    • AfterExec:执行完controller逻辑之后执行的过滤器
    • FinishRouter:执行完逻辑之后执行的过滤器
  • 第三个函数:
    • func(*context.Context) 参数必须context.Context
  • 第四个参数:
    • 第一个设置 returnOnOutPut的值,默认true,即如果有输出是否跳过其他过滤器,默认只要有输出就不再执行其他过滤器,即执行完controller之后不会执行后面的过滤器
    • 第二个设置表示是否重置过滤器的参数,默认是false

注意:使用session和Filter必须在BeforeStatic之后才能获得,因为session没有在这之前初始化。

示例:如果session不正确或者没有session就跳转到login界面,有问题

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> mian.go 逻辑
func main() {
    ...
	beego.BConfig.WebConfig.Session.SessionOn = true
	beego.InsertFilter("/*",beego.BeforeRouter,controllers.Filter_user)
	beego.Run()
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> router
func init() {
	...
	beego.Router("/upload",&controllers.UploadController{})
	beego.Router("/ajax_upload",&controllers.AjaxUploadController{})  
	beego.Router("/login",&controllers.LoginController{})  
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LoginController
package controllers

import (
	beego "github.com/beego/beego/v2/server/web"
)

type LoginController struct {
	beego.Controller
}

func (l *LoginController) Get() {
	l.SetSession("username","icewake")
	l.TplName = "login.html"
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> testFilter逻辑
package controllers

import (
	"fmt"
	context "github.com/beego/beego/v2/server/web/context"
)

//如果不携带session,或者session不对,则跳转到login界面
func Filter_user(ctx *context.Context) {
	login := LoginController{}
	fmt.Printf("===========>%v======%T\n",login,login)
	if login.GetSession("username") != "icewake" && 	ctx.Request.RequestURI != "/login" {  //这里有问题,待确认
		ctx.Redirect(302,"/login")
	}
}

3.8、URL反转

动态构建url,就是根据方法名生成对应的url,原来是通过url找到对应的方法,现在我们要通过方法找到对应的url,这就是url反转,带来的好处就是:url改变不需要改变代码

beego.Router("/login",&controllers.LoginController{}) 正常是根据url /login找到对应的controller,现在反过来根据 controllers.LoginController{}找到对应的/login

  • controller
type UploadController struct {
	beego.Controller
}

func (u *UploadController) Get(){
	fmt.Println("++++++++++++++++++++++++++++++++")  //UploadController 反转url到 LoginController
	fmt.Println(beego.URLFor("LoginController.Get"))  //打印内容为"/login"

	u.TplName = "upload.html"
}
  • html前端反转
<a href="{{urlfor "LoginController.GET"}}">URL反转</a>

带参数的url反转

修改controller部分内容为:
	fmt.Println(beego.URLFor("LoginController.Get","name","icewake","age",20))  //打印内容为/login?name=icewake&age=20
前端页面调整为:
	<a href="{{urlfor "LoginController.GET" "name" "icewake" "age" 18}}">URL反转</a>

3.9、表单数据校验

3.9.1、合法性校验

  • validta.go
package controllers

import (
	"fmt"
	"github.com/beego/beego/v2/core/validation"
	beego "github.com/beego/beego/v2/server/web"
)

type ValiddataController struct {
	beego.Controller
}

func (v *ValiddataController) Get() {
	v.TplName = "validata.html"
}


func (v *ValiddataController) Post() {
	username := v.GetString("username")
	age := v.GetString("age")
	email := v.GetString("email")
	phone := v.GetString("phone")
	fmt.Println(username,age,email,phone)

	valid := validation.Validation{}
	//校验数据,数据为空校验
	valid.Required(username,"是必填字段")  //第二个key是提示的字段名,如果没有传递提示"用户名是必填字段-- Can not be empty"
	valid.Required(age,"是必填字段")
	valid.Required(email,"Email").Message("邮箱地址不能为空!") //自定义meeage内容,如果email为空,默认提示信息为 "Can not be empty"
	valid.Mobile(phone,"Phone").Message("号码不合法")

	valid.Email(email,"Email").Message("格式不合法")
	//还可以校验IP、Min、max、范围、长度、数字、AlphaNum、Match、Alpha、Tel固话
	//AlphaNumeric字符或数字、AlphaDash字符或数字或_,-

	if valid.HasErrors()   {
		for _,er :=  range valid.Errors {
			fmt.Printf("%s--%s\n",er.Key,er.Message)
			//示例:age是必填字段-- Can not be empty
		}
	}
	v.TplName = "validata_post.html"
}
  • validata.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="/vailddata" method="post">
    用户名: <input type="text" name="username"><br>
    年龄: <input type="text" name="age"><br>
    Email: <input type="text" name="email"><br>
    电话: <input type="text" name="phone"><br>
    <input type="submit" value="提交">
</form>

</body>
</html>

3.9.2、结构体校验

解析前端数据到结构体并校验

  • 校验方式1,使用上一节的方式
package controllers

import (
	"fmt"
	"github.com/beego/beego/v2/core/validation"
	beego "github.com/beego/beego/v2/server/web"
)

type ValiddataController struct {
	beego.Controller
}

func (v *ValiddataController) Get() {
	v.TplName = "validata.html"
}

type Userstruct struct {
	Username string `form:"username"`
	Age int `form:"age"`
	Email string  `form:"email"`
	Phone  int `form:"phone"`
}

func (v *ValiddataController) Post() {
	user := Userstruct{}
	v.ParseForm(&user)
	fmt.Println("==========>",user)

	valid := validation.Validation{}
	//校验数据,数据为空校验
	valid.Required(user.Username,"用户名").Message("不能为空")
	valid.Required(user.Age,"年龄").Message("不能为空")
	valid.Required(user.Email,"邮箱").Message("不能为空")
	valid.Required(user.Phone,"电话").Message("不能为空")

	if valid.HasErrors()   {
		for _,er :=  range valid.Errors {
			fmt.Printf("%s--%s\n",er.Key,er.Message)
		}
	}
	v.TplName = "validata_post.html"
}
  • 校验方式2:使用struct tag的方式校验
    • 验证函数写在valid:tag的标签里
    • 各个函数之间使用;分隔,分号后可以有空格
    • 参数用"()"括起来,多个参数之间用","分开,逗号后可以有空格
    • 正则函数(Match)的匹配模式用两斜杠"/"括起来
    • 各个函数的结果的key值为字段名.验证函数名
package controllers

import (
	"fmt"
	"github.com/beego/beego/v2/core/validation"
	beego "github.com/beego/beego/v2/server/web"
)

type ValiddataController struct {
	beego.Controller
}

func (v *ValiddataController) Get() {
	v.TplName = "validata.html"
}

type Userstruct struct {
	Username string `form:"username" valid:"Required"`  //多个标签使用 空格分开,form标签用于从html中解析post请求
	Age int `form:"age" valid:"Required"`
	Email string  `form:"email" valid:"Email"`
	Phone  int `form:"phone" valid:"Phone"`
}


func (v *ValiddataController) Post() {
	//重写错误信息
	var message = map[string]string {
		"Required": "不能为空",
		"MinSize": "最短长度不能为 %d",
		"Length": "长度必须为 %d",
		"Numeric": "必须是有效的数字",
		"Email": "必须是有效的邮箱",
		"Mobile": "必须是有效的手机号",
	}

	user := Userstruct{}
	v.ParseForm(&user)
	fmt.Println("==========>",user)

	valid := validation.Validation{}
	validation.SetDefaultMessage(message)

	//校验方式2:使用struct tag的方式
	b,err := valid.Valid(&user)  //b表示是否验证通过,err是 要解析的结构体是否有问题
	fmt.Println("b,err======>",b,err)

	if !b {  //如果有错误
		for _,er :=  range valid.Errors {
			fmt.Printf("%s--%s\n",er.Key,er.Message)
		}
	}
	v.TplName = "validata_post.html"
}

3.9.3、错误处理

自定义错误,需要重写controller

  • controllers/test_abort.go
package controllers

import (
	beego "github.com/beego/beego/v2/server/web"
)

type AbortController struct {
	beego.Controller
}

func (a *AbortController) Get() {
	a.Abort("700")  //如果在 tplName之前执行了 Abort,则不会显示 templatename指定的页面看,可以自定义
	//默认的401或者其他 错误界面,在 beego/server/web/error.go的 中定义,内容如下

	/*
	func unauthorized(rw http.ResponseWriter, r *http.Request) {
		responseError(rw, r,
			401,
			"<br>The page you have requested can't be authorized."+
				"<br>Perhaps you are here because:"+
				"<br><br><ul>"+
				"<br>The credentials you supplied are incorrect"+
				"<br>There are errors in the website address"+
				"</ul>",
		)
	}
	 */

	a.TplName = "login.html"
}

type ErrorController struct {
	beego.Controller
}

func(e *ErrorController) Error700() {  //其他的状态码同理,也可以这样玩儿
	e.Data["message"] = "数据库链接错误"
	e.TplName = "db_error.html"
}
  • main.go
func main() {
	...

	beego.ErrorController(&controllers.ErrorController{}) //注册error controller
	beego.Run()
}
  • db_error.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{.message}}
</body>
</html>
  • abort.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    abort页面
</body>
</html>

router.go中添加路由

四、Views

4.1、模板基础用法

go统一使用了{{ 和 }} 作为html中引用后端数据的左右标签,可以修改为其他的符号

在main.go中设置为其他字符
func main(){
	...
    beego.BConfig.WebConfig.TemplateLeft = "##"
    beego.BConfig.WebConfig.TemplateRight = "##"
    bee.Run()
}
  • 基本语法:

    • .表示当前位置的上下文
    • $引用当前模板根级的上下文
    • $.引用模板中根级的上下文
  • go语言支持的符号

    • 字符串: {{ "icewake" }}
    • 原始字符串: "{{ 反引号 icewake 反引号}}"
    • 字节类型:{{ 'a'}} --> 97
    • nil: {{ print nil}} 只有nil会报错,nil is not a command
  • pipeline: 可以是上下文的变量,也可以是函数通过管道传递的返回值

    • {{ .Age }} 是上下文变量的输出,是个pipeline
    • {{ "0000" | len }} 是函数通过管道传递的返回值,是个pipeline
    • pipeline会被认为是空的情况,当pipeline的值等于:
      • false 或 0
      • nil的指针或interface
      • 长度为0的array,slice,map,string
  • 基础函数:

    • eq/ne/lt/le/gt/ge ;eq不同于其他函数,支持多个参数 {{ if eq true .Var1 .Var2 .Var3 }} {{end}}

view1.go

package controllers

import (
  beego "github.com/beego/beego/v2/server/web"
)

type TemplateController struct {
  beego.Controller
}

func (t *TemplateController) Get() {
  t.Data["name"] = "icewake"
  t.Data["value"] = []int{1,2,3,4,5,6}
  t.TplName = "template1.html"
}

template1.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
前端语法1: ## .name ## <br>
前端语法2:
## range .value ##
  ## . ##
## end ## <br>
前端语法3(引用当前模板根):
## range $i,$v := .value ##
  ## $i ## =  ## $v ##
## end ## <br>
前端语法4(引用根):
## range .value ##
## $.value ##
## end ## <br>
5、支持go语言的符号: <br>
字符串:## "符号1" ## <br>
原始字符串:## "\\符号1" ##<br>
原始字符串:## `\\符号1` ##<br>
字节: ## 'a' ## <br>
NIL: ## print nil ## <br>
6、PIPELINE管道:<br>
求长度:## "icewake" | len ## <br>

</body>
</html>

web结果:

前端语法1: icewake
前端语法2: 1 2 3 4 5 6
前端语法3(引用当前模板根): 0 = 1 1 = 2 2 = 3 3 = 4 4 = 5 5 = 6
前端语法4(引用根): [1 2 3 4 5 6] [1 2 3 4 5 6] [1 2 3 4 5 6] [1 2 3 4 5 6] [1 2 3 4 5 6] [1 2 3 4 5 6]
5、支持go语言的符号:
字符串:符号1
原始字符串:\符号1
原始字符串:\\符号1
字节: 97
NIL: <nil>
6、PIPELINE管道:
求长度:7

4.2、模板语法

4.2.1、if语法

if ... end ;if ... else ..end;嵌套使用

  • controller
type TemplateController struct {
	beego.Controller
}

func (t *TemplateController) Get() {
	t.Data["name"] = "icewake"
	t.Data["value"] = []int{1,2,3,4,5,6}

	t.Data["age"] = 19
	t.Data["is_vip"] = true

	t.TplName = "template1.html"

}
  • template.html
...
7、进入网吧条件,必须是18岁,必须有会员卡 <br>
## if ge .age 18  ##
    大于18岁
    ## if eq .is_vip true ##
        可以进入网卡
    ## else ##
        不能进入网吧
    ## end ##
## end ##
...
  • web界面效果
7、进入网吧条件,必须是18岁,必须有会员卡
大于18岁 可以进入网卡

4.2.2、range

## range .value ##
    ## . ##
## end ## <br>

## range $i,$v := .value ##
    ## $i ## =  ## $v ##
## end ## <br>

range也支持else

{{ range .total }}
{{ else }}
	{{ 执行}} //当total为空或者长度为0时会执行这里
{{ end }}

4.2.3、with

伴随,用于重定向pipeline

  • controller
type Student struct {
	Id int
	Name string
	Age int
}

func (t *TemplateController) Get() {
	t.Data["name"] = "icewake"
	t.Data["value"] = []int{1,2,3,4,5,6}
	t.Data["stu1"] = Student{1001,"令狐冲",28}

	t.Data["age"] = 19
	t.Data["is_vip"] = true

	t.TplName = "template1.html"

}
  • template.html
9、with用法1<br>
## with .stu1 ##
## .Id ##
## .Name ##
## .Age ##
## end ## <br>
9、with用法2<br>
## with .name1 ##
## . ##
## else ##
没有该值
## end ## <br>

效果

9、with用法1
1001 令狐冲 28
9、with用法2
没有该值

4.2.4、template

  • controller
type Student struct {
	Id int
	Name string
	Age int
}

func (t *TemplateController) Get() {
	t.Data["name"] = "icewake"
	t.Data["value"] = []int{1,2,3,4,5,6}
	t.Data["stu1"] = Student{1001,"令狐冲",28}

	t.Data["age"] = 19
	t.Data["is_vip"] = true

	t.TplName = "template1.html"

}
  • template1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
前端语法1: ## .name ## <br>
前端语法2:
## range .value ##
    ## . ##
## end ## <br>
前端语法3(引用当前模板根):
## range $i,$v := .value ##
    ## $i ## =  ## $v ##
## end ## <br>
前端语法4(引用根):
## range .value ##
## $.value ##
## end ## <br>
5、支持go语言的符号: <br>
字符串:## "符号1" ## <br>
原始字符串:## "\\符号1" ##<br>
原始字符串:## `\\符号1` ##<br>
字节: ## 'a' ## <br>
NIL: ## print nil ## <br>
6、PIPELINE管道:<br>
求长度:## "icewake" | len ## <br>
7、进入网吧条件,必须是18岁,必须有会员卡 <br>
## if ge .age 18  ##
    大于18岁
    ## if eq .is_vip true ##
        可以进入网卡
    ## else ##
        不能进入网吧
    ## end ##
## end ## <br>
8、range用法<br>
9、with用法1<br>
## with .stu1 ##
## .Id ##
## .Name ##
## .Age ##
## end ## <br>
9、with用法2<br>
## with .name1 ##
## . ##
## else ##
没有该值
## end ## <br>
10、template用法<br>
引入模板: ## template "template2.html" . ## <br>
两个参数,第一个参数模板名称,第二个参数上下文,引入上下文后,就可以在"template2.html"中引用后端数据了 <br>
11、define用法,可用来自定义模板,用于模板定义和模板嵌套<br>
## define "MyDefine" ##
    <ul><li>##.stu1 ##</li></ul>
    <ul><li>##.stu1 ##</li></ul>
    <ul><li>##.stu1 ##</li></ul>
## end ##
## template "MyDefine" . ## <br>




</body>
</html>
  • template2.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
这是模板文件,需要在template中引入的,引入的stu1: ## .stu1 ##


</body>
</html>
  • 效果
前端语法1: icewake
前端语法2: 1 2 3 4 5 6
前端语法3(引用当前模板根): 0 = 1 1 = 2 2 = 3 3 = 4 4 = 5 5 = 6
前端语法4(引用根): [1 2 3 4 5 6] [1 2 3 4 5 6] [1 2 3 4 5 6] [1 2 3 4 5 6] [1 2 3 4 5 6] [1 2 3 4 5 6]
5、支持go语言的符号:
字符串:符号1
原始字符串:\符号1
原始字符串:\\符号1
字节: 97
NIL: <nil>
6、PIPELINE管道:
求长度:7
7、进入网吧条件,必须是18岁,必须有会员卡
大于18岁 可以进入网卡
8、range用法
9、with用法1
1001 令狐冲 28
9、with用法2
没有该值
10、template用法
引入模板: 这是模板文件,需要在template中引入的,引入的stu1: {1001 令狐冲 28}
两个参数,第一个参数模板名称,第二个参数上下文,引入上下文后,就可以在"template2.html"中引用后端数据了
11、define用法,可用来自定义模板,用于模板定义和模板嵌套
{1001 令狐冲 28}
{1001 令狐冲 28}
{1001 令狐冲 28}

4.3、模板基础函数

  • print对应fmt.Sprint //fmt.Sprint和fmt.Print区别在于Sprint用于接收变量并赋值以给新变量,Sprint有返回值,
    • {{ ”icewake“ }} 和 {{ print "icewake" }} 效果是一样的
  • printf对应 fmt.Sprintf
  • println对应fmt.Sprintln
  • and
    • 会逐个判断每个参数,将返回第一个为空的参数,否则就返回最后一个非空参数
    • 只要有一个为空,则整体为空,如果都不为空,则返回最后一个
  • or
    • 会逐个判断每个参数,将返回第一个非空的参数,否则就返回最后 一个参数
  • call:可以调用函数,并传入参数 {{ call .Field .Arg1 .Arg2}}
    • 调用的函数需要返回1个值或者2个值,返回两个值时,第二个值用于返回error类型的错误。返回的错误不等于nil时,执行终止
  • index:读取指定类型对应下标的值
    • 支持map、slice、string
      • this.Date["maps"] = map[string]string
      • {{ index .Maps "name"}} //如果不提供"name" 及 下标,则默认返回所有的
  • len: 返回对应类型的长度
    • 支持类型:map、stirng、array、chan、slice
  • not:返回输入参数的否定值
  • urlquery:有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了。
    • http://www.baidu.com
    • 编码后: http%3A%2F%2Fwww.baidu.com
    • %后面的就是16进制的字符编码
  • eq/ne/lt/le/gt/ge
    • eq特殊,支持多个参数

1、controller.go

package controllers

import (
	"fmt"
	beego "github.com/beego/beego/v2/server/web"
)

type Template2Controller struct {
	beego.Controller
}

func (t *Template2Controller) Get() {
	t.Data["name"] = "icewake"
	t.Data["a"] = 0
	t.Data["b"] = "icewake"
	t.Data["c"] = []int{}
	t.Data["func_test"] = Test  //注意返回函数名称即可,不要添加()
	t.Data["func_test2"] = Test2

	//index
	t.Data["data_map"] = map[string]string{"天下第一":"铁蛋深厚","天下第二":"不败顽童"}
	t.Data["data_slice"] = []int{9,4,65}
	t.Data["data_string"] = "icewake"


	t.Data["data_string2"] = "北京欢迎你"
	t.Data["is_true"] = true
	t.Data["is_true2"] = "true"



	t.TplName = "template2.html"
}


func Test() string {
	return "hello icewake"
}

func Test2(name string) string {
	str1 := fmt.Sprintf("你好,世界! 你好 %s !",name)
	return str1
}

2、template2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
模板文件示例<br>
1、Print用法: {{ .name | printf "%s"}} <br>
2、括号用法: {{ printf "%s,%d" (printf "%d-%d" 2 4) 6}} <br>
3、and用法:{{ .a}} {{ .b }} {{ .c}}<br>  //输出:0 icewake []
{{ and .a .b .c}}  //返回0 <br>
4、or用法: <br>
{{ or .a .b .c}}  //返回 icewake <br>
5、call函数: <br>
{{ call .func_test}} <br>
6、call函数2: <br>
{{ call .func_test2 "icewake2"}} <br>
7、index函数: <br>
{{ index .data_map "天下第一"}} <br>
所有:{{ index .data_slice }} <br>
单个:{{ index .data_slice 0}} <br>
字符串:{{ index .data_string2 }} <br>
字符串:{{ index .data_string2 2}} //返回的是151 而不是 "欢" <br>
7、len函数: <br>
{{ .data_string2 | len }}  //一个中文三个字符,共15个 <br>
{{ len .data_map }}  //返回2,因为data_map中只有两个元素<br>
8、not函数<br>
{{ not .is_true }}
{{ not .is_true2 }}  //两个都是false <br>
9、urlquery函数<br>
{{ urlquery "http://www.baidu.com" }}
10、eq/ne/lt/le/gt/ge <br>
{{ eq 1 2}} <br>
{{ le 1 2}} <br>
{{ eq 3 8 8  3}} //true,只要有一个和3相等即为true <br>


</body>
</html>

3、效果

模板文件示例
1、Print用法: icewake
2、括号用法: 2-4,6
3、and用法:0 icewake []
//输出:0 icewake [] 0 //返回0
4、or用法:
icewake //返回 icewake
5、call函数:
hello icewake
6、call函数2:
你好,世界! 你好 icewake2 !
7、index函数:
铁蛋深厚
所有:[9 4 65]
单个:9
字符串:北京欢迎你
字符串:151 //返回的是151 而不是 "欢"
7、len函数:
15 //一个中文三个字符,共15个
2 //返回2,因为data_map中只有两个元素
8、not函数
false false //两个都是false
9、urlquery函数
http%3A%2F%2Fwww.baidu.com
10、eq/ne/lt/le/gt/ge
false
true
true //true,只要有一个和3相等即为true

4.4、内置模板函数

  • dateformat:

    • 实现了时间的格式化,返回字符串
    • 使用方法:
      • 使用方法:{{ d ateformat.Time "2006/01/02/ 15:04:05"}}
      • {{ dateformat.Time "2006/01/02/ 3:04:05 PM"}}
      • 设置时间格式比较特殊,需要按照如下格式
        • "2006/01/02 15:04:05"
        • "2006/01/02 03:04:05 PM"
  • date

    • 根据字符串返回时间
    • 使用方法 {{ date .T "YY-m-d H:i:s"}}
      • Y: 四位年 y两位年, m月份数字,M月份英文,d日(两位),D星期几英文,i数字分钟,I英文星期
  • compare

    • 实现了比较两个对象的比较,如果相同返回true,否则返回false,使用方法 {{ compare .A .B}}
  • compare_not

    • compare相同的,不相等返回true,相等返回false
    • { { compare_not 'a' 97 }}
  • not_nil

    • 判断是不是为空,如果为空返回false,否则返回true
  • not_null : 同not_nil

  • substr: 用于截取字符串

    • 使用方法:{{ substr "我是中国人" 0 4}} ,左闭右开区间
  • html2str

    • 实现了把html转化为字符串,剔除一些script,css之类的元素,返回纯文本信息,使用方法 {{ html2str.Htmlinfo }}
    • {{ html2str "<a href="www.baidu.com" a>百度</a>}}\
  • str2html

    • 实现了把str转换为html,不转义
  • htmlquote

    • 实现了基本html字符转义,使用方法{{ htmlquote.quote}}
    • 只转义特殊字符,比如< > ' ' 等
  • renderform 看renderform章节

    • 根据StructTag 直接生成对应的表单,使用方法{{ &struct | renderform}}
  • assets_js

    • 为js文件生成一个
posted @ 2022-01-07 10:59  MT_IT  阅读(139)  评论(0编辑  收藏  举报