今日内容

154 阅读5分钟
init函数

init函数就是在程序执行之前被调用的函数,不需要传入参数也没有返回值,而且init函数是不能被其他函数调用的。

init函数的作用:

  • 变量初始化
  • 检查和修复程序状态
  • 运行前注册,例如decoder,parser的注册
  • 运行只需计算一次的模块,像sync.once的作用
  • 其他

init函数执行顺序

image.png

这是引用多个包,不同包之间init函数执行的顺序。当同一个包中不同的文件有多个init函数时,则按文件的字典序进行初始化;若一个文件有多个init函数,则按函数的位置从上到下进行执行。

执行顺序总结:从当前包开始,如果当前包包含多个依赖包,则先初始化依赖包,层层递归初始化各个包,在每一个包中,按照源文件的字典序从前往后执行,每一个源文件中,优先初始化常量、变量,最后初始化init函数,当出现多个init函数时,则按照顺序从前往后依次执行,每一个包完成加载后,递归返回,最后在初始化当前包!

小例子:

var a = first()

func init() {
   fmt.Println("this is init function")
}

func main() {
   fmt.Println("this is main function")
}

func first() int {
   fmt.Println("我比init先初始化")
   return 0
}

输出:

image.png

有时候我们在导入包前加_表示只是想执行导入包中的init函数。

import _ "image/png"

image/png包里的init函数作用是向image包注册png图片的解码器,见src/image/png/reader.go

常用的init()作用是初始化在函数变量申明时无法初始化的变量:

var square [10]int

func init() {
   //fmt.Println("this is init function")
      for i := 0; i < 10; i++ {
         square[i] = i * i
      }
   
}

error 减少erro的技巧:

当程序中要多次调用一个函数并多次处理返回的error时可以将error与方法调用包装起来,避免重复error处理。

type wrapWrite struct {
   w   io.Writer
   err error
}

func (w *wrapWrite) write(bytes []byte) {
   if w.err != nil {
      return
   }
   _,w.err = w.w.Write(bytes)
}

func (w *wrapWrite) getErr()error {
   return w.err
}

func main() {
   s1 := "123"
   s2 := "456"

   newWrite := &wrapWrite{}
   newWrite.write([]byte(s1))
   newWrite.write([]byte(s2))

   if newWrite.getErr() != nil {
      //do something
   }

}

panic:

  • go中一旦使用的panic,就是想要程序终止。
  • 虽然go有recover的机制来恢复panic,但是在写panic时,不能假设调用者会使用recover。这和try-catch-exception机制中,抛出的exception可认为调用者有义务try-catch的思想是不同的。
  • 在写一般函数方法时,如果不是类似索引越界,栈内存溢出等无法恢复的错误,尽量不去使用panic。
  • 在项目启动时,资源初始化如加载配置文件、数据库连接等这种一旦失败了,整个项目没有进行下去的意义时,会使用panic。但是也有一些被弱依赖的资源初始化,可能会有一些不同,这要根据项目的实际情况来定。 recover
  • 一般来说,出现了panic就不需要再救了。
  • 但在类似web服务的框架中,不能由于某一个请求中出现了panic而导致整个服务gg。所以在框架层面会在每一个请求调用链的最上游统一做recover。 当recover和panic未在一个携程中时就不起作用了
func do() {
   panic(errors.New("panic in do"))
}

func DoSomething() {
   defer func() {
      if err := recover();err != nil {
         // handle
         log.Printf("panic被recover了 %v",err)
      }
   }()
   // 无法捕获新启动的协程
   go do()
}

session和cookie

cookie,就是在本地计算机保存一些用户操作的历史信息(当然包括登录信息),并在用户再次访问该站点时浏览器通过HTTP协议将本地cookie内容发送给服务器,从而完成验证,或继续上一步操作。 流程:

image.png

session,就是在服务器上保存用户操作的历史信息。服务器使用session id来标识session,session id由服务器负责产生,保证随机性与唯一性,相当于一个随机密钥,避免在握手或传输中暴露用户真实密码。但该方式下,仍然需要将发送请求的客户端与session进行对应,所以可以借助cookie机制来获取客户端的标识(即session id),也可以通过GET方式将id提交给服务器。

seesionId可以有两种方式存放方式:

  • 经过url传递
  • 放在cookie里面

image.png

hst框架中的session

image.png Session接口中定义了对session的操作,框架中有两种该接口的实现方式,一个是session存储在内存中,一个是session存储在文件中。

image.png

在sessionMemory中定义了SessionMemory结构体来存储每个用户对应的Session

type SessionMemory struct {
   cookieName   string
   cookiePath   string
   cookieDomain string
   cookieExpire time.Duration
   lock         sync.RWMutex
   data         map[string]*map[string]*sessionData  //存储session信息 第一个string为cookie标识
   maxExpire    time.Duration // 文件过期时间
}

image.png 新建全局SeesionMemory时,新开一个协程来清除保存在SessionMemory中的过期session

image.png set方法流程:

  1. 查看请求中是否已经存在生成的cookie
  2. 若不存在,新生成一个cookie填充进请求中,生成的value表示seesionId,对应用户访问的保存在服务端的信息
  3. 若存在,则更新sesrrion中数据的过期时间;不存在则将key,value保存进sessionData中

image.png get方法流程:

  1. 根据cookieName获取请求中cookie,再根据value和key获取sesionData中保存的数据
  2. 如果不存在该cookie则直接返回,未找到key对应SeesionData也返回

image.png 销毁当前访问用户存储在SessionMemory中的Session

image.png Hst框架在context中对session操作进行了进一步封装。

简单小例子:登录操作

初始化路由:

h := hst.New(nil)

//初始化session
h.SetSession(hst.NewSessionMemory("", "/", "HST_SESSION", time.Minute))

h.HandleFunc("/addUser", controller.CreateController)
h.HandleFunc("/getUserList", controller.GetUserList)
h.RegisterHandle(nil, &controller.User{})

handler:

func (user *User) LoginCheck(c *hst.Context) {

   c.R.ParseForm()
   username := c.R.FormValue("name")
   password := c.R.FormValue("password")

   quesUser, err := service.GetUserByName(username)

   if err != nil {
      c.JSON(http.StatusBadRequest, err)
   }
   if quesUser.Password != password {
      c.JSON(http.StatusBadRequest, "用户密码错误")
   }

   //登录操作成功 用户信息写入session 返回给客户端
   c.SessionSet("userInfo", user)

   c.JSON(http.StatusOK, struct { //常见错误
      Id   int
      Name string
      Msg  string
   }{
      Id:   quesUser.Id,
      Name: quesUser.Name,
      Msg:  "登录成功",
   })

   //c.JSON2(http.StatusOK, 0, quesUser)
}

问题: 实际中session的应用场景? 该怎么用?

今日踩坑

image.png

image.png

当用该函数返回结果时,第二个参数中的struct中字段的首字符没有大写,导致后面将该数据解析为json数据时,解析为空,且不会报错。 要进行marshaled/unmarshaled,结构体的字段必须被导出(定义为公有)