配置Go开发环境
1.下载SDK:
Downloads - The Go Programming Language (google.cn)

2.解压配置环境变量
验证:在任意目录开启dos窗口 执行指令:go -version
结果:

3.目录结构
通常GO语言的工作空间为:goProject/src/goCode/Project01
代码目录一般在Project01/main下
4.第一个Go代码(helloWorld.go)
package main
import "fmt"
func main(){
fmt.Println("Hello WOrld!")
}
5.编译和执行
在helloWorld.go 同级目录下执行指令 “go build helloWorld.go"将会生成一个名称为helloWorld.exe 的二进制可执行文件
执行helloWorld.exe ,控制台输出 Hello World!
6.总结
至此,Go语言的开发环境就配置完毕
GoLang 正题
1.API
Go语言提供了大量的标准库,官方标准库中文社区:https://studygolang.com/pkgdoc
2.变量声明
2.1
声明变量的格式:
//第一种方式:声明类型+初始化
var age int = 20
//第二种方式:1.声明类型 2.初始化 ,如果不对变量进行初始化,那么在明确类型的情况下,系统会给定默认值
var age int
age = 20
//第三种方式:不声明类型+初始化,系统会根据初始化的值自动推断变量的类型
var num = 10
//第四种方式:省略var 关键字,关键字":="
sex := "男"
//第五种方式:一次性声明多个变量
var(
v1 = 10
v2 = "Develoven"
v3 = 3.1415
)
go 是强类型语言,如果不对变量进行初始化,会有默认值
如果一个变量名、函数名、常量名是以大写字母开头,则可以被其他的包访问,如果首字母是小写只能被所在包访问
PS:若需要左侧显示目录的树形结构, ”设置“——user——features——Explorer——Compact Folders(取消勾选)
3.数据类型

4.指针
指针就是一个存储了一个内存地址的变量
package main
import "fmt"
func main(){
var age int = 18
fmt.Println("变量age的值:",age)
fmt.Println("变量age的地址:",&age)
var prt * int = &age
fmt.Println("指针变量prt的值:",prt)
fmt.Println("指针变量prt的地址:",&prt)
fmt.Println("指针变量prt指向的地址的值:",*prt)
}
运行结果:

4.1 可以通过指针改变指向值
package main
import "fmt"
func main (){
var num int = 10
fmt.Println("num初始值:",num)
var prt *int = &num
*prt = 20
fmt.Println("num当前值:",num)
}
运行结果:

5.包引用
在go语言中,如果导入自定义包,那么包名是从 $GOPATH/src后开始计算的,所以在配置环境变量时不仅需要在PATH上添加GO语言的SKD目录里还需要配置GOPATH环境变量,GOPAT表示工作目录,类似于idea和eclipse的工作空间,前边提到的工作空间的常见目录结构:goProject/src/goCode/Project01,其中goProject层目录就是GOPATH应该指向的目录
go有两种包管理模式,gomod和gopath,两者相互不兼容,在一些版本中,默认是启用gomod 的,在一些版本中是使用gopath的,还有一些版本中是模棱两可的(这个版本中若不做环境修改则不能导如第三方依赖),使用指令go env查看
在控制台输入并运行指令“go env”
-
GO111MODULE=off,无模块支持,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。
-
GO111MODULE=on,模块支持,go命令行会使用modules,而一点也不会去GOPATH目录下查找
-
GO111MODULE= ,此模式下不能引入包,必须设置为on或者off(这是我初学阶段遇到的一个大坑)
重申一下,在go语言中,变量、常量、函数的权限用首字母的大小写控制,也就是说如果先让外部的包能够调用到内部定义的变量、常量、函数,必须将其首字母大写
6.获取用户终端输入
在编程中,需要接受用户数的数据时,可以使用键盘键入获取用户输入数据
6.1 fmt.Scanln
Scanln 以换行符为终止扫描的信号
package main
import "fmt"
//键入学生的 姓名 性别 地址 电话 等信息并输出
func main(){
var name string
fmt.Println("请输入学生姓名:")
fmt.Scanln(&name)
var gender string
fmt.Println("请输入学生性别:")
fmt.Scanln(&gender)
var address string
fmt.Println("请输入学生家庭住址:")
fmt.Scanln(&address)
var phone string
fmt.Println("请输入学生手机号:")
fmt.Scanln(&phone)
fmt.Println("学生姓名:",name)
fmt.Println("学生性别:",gender)
fmt.Println("学生家庭住址 :",address)
fmt.Println("学生手机号:",phone)
}
运行结果:

