下载go并安装(win下),环境变量他自己要配置上
https://dl.google.com/go/go1.21.3.windows-amd64.msi
验证是否安装成功:
//打开cmd
go version
下载VSCODE…略
配置VSCODE的环境
下载插件
打开cmd,或者VSCODE自带的终端,逐条复制
//打开go mod ,go mod相当于java的maven
go env -w GO111MODULE=on
//设置代理
go env -w GOPROXY=https://goproxy.cn,direct
然后再在VSCODE上下载工具包
Ctrl+Shift+P
输入go
点第一个,安装工具包
全选,确定
等待下载
go modules是官方推荐的替换GOPATH而诞生的依赖库管理工具(相当于java的maven),之前的包都放在GOPATH了
首先自己下载的go路径应该是在C盘
go env
这样创建一个初始化案例(先转到需要初始化的文件下)
go mod init example.com/m
发现出现了像配置文件的东西,这是用于记录依赖库和版本号的东西
如果使用网上说的初始化
PS D:\work\GoWork\modtest> go mod init
go: cannot determine module path for source directory D:\work\GoWork\modtest (outside GOPATH, module path must be specified)
Example usage:
'go mod init example.com/m' to initialize a v0 or v1 module
'go mod init example.com/m/v2' to initialize a v2 module
Run 'go help mod init' for more information.
会提示没有指定module
意思是后面要取一个模块的名字,打开生成的go.mod文件就能看见,说明这个模块名字是module
go的程序中一行就表示一个语句的结束,如果要多个语句写一行就必须用";"人为区分,实际开发中不用写分号
go的注释
//单行注释
/*
多行注释
*/
标识符:用来命名变量.类型等程序实体,一个标识符就是一个或多个字母和数字,下划线组成,第一个字符不能是数字(就是java的变量名)
字符串连接
package main
import "fmt"
func main() {
fmt.Println("Google" + "Runoob")
//输出GoogleRunoob
}
GO的空格
分隔标识符、关键字、运算符和表达式,以提高代码的可读性
一般VSCODE会自动帮忙填空格
在函数调用时,函数名和左边等号之间要使用空格,参数之间也要使用空格。
例如:
result := add(2, 3)
关键字
下面列举了 Go 代码中会使用到的 25 个关键字或保留字:
break | default | func | interface | select |
---|---|---|---|---|
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
除了以上介绍的这些关键字,Go 语言还有 36 个预定义标识符:
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
---|---|---|---|---|---|---|---|---|
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
程序一般由关键字、常量、变量、运算符、类型和函数组成。
程序中可能会使用到这些分隔符:括号 (),中括号 [] 和大括号 {}。
程序中可能会使用到这些标点符号:.、,、;、: 和 …。
按照语言分:
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
4 | 派生类型: 包括:(a) 指针类型(Pointer)(b) 数组类型© 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型 |
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
以下列出了其他更多的数字类型:
序号 | 类型和描述 |
---|---|
1 | byte 类似 uint8 |
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
单变量,多变量,全局变量,变量类型
package main
import (
"fmt"
)
// 声明全局变量 方法一,方法二,方法三
var gA int = 100
var gB = "qwd"
//方法四的 :=只能用在函数体内
//gc :=200
func main() {
//方法一:声明一个变量,默认值是0
var a int
fmt.Println("a = ", a)
//方法二:声明一个变量,初始化一个值
var b int = 100
fmt.Println("b = ", b)
//方法三:初始化值的时候,省去数据类型,自动匹配数据类型
var c = 1
fmt.Println("c =", c)
var d = "1"
fmt.Printf("d=%s ,type of d = %T\n", d, d) //输出这里用的Printf,,输出类型
//方法四,常用的,省去var关键字,自动匹配
e := 100.12
fmt.Println("e=", e)
//声明多变量
//var xx,yy int=100,120
var xx, yy = "a0", 12
fmt.Println("xx=", xx, ",yy=", yy)
//多行的,多变量声明
var (
vv int = 100
jj bool = true
)
fmt.Println("vv=", vv, ",jj=", jj)
}
int的零值为0
bool的零值为false
字符串的零值为""
下面类型的零值为nil
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error 是接口
使用等号就是将一个变量的值赋值给另一个变量,例如j=i,实际上是在内存中将i的值进行了拷贝
获取内存地址: &i
也称为指针
变量需要被使用,不然会报错
极简的混合赋值:
a, b, c := 5, 7, "abc"
Go 语言中使用 fmt.Sprintf 或 fmt.Printf 格式化字符串并赋值给新串:
在程序运行时不会改变的量
const b = "ab"
常量枚举
const(
Unknown=0
Female=1
Male=2
)
常量函数使用
可以使用len(),cap(), unsafe.Sizeof()计算值,函数必须是内置函数
package main
import "unsafe"
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)
func main(){
println(a, b, c)
}
iota(特殊常量)
可被编译器修改的常量,在const关键词出现时重置为0,
在const中每新增一行常量声明就使iota计数一次
用法:
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
//0 1 2 ha ha 100 100 7 8
}
算术运算符
下表列出了所有Go语言的算术运算符。假定 A 值为 10,B 值为 20。
运算符 | 描述 | 实例 |
---|---|---|
+ | 相加 | A + B 输出结果 30 |
- | 相减 | A - B 输出结果 -10 |
* | 相乘 | A * B 输出结果 200 |
/ | 相除 | B / A 输出结果 2 |
% | 求余 | B % A 输出结果 0 |
++ | 自增 | A++ 输出结果 11 |
– | 自减 | A-- 输出结果 9 |
关系运算符
下表列出了所有Go语言的关系运算符。假定 A 值为 10,B 值为 20。
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 | (A == B) 为 False |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 | (A != B) 为 True |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 | (A > B) 为 False |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 | (A < B) 为 True |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 | (A >= B) 为 False |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 | (A <= B) 为 True |
逻辑运算符
下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 | (A && B) 为 False |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 | (A || B) 为 True |
! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 | !(A && B) 为 True |
位运算符
位运算符对整数在内存中的二进制位进行操作。
下表列出了位运算符 &, |, 和 ^ 的计算:
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
赋值运算符
下表列出了所有Go语言的赋值运算符。
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 | C = A + B 将 A + B 表达式结果赋值给 C |
+= | 相加后再赋值 | C += A 等于 C = C + A |
-= | 相减后再赋值 | C -= A 等于 C = C - A |
*= | 相乘后再赋值 | C *= A 等于 C = C * A |
/= | 相除后再赋值 | C /= A 等于 C = C / A |
%= | 求余后再赋值 | C %= A 等于 C = C % A |
<<= | 左移后赋值 | C <<= 2 等于 C = C << 2 |
>>= | 右移后赋值 | C >>= 2 等于 C = C >> 2 |
&= | 按位与后赋值 | C &= 2 等于 C = C & 2 |
^= | 按位异或后赋值 | C ^= 2 等于 C = C ^ 2 |
|= | 按位或后赋值 | C |= 2 等于 C = C | 2 |
下表列出了Go语言的其他运算符。
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量存储地址 | &a; 将给出变量的实际地址。 |
* | 指针变量。 | *a; 是一个指针变量 |
Go 语言提供了以下几种条件判断语句:
语句 | 描述 |
---|---|
if 语句 | if 语句 由一个布尔表达式后紧跟一个或多个语句组成。 |
if…else 语句 | if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。 |
if 嵌套语句 | 你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。 |
switch 语句 | switch 语句用于基于不同条件执行不同动作。 |
select 语句 | select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。 |
注意:Go 没有三目运算符,所以不支持 ?: 形式的条件判断。
switch var1 {
case val1:
...
case val2:
...
default:
...
}
计算1到10的数字之和
//没得括号,而且{必须放在后面
package main
import "fmt"
func main() {
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Println(sum)
}
使用循环外变量
package main
import "fmt"
func main() {
sum := 1
for ; sum <= 10; {
sum += sum
}
fmt.Println(sum)
// 这样写也可以,更像 While 语句形式
for sum <= 10{
sum += sum
}
fmt.Println(sum)
}
死循环
就一个for加花括号
package main
import "fmt"
func main() {
sum := 0
for {
sum++ // 无限循环下去
}
fmt.Println(sum) // 无法输出
}
停止死循环就用 ctrl+C
迭代输出
package main
import "fmt"
func main() {
strings := []string{"google", "runoob"}
for i, s := range strings {
fmt.Println(i, s)
}
numbers := [6]int{1, 2, 3, 5}
for i, x := range numbers {
fmt.Printf("第 %d 位 x 的值 = %d\n", i, x)
}
}
迭代输出省略key和value
package main
import "fmt"
func main() {
map1 := make(map[int]float32)
map1[1] = 1.0
map1[2] = 2.0
map1[3] = 3.0
map1[4] = 4.0
// 读取 key 和 value
for key, value := range map1 {
fmt.Printf("key is: %d - value is: %f\n", key, value)
}
// 读取 key
for key := range map1 {
fmt.Printf("key is: %d\n", key)
}
// 读取 value
for _, value := range map1 {
fmt.Printf("value is: %f\n", value)
}
}
输出
key is: 4 - value is: 4.000000
key is: 1 - value is: 1.000000
key is: 2 - value is: 2.000000
key is: 3 - value is: 3.000000
key is: 1
key is: 2
key is: 3
key is: 4
value is: 1.000000
value is: 2.000000
value is: 3.000000
value is: 4.000000
最少有个main()函数
命名格式:
func function_name( [parameter list] ) [return_types] {
函数体
}
func:函数由 func 开始声明
function_name:函数名称,参数列表和返回值类型构成了函数签名。
parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
函数体:函数定义的代码集合。
例子:
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 声明局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
GO函数可以返回多个值
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Google", "Runoob")
fmt.Println(a, b)
}
函数的值传递
/* 定义相互交换值的函数 */
func swap(x, y int) int {
var temp int
temp = x /* 保存 x 的值 */
x = y /* 将 y 值赋给 x */
y = temp /* 将 temp 值赋给 y*/
return temp;
}
后面的int是指(int,int),好像可以简写
引用传递
/* 定义交换值函数*/
func swap(x *int, y *int) {
var temp int
temp = *x /* 保持 x 地址上的值 */
*x = *y /* 将 y 值赋给 x */
*y = temp /* 将 temp 值赋给 y */
}
函数内定义的变量称为局部变量
函数外定义的变量称为全局变量
函数定义中的变量称为形式参数
例子:
var balance [10]float32
var numbers [5]int
var numbers = [5]int{1, 2, 3, 4, 5}
numbers := [5]int{1, 2, 3, 4, 5}
多维数组:
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}, /* 第三行索引为 2 */
}
var threedim [5][10][4]int
函数里的数组:
void myFunction(param []int)
{
.
.
.
}
取地址符&
取值 *
package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
空指针
if(ptr != nil) /* ptr 不是空指针 */
if(ptr == nil) /* ptr 是空指针 */
指针交换
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int= 200
a, b = b, a
fmt.Printf("交换后 a 的值 : %d\n", a )
fmt.Printf("交换后 b 的值 : %d\n", b )
}
相当于对象
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
// 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})
// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})
// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})
//访问结构成员
var book1 Books
book1.author = "这是作者"
book1.book_id = 123
fmt.Println(book1)
fmt.Println(book1.author)
}
相当于arraylist,可以动态追加元素的数组(动态数组)
切片不需要说明长度。
或使用 make() 函数来创建切片:
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
len()和cap()函数
len()是计算长度的,cap()是测量切片最长长度(额外长度?)
切片截取
package main
import "fmt"
func main() {
/* 创建切片 */
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
printSlice(numbers)
/* 打印原始切片 */
fmt.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int, 0, 5)
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
append()和copy()
例:
numbers = append(numbers,-2)//追加数字"-2"
numbers = append(numbers,-2,2,99)//追加数字"-2,2,99"
//创建切片numbers1,为上个切片的两倍容量
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
//从numbers拷贝到numbers1
copy(numbers1,numbers)
用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素
for key, value := range numbers {
fmt.Printf("%d-->%d", key, value)
}
//key在map之类的正常用,在切片/数组之类的key是数组索引
可以迭代字符串,迭代出来是Unicode
键值对
//创建一个空map,键是string,值是int
m := make(map[string]int)
//创建带有初始容量的
m := make(map[string]int,10)
//创建自带键值对的
// 使用字面量创建 Map
m := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
// 获取键值对
value := m["apple"]
value, ok := m["pear"]
//如果键不存在,ok 的值为 false,v2 的值为该类型的零值
//这里不能用bool,因为bool是关键字
//也记得不要取名为map,map也是关键字,小写有些是关键字
//修改元素
m["apple"]=5
//获取Map的长度
len:=len(m)
//遍历
for k,v:=range m{
fmt.Printf("key=%s,value=%d\n",k,v)
}
//删除元素
delete(m,"banana")
记得写好退出条件
func recursion() {
recursion() /* 函数调用自身 */
}
func main() {
recursion()
}
整形转浮点
var a int = 10
var b float64 = float64(a)
//貌似像强转,但是换了括号位置
字符串转成整数
var str string="10"
var num int
num,_=strconv.Atoi(str)
//conv的意思是转换器/卷积
//Atoi的意思是ASCII to Integer
整数转换成字符串
num := 123
str := strconv.Itoa(num)
浮点转换字符串
num := 3.14
str := strconv.FormatFloat(num, 'f', 2, 64)
//f是格式说明符, 2是精度 小数点后两位, 64是表示浮点的位数 表示双精度
可以兼容的转换(int8转int64之类的)
var i8 int8 = 12
var i64 = int64(i)
接口可以让我们将不同类型绑定到一组公共方法上,从而实现多态和灵活的设计
Go的接口是隐式实现的(如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口)
package main
import (
"fmt"
)
//接口类型
type Phone interface {
call()
}
//诺基亚手机类型,用结构体代替对象
type NokiaPhone struct {
str string
}
//诺基亚手机的call方法 ,带有参数,是为了类似于java中的this
func (nokiaPhone NokiaPhone) call() {
nokiaPhone.str = "I am Nokia"
fmt.Println(nokiaPhone.str, ", I can call you! ")
}
//苹果手机的结构体
type IPhone struct {
}
//苹果手机的call方法
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
内置错误接口提供了简单的错误处理机制
error类型是一个接口类型
在返回值的后面加上错误处理
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
自定义异常结构(类)
package main
import (
"fmt"
)
// 定义一个 DivideError 结构
type DivideError struct {
dividee int
divider int
}
// 实现 `error` 接口
func (de *DivideError) Error() string {
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}
// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}
}
func main() {
// 正常情况
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 当除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}
通过关键字开启goroutine
goroutine就是轻量级线程,
//开启新线程方法
go func1(x,y,z)
//go是关键字,func1是方法,xyz是参数
通道(channel)
传递数据的一个数据结构
用于两个goroutine传递指定类型的值来同步运行和通讯
操作符<-用于指定通道方向,没有指定方向就是表示双向通道
//channel创建
ch :=make(chan int)
ch <- v //把v发送给ch
v :<- ch //从ch里接收数据,赋值给v
默认情况不带缓冲区,意思就是像水龙头接水一样,错过了就不管
带缓冲的通道
ch :=make(chan int, 100)//100是指,可hu
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
// 会结束,从而在接收第 11 个数据的时候就阻塞了。
for i := range c {
fmt.Println(i)
}
}
小练习:
Gin是一个 go 写的 web 框架,具有高性能的优点。官方地址:https://github.com/gin-gonic/gin
Gin用下面语句导一下包,就能用
go get -u github.com/gin-gonic/gin
Gin的简单实践:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"os"
"time"
)
func main() {
router := gin.Default()
router.Use(RequestTimingMiddleware)
router.GET("/hi", func(context *gin.Context) {
context.JSON(200, gin.H{"msg": "hello"})
})
router.GET("/healthz", func(context *gin.Context) {
//获取请求头
requestHeader := context.Request.Header
//添加VERSION环境变量
requestHeader.Add("VERSION", os.Getenv("VERSION"))
//Server 端记录访问日志包括客户端 IP,HTTP 返回码,输出到 server 端的标准输出
fmt.Printf("Client IP为%s, HTTP Status: %d, Method: %s, Path: %s\n", context.ClientIP(), context.Writer.Status(), context.Request.Method, context.Request.URL.Path)
//迭代设置请求头
for key, values := range requestHeader {
for _, value := range valu2es {
context.Writer.Header().Set(key, value)
}
}
//返回
context.JSON(200, gin.H{"msg": "返回请求"})
})
router.Run(":8099")
}
// 时间计算中间件
func RequestTimingMiddleware(c *gin.Context) {
startTime := time.Now()
c.Next()
endTime := time.Now()
countTime := endTime.Sub(startTime)
fmt.Printf("耗时: %v\n", countTime)
}