赞
踩
本笔记中前端内容主要涉及Vue.js,以及部分前端通用知识。
使用vue-cli脚手架可以很方便地生成一个最简单的前端项目框架。下面是一个示例。
vue create frontend
其中的frontend
就是项目名称,这时候会在当前路径下生成一个frontend项目文件夹,我们之后就可以在这里面编写前端相关的内容。
vue ui
上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。
|-- build // 项目构建(webpack)相关代码 | |-- build.js // 生产环境构建代码 | |-- check-version.js // 检查node、npm等版本 | |-- utils.js // 构建工具相关 | |-- vue-loader.conf.js // webpack loader配置 | |-- webpack.base.conf.js // webpack基础配置 | |-- webpack.dev.conf.js // webpack开发环境配置,构建开发本地服务器 | |-- webpack.prod.conf.js // webpack生产环境配置 |-- config // 项目开发环境配置 | |-- dev.env.js // 开发环境变量 | |-- index.js // 项目一些配置变量 | |-- prod.env.js // 生产环境变量 | |-- test.env.js // 测试脚本的配置 |-- src // 源码目录 | |-- components // vue所有组件 | |-- router // vue的路由管理 | |-- App.vue // 页面入口文件 | |-- main.js // 程序入口文件,加载各种公共组件 |-- static // 静态文件,比如一些图片,json数据等 |-- test // 测试文件 | |-- e2e // e2e 测试 | |-- unit // 单元测试 |-- .babelrc // ES6语法编译配置 |-- .editorconfig // 定义代码格式 |-- .eslintignore // eslint检测代码忽略的文件(夹) |-- .eslintrc.js // 定义eslint的plugins,extends,rules |-- .gitignore // git上传需要忽略的文件格式 |-- .postcsssrc // postcss配置文件 |-- README.md // 项目说明,markdown文档 |-- index.html // 访问的页面 |-- package.json // 项目基本信息,包依赖信息等
对于组件的理解:组件是可以复用的Vue实例,带有名字,也可以说是一个自定义元素。是页面的一部分,就是可能其他地方也要用到,所以就封装起来,所以要用的时候进行导入,方便复用。
var app = new Vue({ el: '#app', //绑定到元素上 data: { message:'hello world' }, filters:{ filterName1:function(){}, filterName2:function(){} }, methods:{ methodName1:function(){}, methodName2:function(){} }, //生命周期钩子 created:function(){ //未挂载到dom之前触发 }, mounted:function(){ //挂载到dom后触发 }, beforeDestroy:function(){ } })
{{ data中的变量名 | filter中的过滤器名 }}
v-cloak:解决页面没加载完的时候出现类似{{ msg }}
等变量名的现象,一般和display:none
一起使用
<style>
[v-cloak]:{
display-none;
}
</style>
-------------------------
<p v-cloak>
{{ msg }}
</p>
v-once:只在页面中渲染一次,改变变量的值也不会重新渲染
<p v-once>
{{ msg }}
</p>
条件指令:v-if、v-else-if、v-else
<p v-if = '6>3'>{{ apple }}</p>
<p v-else-if = '9<6'> {{ banana}} </p>
<p v-else> {{ orange }} </p>
v-show:只改变display属性
v-if和v-show的区别:
v-if:实时渲染:页面显示就渲染,不显示就移除
v-show:元素始终存在于页面中,只是改变了display属性
v-for
<div id='app'> <ul> <!-- intm in items --> <li v-for="fruit in fruits">{{ fruit.name }}</li> </ul> <ul> <!-- 带索引的写法 --> <li v-for="(fruit,index) in fruits">第{{ index }}个 {{ fruit.name }}</li> </ul> </div> <script> var app = new Vue({ el: '#app', data: { //这里是一个数组 fruits: [ {name: 'apple'}, {name: 'banana'}, {name: 'orange'} ] } }) </script>
<div id='app'> <span v-for='value in fruits'>{{ value }}</span> <!-- 带v-k-i的写法:value key index (外开) --> <p v-for="(value,key,index) in fruits2"> value:{{value}}<br> key:{{key}}<br> index:{{index}} </p> </div> <script> var app = new Vue({ el: '#app', data: { //这里是一个对象 fruits: { fruit1: 'apple', fruit2: 'banana', fruit3: 'orange' } } }) </script>
基础例子
<div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } } })
计算属性的setter
计算属性默认只有getter,不过在需要时你也可以提供一个setter:
在赋值的时候会触发set函数,把新赋的值以参数的形式传递进去
// ... computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } // ...
现在再运行vm.fullName = 'John Doe'
时,setter会被调用,vm.firstName
和vm.lastName
也会相应地被更新。
改变数组的一系列方法:
两种数组变动vue检测不到:改变数组的指定项;改变数组长度
对应解决方案:
Vue.set(app.arr,1,”car”);//用来改变指定项
app.arr.splice(1);//用来改变数组长度
组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。我们的项目中就大量运用到了组件这一功能,如视频播放功能就根据前端布局分成了5大部分。
全局注册
Vue.component('component-name',{
template: '<div>组件内容</div>'
})
//优点:所有Vue实例都可以使用
//缺点:权限太大,容错率低
局部注册
var app = new Vue({
el: '#app',
data:{},
components: {
'component-name': {
template: '<div>组件内容</div>'
}
}
})
这里有几点需要注意:
组件名用横杆命名法,不能用驼峰命名法(这一点与Gin里面通过首字母大写、python里面通过下划线来区分函数可见性有点像,但也就是这种语法检查会让人觉得有点烦,我一开始在我们的小组项目中就用的是驼峰命名法,后来看到IDEA的智能提示才逐渐改了过来)
template中的内容必须被一个DOM元素包起来(如果真的有多个元素,可以考虑外边套一个div)
在组件的定义中,除了template,还可以有data、methods、computed选项
data必须是一个方法(它返回了真正的变量),以下两种写法都可以接受(我用的是第二种)
//第一种方法
data: function(){
return {
message: 'hello world'
}
}
//第二种方法
data() {
return {
message: 'hello world'
}
}
<div id="app"> ---用v-bind传递数据(需要回车)-------------------------<br> <input type="text" v-model.lazy="parentMsg"> <bind-component v-bind:msg='parentMsg'></bind-component> </div> var app = new Vue({ el: '#app', data: { count: 0, parentMsg: '这是来自父级的数据' }, components: { 'bind-component': { props:['msg'], template: '<div>{{ msg }}</div>' } } })
<div id="app"> <my-component msg="来自父级的数据"></my-component> </div> ---js---------------------------------------------------- <script> var app = new Vue({ el: '#app', data: {}, components: { 'my-component': { props: ['msg'], template: '<p>{{ newMsg }}<p>', data: function () { return { //在data中保存下来 newMsg: this.msg } } } } }) </script>
<div id="app"> <input type="number" v-model="width"> <computed-component :wth='width'></computed-component> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var app = new Vue({ el: '#app', data: {width:10}, components: { 'computed-component': { props: ['wth'], template:'<div :style="style"></div>', //用计算属性转换传入的值 computed: { style: function(){ return { width: this.wth + 'px', height: '100px', background: 'red' } } } } } }) </script>
Vue.component(‘ my-compopent ’,{ props : { //必须是数字类型 propA : Number , //必须是字符串或数字类型 propB : [String , Number] , //布尔值,如果没有定义,默认值就是 true propC: { type : Boolean , default : true }, //数字,而且是必传 propD: { type: Number , required : true }, //如果是数组或对象,默认值必须是一个函数来返回 propE: { type : Array, default : function () { return [] ; } }, //自定义一个验证函数 propF: { validator : function (value) { return value > 10; } } } });
自定义事件用于让子组件给父级传递数据。
子组件用$emi(‘父组件中事件名’, 要传递的数据)来触发事件,父组件用@‘父组件中事件名’='触发的函数’来 监听子组件的事件,触发的函数的参数即为子组件传递来的数据。
具体步骤如下:
第一步:自定义事件;
第二步:在子组件中用$emit触发事件,第一个参数是事件名,后边的参数是要传递的数据;
第三步:在自定义事件中用一个参数来接受。
<div id="app"> 您的余额是:{{ acount }}<br> <my-component :acount="acount" @change-acount='handleAcount'></my-component> </div> <script> var app = new Vue({ el: '#app', data: { acount: 2000 }, methods:{ handleAcount:function(value){ this.acount = value } }, components: { 'my-component': { props:['acount'], template: `<div> <button @click="addAcount">+1000</button> <button @click="reduceAcount">-1000</button> </div>`, data: function(){ return { count: this.acount } }, methods:{ addAcount: function(){ this.count += 1000 //注意这里$emit的参数 this.$emit('change-acount',this.count) }, reduceAcount: function(){ this.count -= 1000 this.$emit('change-acount',this.count) } } } } })
在组件中使用vmodel其实是一个语法糖,这背后其实做了两个操作:利用v-bind绑定一个 value属性,然后利用v-on指令给当前元素绑定input事件。
$emit(‘input’,value)的代码,这行代码实际上会触发一个input事件, ‘input’后的参数就是传递给vmodel进行绑定。
<div id="app"> 您的余额是:{{ acount }}<br> <!-- <my-component :acount="acount" @change-acount='handleAcount'></my-component> --> <!-- 下面使用v-model --> <my-component :acount="acount" v-model='acount'></my-component> </div> <script> var app = new Vue({ el: '#app', data: { acount: 2000 }, components: { 'my-component': { props:['acount'], template: `<div> <button @click="addAcount">+1000</button> <button @click="reduceAcount">-1000</button> </div>`, data: function(){ return { count: this.acount } }, methods:{ addAcount: function(){ this.count += 1000 // 这里触发的是input事件 this.$emit('input',this.count) }, reduceAcount: function(){ this.count -= 1000 this.$emit('input',this.count) } } } } }) </script>
用于两个组件之间的通信,(非父子关系),可以在根组件中用一个空的Vue对象作为中介,一个组件中监听该中介的自定义事件,另一个组件中触发该自定义事件并传递数据。
<div id="app"> <acomponent></acomponent> <bcomponent></bcomponent> </div> <script> Vue.component('acomponent', { template: '<button @click="handle">点击向B组件传递数据</button>', data: function () { return { msg: '我是来自A组件的数据' } }, methods: { handle: function () { // 触发根组件的bus中的method1事件 this.$root.bus.$emit('method1', this.msg) } } }) Vue.component('bcomponent', { template: '<div>{{ msg }}</div>', data: function () { return { msg: '这是B组件' } }, created: function () { // 对根组件bus监听method1事件 this.$root.bus.$on('method1', (value)=> { this.msg = value }) } }) var app = new Vue({ el: '#app', data: { bus: new Vue() } }) </script>
父链:this.$parent
子链:this.$refs.索引
<div id="app"> <button @click='clickA'>A子组件</button> <button @click='clickB'>B子组件</button> <son-a ref='a'></son-a> <son-b ref='b'></son-b> </div> <script> var app = new Vue({ el: '#app', data: { msg: '来自父组件的msg' }, methods: { clickA: function(){ alert(this.$refs.a.name) }, clickB: function(){ alert(this.$refs.b.name) } }, components: { 'son-a': { template: '<div>这是子组件:{{ msg }}</div>', data: function () { return { msg: this.$parent.msg, name: 'son-a' } } }, 'son-b': { template: '<div><div>', data: function () { return { name: 'son-b' } } } } }) </script>
//通用形式,指定变量名,变量类型,变量值 var name int = 99 fmt.Println(name) //指定变量名,以及变量类型,未指定值的时候默认是类型零值 var age int //可以后面赋值 age = 88 fmt.Println(age) //短变量声明,指定变量名,以及变量的值,而变量类型会自动由值的类型决定。 gender := "male" //gender变量的类型是string fmt.Println(gender) //变量列表 要求:左右两边变量名与值的数量相同 //先定义,然后赋值 var x, y, z int //x,y,z都是int类型 x, y, z = 1, 2, 4 fmt.Println(x, y, z) //先定义,同时赋值 var o, p, q = "abc", true, 3.14 fmt.Println(o, p, q) //使用短变量的变量列表形式,需要注意,左边至少有一个新变量声明,否则报错 o, r, s, t := "changed", "xyz", false, 99 fmt.Println(o, r, s, t)
注意:
在函数中(包括自己定义的函数或者主函数中)声明的变量如果没有使用,在编译的时候,会报错。
在函数外面定义全局变量不能使用短变量形式。
const x = 13
const p int = 13
const pp, ppp = 1, 2
const (
i = 10
ii = 20
)
在声明常量的时候,必须指定值,否则出错。
声明在函数中的常量不使用,并不会报错,只有声明的变量或者导入的包未使用才会报错。
注意下面这个用法:
const (
i = 1
ii
iii = 2
iiii
)
fmt.Println(i, ii, iii, iiii)// 1 1 2 2
定义多个常量时,如果某个变量没有执行值,那么就会沿用上一个常量的值。
在和const联用时,iota的作用是:iota初始值为0,每出现一个常量,iota就加1。
const (
i = iota
ii
iii = 4
iiii = iota
)
fmt.Println(i, ii, iii, iiii) //0 1 4 3
基本数据类型:string、bool、intX、floatX
复合数据类型:array、struct
引用类型:point、slice、map、func、channel
接口类型:interface
和其他语言一样,注意–、 ++只能后置,即只能i++、i–。
逻辑运算符||、&&、!,这3个运算符左右两边的表达式必须是bool型,不能是int或者string类型。
在两种类型的变量相互赋值或者进行比较时,Go语言不会像其他语言一样进行隐式转换,所以必须进行显式地转换。
所以下面两种做法是不可行的:
var f1 float32 = 3.14
var f2 float64 = 6.28
//类型不同,不能进行比较,会报错
// fmt.Println(f1 == f2)
//类型不同,不能进行运算
f3 := f1 - f2
fmt.Println(f3)
solution:
var f1 float32 = 3.14
var f2 float64 = 6.28
//进行强制转换
f := float32(f2)
fmt.Println(f1 == f) //false
f3 := f1 - f
fmt.Println(f3) //0
c := complex(3, 4)
fmt.Println(real(c), imag(c))//3 4
**注意:**bool值的true或者false,并不会自动转换为整型的1或者0。
单引号只能用来表示单个字符,不能用来括字符串;双引号技能括单个字符,又能括字符串。
a := 'ab' //wrong
b := 'x' //correct
c := "y" //correct
d := "abc" //correct
str1 := "你 " str2 := "好" //使用 + 进行字符串拼接 str3 := str1 + str2 //你 好 fmt.Println(str3) length := len(str1) //使用len()求字符串的长度时,求的是字节数,不是字符数 fmt.Println(length) //4 str := "hello world" //取某一个索引的字符 fmt.Println(str[0]) //104 fmt.Println(string(str[0])) //h //错误用法,不能修改字符串内部的值 //str[0] = "d" //可以改变字符串的值 str = "go to hell" fmt.Println(str) //go to hell
截断格式str[start:end],左闭右开区间,即end位置的字符不会包含在内。省略start时,默认start为0。省略end时,默认end为字符串长度len(str)。
str := "abcdefg"
fmt.Println(str[2:4]) //cd
fmt.Println(str[2:]) //cdefg
fmt.Println(str[:4]) //abcd
str := "abcdefg"
length := len(str)
for i := 0; i < length; i++ {
fmt.Println(str[i], string(str[i]))
}
for index, value := range str {
fmt.Println(index, string(value))
}
使用``代替双引号。可以保存内容的格式,原样输出.
str := `
menu:
1、
2、
`
fmt.Println(str)
var num int = 10
var p *int = &num
// 等价于
//p := &num
fmt.Println(num, p, *p) //10 0xc42001a0f0 10
注意:
指针的默认值是nil,不是NULL,并且go语言中没有NULL常量
使用&取地址,使用*解引用
支持C语言类似的指针运算,比如指针自增等。
使用“.”来访问目标成员,不支持“->”运算符
不存在函数指针
package main
import "fmt"
type person struct {
Name string
Age int
}
func main() {
p := &person{"abc", 90}
name := p.Name
//等价于
//name := (*p).Name
fmt.Println(name)
}
arr := [3]int{99, 999, 9999}
p := &arr
fmt.Println(arr) //[99 999 9999]
fmt.Println(p) //&[99 999 9999]
fmt.Println(*p) //[99 999 9999]
var p [3]*int
x, y, z := 1, 11, 111
p[0], p[1], p[2] = &x, &y, &z
fmt.Println(p[0], p[1], p[2]) //0xc42001a0f0 0xc42001a0f8 0xc42001a100
fmt.Println(*p[0], *p[1], *p[2]) //1 11 111
var num int = 10
p := &num //p保存着num的地址
pp := &p //pp保存着p的地址,即地址的地址
fmt.Println(p, *pp) //0xc42001a0f0 0xc42001a0f0
fmt.Println(&p, pp) //0xc42000c028 0xc42000c028
fmt.Println(*p, **pp) //10 10
使用type关键字定义新的类型名,一般出现在包一级,即函数外部,如果新建的类型名字首字母大写,则在包外可以使用,否则只能在包内使用。
格式:type new_type base_type
注意type和C语言的typedef不同,typedef只是定义别名而已,而Go语言的type不仅定义类型名称,还包括类型的行为。
package main
import "fmt"
type Score int
func (s Score) Pass() bool {
return s >= 60
}
func main() {
var s1 Score = 69
fmt.Println(s1.Pass()) //true
}
虽然使用type关键字定义了一个新的类型,就像上面的Score和int,虽然在底层都是int,底层都是同一个类型,但是:不能直接进行比较、计算,必须要先转换后才可比较和计算。
a, b, c := 1, "abc", true
fmt.Println(a, b, c) //1 abc true
fmt.Print(a, b, c, "\n") //1abctrue
fmt.Printf("%d,%s,%t\n", a, b, c) //1,abc,true
var (
name string
age int
)
for {
fmt.Println("please intput name,age")
fmt.Scan(&name, &age)
fmt.Println("name:", name, "\t age:", age, "\n")
}
运行结果如下:
please intput name,age
abc 30
name: abc age: 30
please intput name,age
abc
50
name: abc age: 50
please intput name,age
abc 50 xyz 99
name: abc age: 50
please intput name,age
name: xyz age: 99
var (
name string
age int
score int
)
for {
fmt.Println("please intput name,age,score")
fmt.Scanln(&name, &age, &score)
fmt.Println("name:", name, "\t age:", age, "\t score:", score, "\n")
}
运行结果如下:
please intput name,age,score
abc
name: abc age: 0 score: 0
please intput name,age,score
abc 30
name: abc age: 30 score: 0
please intput name,age,score
abc 30 99
name: abc age: 30 score: 99
var (
name string
age int
)
fmt.Println("please intput name,age")
fmt.Scanf("%s %d", &name, &age) //输入时以空格分隔
fmt.Println("name:", name, "\t age:", age, "\n")
fmt.Println("please intput name,age")
fmt.Scanf("%s,%d", &name, &age) //输入时,以逗号分隔
fmt.Println("name:", name, "\t age:", age, "\n")
运行结果:
please intput name,age
abc 99
name: abc age: 99
please intput name,age
xyz 88
name: xyz age: 99
注意,Scan并不会在遇到回车换行的时候结束输入。而Scanln和Scanf都是一旦遇到回车换行,就认为本轮输入(一个Scanln或者Scanf)结束,执行下一个Scanln或者Scanf。当然,缓冲的数据,可以自动赋值。
if score := 61; score > 80 {
fmt.Println("优秀")
} else if score > 60 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
注意:
花括号不可省,切不可另起一行。
如果要声明变量,可以在if后面声明,并用分号分隔,分号后面跟条件。
最最重要的是:
if后面的条件必须是bool类型的值或者结果为bool类型的表达式,不可以是整型或者其他类型。
var length int = 3
switch length {
case len("abc"): //可以是表达式
fmt.Println(3)
case 4:
fmt.Println(4)
default:
fmt.Println(5)
}
//输出
//3
Go语言中的switch case结构中,每一个case条件分支末尾不用加break;
case可以后面的条件可以使变量,也可以是常量,但是有一点:case后面的类型,要和switch后面的类型相同。
fallthrough关键字的作用如下:
var score int = 12
switch score {
case 6:
fallthrough
case 12:
fmt.Println(12)
fallthrough
case 24:
fmt.Println(24)
default:
fmt.Println(36)
}
上述代码会输出12和24。
如果某个case里面有fallthrough,那么,这个case的紧接着的下一个case条件会默认满足,直接执行写一个case中的语句,并不会判断下一个case的条件是否满足。
case可以这样写:
var score int = 12
switch score {
case 6, 12:
fmt.Println(6, "或者", 12)
default:
fmt.Println(36)
}
省略switch后面的表达式,此时相当于if else。
var score int = 90
switch {
case score > 80:
fmt.Println("优秀")
fallthrough
case score > 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
上述代码会输出“优秀”和“及格”。
Go语言中没有while循环和do…while循环,只有一个for循环,但是for循环有三种形式,可以替代while循环和do while循环。
for i:=0;i<10;i++{
fmt.Println(i)
}
for {
fmt.Println("这是死循环,相当于while(1)")
}
for i,v := range m{
fmt.Println("同样是死循环")
}
上面三种用法分别对应其他语言中的for循环,while()循环,do…while循环。
在循环中可以使用break和continue来控制循环。
export GO111MODULE # 开启go mod支持
go mod init project_name # 初始化 go mod
go get -v github/go-gnic/gin@v1.4.0 # 安装gin包
r := gin.Default() // 获取默认服务器对象
r.GET("/path", func (ctx *gin.Context){ // 绑定路由地址 get请求 以及 /path 后面执行匿名回调函数
ctx.JSON(200, gin.H{ // http response 响应 200 以json格式输出数据
"state": 1
"data":"data",
"msg":"获取数据成功",
})
})
r.Run("127.0.0.1:8080") // 阻塞 监听127.0.0.18080端口
1. http常见的请求GET、POST、DELETE、PATCH等路由实现
1.1. 常见绑定路由方法名称:
r.GET(path, func (ctx *gin.Context){
})
其他方法同理。
1.2. Handle方法
其实上面的方法底层还是调用了这个Handle方法
r.Handle(http_method, path, func(ctx *gin.Context){ // 绑定请求method方法 路径path 回调函数
})
2. 绑定静态static目录
r.Static("/css/", "./css") // 1.绑定css目录访问,本质上使用StaticFS做了一层封装而已
r.StaticFS("/js/", http.Dir("./js")) // 第二种方式。
r.StaticFile("/hello.php", "./hello.html")
3. 动态url参数(固定参数以及泛参数)
3.1. 通过url传递固定参数
r.GET("/get/:name/:id", func(c *gin.Context){ // 绑定固定参数 name id
name := c.Param("name") // 获取name参数
id := c.Param("id") // 获取id参数
c.JSON(200, gin.H{
"state":1,
"name": name,
"id": id
})
})
3.2. 泛url绑定
r.GET("/user/*action", func(c *gin.Context){ // 泛绑定前缀所有的/user/xx user/abc 等url都走到这里
})
1. 获取get参数
name := r.Query("name")// 不带默认值的获取参数
age := r.DefaultQuery("age")// 带默认值的获取参数
2. 获取post参数
name := c.PostForm("name")
age := c.DefaultPostForm("age")
3. 获取body参数
strByte ,err := ioutil.ReadAll(context.Request.Body) // 从http request body中读取数据
在读取body之后,通过PostForm()和DefaultPostForm()函数获取post参数会失效。
解决方案:把body读到的数据又重新放到Request.Body里面就可以了。
ioutil.NopCloser(bytes.NewReader(strByte)) // 重新写好回 Request.Body里面就可以了。
4. 绑定参数到struct结构体
// 定义结构体类
type person struct{
Name string `form:"name"` // 加上form的tag标签
Gender string `form:"gender"`
BirthDay time.Time `form:"birthday" time_format:"2006/01/02"`
}
var p person
err := context.ShouldBind(&p) // 自动注入person的实例中
5. 校验参数格式以及I18N
5.1. 结构体验证参数
常见校验tag:form、time_format、binding
// 定义结构体类
type person struct{
Name string `form:"name" binding:"required"` // 必须
Gender string `form:"gender"`
Age int `form:"age" binding:"gt=10"` // 必须传且 大于10
BirthDay time.Time `form:"birthday" time_format:"2006/01/02"`
}
校验规则详细文档: https://godoc.org/gopkg.in/go-playground/validator.v8
5.2. 自定义校验规则
基于v8校验去实现一个tag,实现一个tag也就是实现一个方法。
具体文档: https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Custom_Functions
6. 多语言I18N
使用v9校验器。
7. 上传文件uploadFile
file, err := ctx.FormFile("file") // 获取到上传的文件对象
// file.FileName 文件名称 file.Size 文件大小
ctx.SaveUploadFile(file, "/path/" + file.FileName) // 保存上传文件
1. 默认gin.Default()使用了logger和recover中间件
f,_ := os.Create("gin.log") // 可以将logger的内容重定向写入到日志log中
gin.DefaultWriter = io.MultiWriter(f)
gin.DefaultErrorWriter = io.MultiWriter(f)
r := gin.New() // 空白app对象
r.Use(gin.Logger()) // 使用中间件
2. 自定义中间件
自定义一个func,返回值是gin.HandleFunc函数即可。
func IpblockMiddleware() gin.HandlerFunc { return func(ctx *gin.Context) { ipLists := []string{ "127.0.0.2", } clientIp := ctx.ClientIP() flag := false for _,ip := range ipLists { if ip == clientIp { flag = true break } } if !flag { ctx.String(401, "权限有误: %s 禁止访问", clientIp) ctx.Abort() // 结束中间件往下执行 } } } r.Use(IpblockMiddleware) // 这样使用中间件即可
1. 模板渲染
r.LoadHTMLGlob("templdate/*")
r.HTML(200,"index.html",gin.H{ //设置数据传递给index.html
"title":"标题"
})
在index.html中使用模板渲染:
<!doctype html>
<html>
<h1> {{.title}} </h1> // 渲染title (标题)
</html>
2. https整数自动配置
autotls.Run(r, "www.baidu.com")
1. 自己可以把项目目录结构设计成类似这样:
-----------------conf
--------------config.yaml
-----------------controllers
-----------------routers
-----------------dao
-----------------services
-----------------lib
-----------------middleware
-----------------logs
-----------------docker
2. 使用yaml作为配置文件工具
yaml解析包
go get gopkg.in/yaml.v2
demo案例:
// 定义sturct来解析yaml type Config struct{ Version string `yaml:"version"` Items []string `yaml:"items"` Spec struct { Image struct{ Url string `yaml:"url"` Name string `yaml:"name"` } `yaml:"image"` } `yaml:"spec"` } // config.yaml文件内容 version: "1.0" items: - "a" - "b" - "c" spec: image: url: "www.baidu.com" name: "baidu" // 解析代码案例 var config Config err = yaml.Unmarshal(strByte, &config) if err != nil { fmt.Println("yaml解析失败: " + err.Error()) }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。