6.2 fmt.Scanf
Scanf需要自定义键入格式,因为自定义键入格式比较麻烦,所以很少使用
package main
import "fmt"
func main(){
var name string
var gender string
var address string
var phone string
fmt.Println("请依次输入学生的名称/性别/地址/手机号并以“ ”隔开")
fmt.Scanf("%s %s %s %s", &name, &gender, &address, &phone)
fmt.Println("学生姓名:", name)
fmt.Println("学生性别:", gender)
fmt.Println("学生家庭住址 :", address)
fmt.Println("学生手机号:", phone)
}
执行结果:

7.流程控制
7.1 if
if与Java相同,有两个小细节需要注意:
1.条件表达式可以不用(),go推荐不适用()
2.{}必须要存在,即使是一句逻辑代码,也不能省略
3.if后可以并列接入变量的定义
package main
import "fmt"
func main() {
if count := 20; count < 30 {
fmt.Println("count<30")
}
}
执行结果

7.2 switch
switch示例代码
package main
import "fmt"
func main(){
var score int 100
switch score/10
case 10 :
fmt.Println("满分")
case 9 :
fmt.Println("90+")
case 8 :
fmt.Println("80+")
case 7 :
fmt.Println("70+")
case 6 :
fmt.Println("60+")
case 5 :
fmt.Println("50+")
default :
fmt.Println("成绩低于50分,不合格")
}
注意点:
1.switch后的表达式可以是一个常量值、变量,也可以是一个有返回值的函数
2.如果case后的值是一个常量表达式,那么必须不能重复
3.case后的值必须与switch后的表达式属于同一类型
4.case后的值可以是多个,中间用","隔开
package main
import (
"fmt"
"math"
)
func main(){
var a int
fmt.Println("请输入a的值:")
fmt.Scanln(&a)
switch a/math.Abs(a){
case 1,-1 :
fmt.Println("a=",a)
fmt.Println("Abs(a)=",Abs(a))
}
}
运行结果

5.case后不需要break,golang会自动短路
6.default 语句的位置是任意的,而且不是必需的
7.switch后可以不带表达式,当作if语句使用,可以使用但 不推荐
package main
import "fmt"
func main(){
var a int32 = 2
var a int32 = 2
switch {
case a == 1:
fmt.Println("a==1")
case a == 2:
fmt.Println("a==2")
default:
fmt.Println("default")
}
}
运行结果:

8.switch后可以定义一个变量,以分号结束,可以使用,但不推荐
package main
import "fmt"
func main(){
switch a := 7 ; {
case a >= 7:
fmt.Println("a >= 7")
default :
fmt.Println("a < 7")
}
}
9.switch穿透,关键字fallthrouth,如果在case后加入关键字fallthrough,则会继续执行下一个case,称作switch穿透
package main
import "fmt"
func main(){
var score int
fmt.Println("请输入成绩:")
fmt.Scanln(&score)
fmt.Println("输入的成绩是:",score)
switch score/10 {
case 10 :
fmt.Println("满分")
fallthrough
case 9 :
fmt.Println("90+")
case 8 :
fmt.Println("80+")
case 7 :
fmt.Println("70+")
case 6 :
fmt.Println("60+")
case 5 :
fmt.Println("50+")
default :
fmt.Println("成绩低于50分,不合格")
}
}
执行结果:

7.3 for
在golang中,循环表达式只有for,格式与其他语言格式相同,注意点是初始化表达式不能使用 var i int =0 初始化必须使用 i := 0 的方式定义初始化表达式:
for 初始化表达式 ;布尔表达式;迭代因子 {
循环体;
}
package main
import "fmt"
func main() {
var sum int = 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Println("sum = ", sum)
}
执行结果:

7.3.1 for循环的灵活格式
package main
import "fmt"
//格式1:基础格式
func main() {
var sum int = 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Println("sum = ", sum)
}
package main
import "fmt"
//格式2:将初始化表达式和迭代因子分别提起到for循环外和循环体内,也可单独提取出其中一个
func main() {
var sum int = 0
i := 0
for i <= 10 {
sum += i
i++
}
fmt.Println("sum = ", sum)
}
package main
import "fmt"
//golang中的死循环格式1
func main(){
for {
fmt.Println("Golang 是世界上最好的语言")
}
}
package main
import "fmt"
//golang中的死循环格式2
func main() {
for {
fmt.Println("Golang 是世界上最好的语言")
}
}
for ... range 结构,for…range结构是Golang独有的一种迭代结构,for…range可以遍历数组、切片、map以及通道,for…range结构类似于其他语言中的foreach,一般的格式为:
for key,value :range coll {
循环体
}
package main
import "fmt"
//暂时不适用中文,学习完切片即可使用
func main() {
var str string = "hello golang"
for i := 0; i < len(str); i++ {
fmt.Printf("%c\n", str[i])
}
}
执行结果:

使用 for…range格式实现上述代码:
package main
import "fmt"
func main() {
//此处的中文是可以使用的
var str string = "hello golang,你好"
for i, value := range str {
fmt.Printf("i = %d,value = %c\n", i, value)
}
}
执行结果:

7.4 break
break 的用途:
1.switch case 的case块后都需要break关键字来结束,在传统的语言中如果不加入break关键字会出现代码穿透的效果,但是在go语言中可以省略
package main
import "fmt"
func main() {
var score int64
fmt.Print("请输入成绩:")
fmt.Scanln(&score)
switch score / 10 {
case 10:
fmt.Println("满分")
break
case 9:
fmt.Println("优秀")
break
case 8:
fmt.Println("80+")
break
default:
fmt.Println("80-")
}
}
运行结果:

2.break可以跳出当前循环
package main
import "fmt"
func main() {
for i := 1; i <= 5; i++ {
for j := 2; j <= 4; j++ {
fmt.Printf("i = %v,j = %v \n", i, j)
if i == 2 && j == 3 {
break
}
}
}
}
执行结果:

3.break跳出指定循环(标签的使用)
package main
import "fmt"
func main() {
tag:
for i := 0; i <= 5; i++ {
for j := 1; j <= 4; j++ {
fmt.Printf("i = %v ; j = %v ;\n", i, j)
if i == 2 && j == 2 {
break tag
}
}
}
}
执行结果:

7.5 continue
continue结束本次循环
package main
import "fmt"
func main() {
for i := 1; i <= 5; i++ {
if i == 2 {
continue
} else {
fmt.Printf("i = %v \n", i)
}
}
}
运行结果:

7.6 go...to
在汇编中常用,但是在Golang 、Java的高级语言中不推荐使用
8 函数
ps:Golang的函数比较特殊,他的返回值可以是多个
Golang的函数也支持可变长度,与Java相同 比如:func test(args...int){
函数体,函数体内部处理可变参数时候,可将其当作切片来处理,切片类似于Java中的数
}
8.1 函数定义的格式:
func 函数名 (形参列表) (返回值列表){
函数体
}
形参列表描述了函数的参数类型以及参数名称,他们作为局部变量,由函数的调用者提供,函数的返回值列表描述了函数返回值的变量名以及类型,
8.1.1定义函数计算两个整数的和
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
sum := sum(a, b)
fmt.Printf("%v + %v = %v", a, b, sum)
}
func sum(a int, b int) int {
sum := a + b
return sum
}
运行结果:

8.1.2 定义函数计算两个数的差
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
difference := difference(a, b)
fmt.Printf("%v - %v = %v", a, b, difference)
}
func difference(a int, b int) int {
difference := a - b
return difference
}
运行结果:

8.1.3定义函数计算两个数的和与差
package main
import "fmt"
func main() {
var a int
var b int
fmt.Print("请输入a的值:")
fmt.Scanln(&a)
fmt.Println()
fmt.Print("请输入b的值:")
fmt.Scanln(&b)
sum, difference := sumAndDifference(a, b)
fmt.Printf("%v + %v = %v \n%v - %v = %v", a, b, sum, a, b, difference)
}
func sumAndDifference(a int, b int) (int, int) {
sum := a + b
difference := a - b
return sum, difference
}
运行结果:

8.1.4 可变参数
Golang的函数也支持可变长度,与Java相同 比如:func test(args...int){
函数体,函数体内部处理可变参数时候,可将其当作切片来处理,切片类似于Java中的数
}
package main
import "fmt"
func main() {
test(1)
fmt.Println("--------------------------------")
test(1, 2, 3)
fmt.Println("--------------------------------")
test(1, 2, 3, 4, 5)
}
func test(args ...int) {
for i := 0; i < len(args); i++ {
fmt.Println(args[i])
}
}
运行结果:

8.1.5 传值与传指针
当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数的值的时候,调用函数中响应实参并不会发生任何变化,因为数值变化只是作用在copy上
我们来看一个例子:
package main
import "fmt"
func add(a int ) int {
a = a+1
return a
}
func main (){
x := 3
fmt.Println("x = ",x)//输出 x=3
x1 := add(x)
fmt.Println("x1 = ",x1)//输出x1 = 4
fmt.Println("x = ",x)//输出X=3
}
我们可以看到,虽然我们调用了add函数,并且在add中执行了 a = a+1 的操作,但是x的值是没有发生变化的,理由很简单,因为当我们调用add的时候,add接收到的参数其实是x的copy ,而并不是x本身。那如果我们真的需要传这个X的本身,该怎么办呢?
这个就牵扯到指针。变量在内存中是存放在一定地址上的,修改变量实际是修改变量地址处的内存。只有add函数知道x变量所在的地址,才能修改x变量的值,所以我们只需要将x变量的地址&x传入函数,并将函数的参数类型从int 修改为 *int即可,即将函数的参数类型修改为指针类型,这样就能在函数中修改x的值。此时参数仍然是按copy传递的,这是copy的是一个指针,示例如下
package main
import "fmt"
func add1(a * int ) int {
*a = *a +1
return *a
}
func main(){
m := 100
fmt.Println("m = ",m)//输出m = 100
m1 := add1(&m)
fmt.Println("m1 = ",m1)//输出 m1 = 101
fmt.Println("m = ",m)//输出 m = 101
}
这样我们就达到了修改x的目的,那么到底传指针有什么好处呢?
①传指针使得多个函数能操作同一个对象
②传指针比较轻量级(8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会话费相对较多的系统开销(内存和时间)。所以当需要传递打的结构体的时候,用指针是一个明智 的选择
③Go语言中,channel,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针,但是如果要盖面slice的长度,需要去地址传递指针
8.2 init函数
init函数是初始化函数,可以用来执行一些初始化操作,每一个.go文件都可以拥有一个init函数,该函数会在main函数之前执行,等同于Java中的静态代码块
package main
import "fmt"
func main() {
fmt.Println("main--------------------")
}
func init() {
fmt.Println("init----------------------")
}

8.3 匿名函数
Golang支持匿名函数,当我们希望一个函数只被调用1次时,可以使用匿名函数
8.3.1 匿名函数的调用方式
8.3.1.1 在匿名函数定义时直接调用这种匿名函数只能调用一次
package main
import "fmt"
func main() {
a := func(i int, j int) (k int) {
k = i + j
return
}(10, 20)
fmt.Println(a)
}
执行结果:

8.4 闭包
//需求,求和
package main
import "fmt"
func main(){
}
func getSum() func(int) int{
int sum =0
return func(num int ){
return sum+num
}
}
8.5关键字defer
defer关键字的使用:在函数中,经常需要释放资源,在函数执行完毕后及时释放资源,在Go语言中使用defer关键字
当程序执行到defer关键字定义的语句时,会把当前语句压入一个特殊栈中,待所有非defer语句执行完成后,再执行特殊栈中的语句,
注意点①
入栈时,入栈的信息为代码按照顺序执行执行到defer语句时的即时信息,例如
var num int = 10
defer fmt.Println("numDefer=",num)
num += 50
fmt.Println("num=",num)
此段代码输出语句有两条,先输出num=60 再输出 numDefer =10
注意点②
栈的规则时先进后出,所以后压入的语句会先执行
//案例展示
package main
import "fmt"
func main() {
var num int = 10
defer fmt.Println(" numDefer1=", num)
num += 50
defer fmt.Println(" numberDefer=", num)
fmt.Println(" num=", num)
}
执行结果:

总结:延迟语句(defer),可以在函数中添加多个defer语句,当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭响应的资源,不然很容易造成资源泄露的问题,在打开一个资源的时候,我们一般会采用如下代码:
func ReadWrite() bool {
file.open("file")
//do something
if(failureX){
file.Close()
return false
}
if(failureY){
fiel.Close()
return false
}
file.Close()
return true
}
在上述代码中,有很多的重复代码,Go语言的defer就有效地解决了这个问题,使用defer之后,不仅代码量减少了很多,而且程序变得更加优雅。在defer后指定的函数会在函数退出前调用,示例如下:
func ReadWrite() bool {
file.open("file")
defer file.Close()
//do something
if(failureX){
return false
}
if(failureY){
return false
}
return ture
}
下面这段代码的输出结果:
for i:= 0; i < 5 ; i++ {
defer fmt.Printf("%d",i)
}
8.6系统常用函数
//Golang常用系统函数演示
package main
import
(
"fmt"
"strconv"
"strings"
)
func main() {
//字符串长度函数 len(),中文汉字在Golang中占3个字节
var str string = "Golang 你好"
fmt.Println(len(str))
fmt.Println("------------------------------------------")
//遍历字符串 for …… range
for i, value := range str {
fmt.Printf("索引:%d,值:%c \n", i, value)
}
fmt.Println("------------------------------------------")
//遍历字符串,切片方式
r := []rune(str)
for i := 0; i < len(r); i++ {
fmt.Printf("索引:%d,值:%c \n",i, r[i])
}
fmt.Println("------------------------------------------")
//字符串与整数的相互转换
//①字符串转整形
num,err := strconv.Atoi("666")
if err != nil{
fmt.Println("发生错误:",err)
}
fmt.Println("num=",num)
fmt.Println("------------------------------------------")
//整形转字符串
strCov := strconv.Itoa(1234)
fmt.Println("strCov=",strCov)
fmt.Println("------------------------------------------")
//一个字符串中有几个指定的字符串
countStr := "java"
subStr := "a"
count := strings.Count(countStr,"a")
fmt.Printf("字符串%v中包含%v个字符串%v\n",countStr,count,subStr)
fmt.Println("------------------------------------------")
//不区分大小写的字符串比较
flag := strings.EqualFold("hello","HELLO")
fmt.Println("flag=",flag)
fmt.Println("------------------------------------------")
//区分大小写的字符串比较
fmt.Println("hello"=="HELLO")
fmt.Println("------------------------------------------")
//返回字符串中某个字符串出现的第一个索引位置
fmt.Println(strings.Index("java","a"))
fmt.Println("------------------------------------------")
//字符串替换:func Replace(s, old, new string, n int) string
//返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换所有old子串。
fmt.Println(strings.Replace("20220412","2","1",-1))
fmt.Println("------------------------------------------")
//字符串分割
strs := strings.Split("go-python-java-c-c++-curl","-")
fmt.Println(strs)
fmt.Println("------------------------------------------")
//大小写转换
fmt.Println(strings.ToUpper("hello"))
fmt.Println(strings.ToLower("OK"))
fmt.Println("------------------------------------------")
//去除字符串两边空格
var trimSpaceStr = " aaaa "
fmt.Printf("去空格之前%v,去空格之后%v\n",trimSpaceStr,strings.TrimSpace(trimSpaceStr))
fmt.Println("------------------------------------------")
//去除字符串两边指定的字符
var trimStr = "~aa~aa~"
fmt.Printf("去除~之前%v,去除~之后%v\n",trimStr,strings.Trim(trimStr,"~"))
fmt.Println("------------------------------------------")
//去除字符串左边的字符
var trimLeftStr = "~aaaa~~"
fmt.Printf("去除左边~之前%v,去除左边~之后%v\n",trimLeftStr,strings.TrimLeft(trimLeftStr,"~"))
//去除字符串右边的字符
var trimRightStr = "~aaaa~~"
fmt.Printf("去除右边~之前%v,去除右边~之后%v\n",trimRightStr,strings.TrimRight(trimRightStr,"~"))
fmt.Println("------------------------------------------")
//判断字符串是否以指定的字符串开头
var prefixStr = "http://localhost:8080"
var preStr = "http"
fmt.Printf("原始字符串:%v,起始字符串:%v,是否开始字符串:%v\n",prefixStr,preStr,strings.HasPrefix(prefixStr,preStr))
fmt.Println("------------------------------------------")
//判断字符串是否
var suffixStr = "你好中国"
var sufStr = "中国"
fmt.Printf("原始字符串%v,结束字符串:%v,是否结束字符串:%v\n",suffixStr,sufStr,strings.HasSuffix(suffixStr,sufStr))
}
执行结果:

8.7 时间和日期函数
package main
import (
"fmt"
"time"
)
//时间日期函数
func main() {
fmt.Println("----------------")
now := time.Now()
//当前时间
fmt.Printf("时间:%v,类型:%T\n", now, now)
fmt.Printf("年:%v\n", now.Year())
fmt.Printf("月:%v\n", now.Month())
fmt.Printf("月:%v\n", int(now.Month()))
fmt.Printf("日:%v\n", now.Day())
fmt.Printf("时:%v\n", now.Hour())
fmt.Printf("分:%v\n", now.Minute())
fmt.Printf("秒:%v\n", now.Second())
fmt.Println("-------------------------------")
//时间格式化
//Printf 输出字符串
fmt.Printf("当前时间:%d-%d-%d %d:%d:%d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
//Sprintf 输出并返回字符串
dateStr := fmt.Sprintf("当前时间:%d-%d-%d %d:%d:%d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
fmt.Println(dateStr)
fmt.Println("---------------------------")
//按照指定格式格式化 格式(连接符)可以变化,但是年份时间字符串必须是Go语言诞生的时间 2006年01月02日 15点04分05秒
fmt.Println(now.Format("2006-01-02 15:04:05"))
}
执行结果:

8.8利用defer+recover机制捕获异常
Golang 没有结构化异常,使用panic抛出异常,recover捕获异常。
异常的使用场景简单描述:Go中可以抛出一个panic异常,然后再defer中通过recover捕获这个异常,然后做正常处理
场景演示:
//利用defer+recover机制捕获异常
package main
import "fmt"
func main() {
var a int = 2
var b int = 0
//由于除数为0 ,报错终止
res := divisionIntRevocer(a, b)
fmt.Println("res=", res)
//下面的语句无法正常运行
var a1 int = 10
var b1 int = 5
res1 := divisionIntRevocer(a1, b1)
fmt.Println("res1=", res1)
}
func divisionIntRevocer(a int, b int) int {
// 不做异常处理,程序会报0除异常,并终止
return a / b
}
以上没有做异常捕获的代码执行结果为:

代码抛出了 0除 异常后,就被终止了,下面我们加上defer+recover 机制进行异常捕获
//defer + recover 异常捕获机制
package main
import "fmt"
func main() {
var a int = 10
var b int = 0
res := devisionIntRecover(a, b)
fmt.Println("res=", res)
var a1 int = 10
var b1 int = 5
res1 := devisionIntRecover(a1, b1)
fmt.Println("res1=", res1)
}
func devisionIntRecover(a int, b int) (ret int) {
defer func() {
err := recover()
if err != nil {
//打印异常,并关闭资源
fmt.Println("err信息", err)
ret = -3
}
}()
ret = a / b
return ret
}
执行结果如下:

8.9 自定义异常
9 GO Web编程
Gin框架,参考 https://www.topgoer.com/ gin 框架模块
9.1 基础代码
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"strings"
"fmt"
)
func main() {
r := gin.Default()
r.GET("/ping",func(c *gin.Context){
c.JSON(http.StatusOK,gin.H{
"message": "Pong",
})
})
//API参数通过Context的Param方法获取API参数
r.GET("/user/:name/*action",func(c *gin.Context){
name := c.Param("name")
action := c.Param("action")
//截取
action = strings.Trim(action, "/")
c.String(http.StatusOK,name+" is "+action)
})
// URL参数
r.GET("/super",func(c *gin.Context){
// name := c.Param("name")
//指定默认值
name := c.DefaultQuery("name","小老虎")
c.String(http.StatusOK,fmt.Sprintf("hello %s",name))
})
//表单参数
r.POST("/form",func(c *gin.Context){
types := c.DefaultPostForm("types","post")
userName := c.PostForm("username")
password := c.PostForm("userpassword")
c.String(http.StatusOK,fmt.Sprintf("userName:%s,password:%s,types:%s",userName,password,types))
})
//上传单个文件
//1、multipart/form-data 格式用于文件上传
//2、gin文件上传与原生的net/http方法类似。不同在于gin把原生的request封装到c.Request中
//限制上传最大尺寸
r.MaxMultipartMemory = 8 << 20
r.POST("/upload",func(c *gin.Context){
file,err := c.FormFile("file")
if err != nil {
c.String(500,"上传图片出错")
}
c.SaveUploadedFile(file,file.Filename)
c.String(http.StatusOK,file.Filename)
})
//多文件上传
//限制表单大小8M,默认32M
r.MaxMultipartMemory = 8 << 20
r.POST("/upload2File",func(c *gin.Context){
form,err:= c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest,fmt.Sprintf("get err %s",err.Error()))
}
//获取所有文件
files := form.File["files"]
//遍历所有文件
for _,file := range files {
//逐个保存
if err := c.SaveUploadedFile(file,file.Filename); err != nil {
c.String(http.StatusBadRequest,fmt.Sprintf("upload err %s",err.Error()))
return
}
}
c.String(http.StatusOK,fmt.Sprintf("upload completed %d files ",len(files)))
})
//routes group 是为了管理一些相同的URL 等同于 JAVA Controller类级别的 @RequestMapping()
//路由1 处理GET请求
v1 := r.Group("/v1")
//此处的{}是书写规范
{
v1.GET("/login",login)
v1.GET("/submit",submit)
}
//路由2 处理POST请求
v2 := r.Group("/v2")
{
v2.POST("/login",login)
v2.POST("/submit",submit)
}
r.Run(":9090")
}
func login(c *gin.Context){
name := c.DefaultQuery("name","小老虎")
c.String(http.StatusOK,"hello %s \n",name)
}
func submit(c *gin.Context){
name := c.DefaultQuery("name","小老虎")
c.String(http.StatusOK,"hello %s \n",name)
}
9.2 路由拆分与注册
以上基础代码中的gin路由注册方式,适用于路由条目比较少的简单项目或者项目demo,当项目的规模增大后就不太适合继续在项目的main.go 文件中取实现路由注册相关逻辑,我们会倾向于把路由部分的代码拆分出来,形成一个单独的文件或者包:
浙公网安备 33010602011771号