go 语言学习12--defer和异常处理

2018-08-12  本文已影响18人  神奇大叶子

defer

go里有个关键字defer

func tryDefer1() {
    defer fmt.Println(1)
    fmt.Println(2)
}

func tryDefer2() {
    defer fmt.Println(1)
    defer fmt.Println(2)
    fmt.Println(3)
}

这两段代码会输出什么呢
tryDefer1: 2 1
tryDefer1: 3 2 1

所以 defer有两个特点

这种特性很方便,可以用在很多资源释放的场合

异常处理

go里没有那么麻烦的 try catch,scala里同样不推崇传统的java那种异常捕获,感觉这是个新的思维,还得多学习学习
举个🌰

func writeFile(filename string) {
    file, err := os.Create(filename)
    if err != nil {
        panic(err)
    }
    defer file.Close()
    write := bufio.NewWriter(file)
    defer write.Flush()

    f := fib.Fib()
    for i := 0; i < 20; i++ {
        fmt.Fprint(write, strconv.Itoa(f())+"\n")
    }
}

我想把 fib 数写进一个文件里,有经验的同学都知道,打开一个文件需要捕获异常,因为这个文件可能不存在,或者写入因为各种权限问题时报错
go里很多函数都是两个返回值,第一个是结果,第二个是异常

go很方便在于,你打开一个文件,你就顺手 defer close掉,不用特意包裹在麻烦的catch
用一个bufio写效率会高很多,记得要 flush到磁盘里
Fprint接收一个 writer

再来看个异常处理的例子
打开一个文件,都知道文件可能不存在会抛出异常

image
不直接panic
http.Error 有三个参数,第一个是 write 也就是你的网页,第二个是出错信息,第三个是 code
随便输错一个再来看
image
可是这样也不太好,程序的报错不应该暴露给用户,只需要让用户知道 not found 就行了

把整个 handler 函数抽出来

package filelisting

import (
    "net/http"
    "os"
    "io/ioutil"
)

func Handler(writer http.ResponseWriter, request *http.Request) error {
    path := request.URL.Path[len("/list/"):]
    file, err := os.Open(path)
    if err != nil {
        return err
    }
    defer file.Close()
    all, err := ioutil.ReadAll(file)
    if err != nil {
        return err
    }
    writer.Write(all)
    return nil
}

返回值是 error
定义一个结构体,对应这个handler 函数

type appHandler func(writer http.ResponseWriter, request *http.Request) error

下面用到函数式编程的思想
传这个 error 进去,但是返回 http.HandleFunc 需要的函数
有点像python的装饰器

func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
    return func(writer http.ResponseWriter, request *http.Request) {
        err := handler(writer, request)
        if err != nil {
            log.Warn("Error handling request: %s",
                err.Error())
            var code int
            switch {
            case os.IsNotExist(err):
                code = http.StatusNotFound
            case os.IsPermission(err):
                code = http.StatusForbidden
            default:
                code = http.StatusInternalServerError
            }
            http.Error(writer, http.StatusText(code), code)
        }
    }
}

一个装饰 error 的函数,返回也是一个函数
在函数体内包装 error
整个程序最后是这样的

package main

import (
    "net/http"
    "golearn/lesson11/filelistserver/filelisting"
    "os"
    "github.com/gpmgo/gopm/modules/log"
)

type appHandler func(writer http.ResponseWriter, request *http.Request) error

func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
    return func(writer http.ResponseWriter, request *http.Request) {
        err := handler(writer, request)
        if err != nil {
            log.Warn("Error handling request: %s",
                err.Error())
            var code int
            switch {
            case os.IsNotExist(err):
                code = http.StatusNotFound
            case os.IsPermission(err):
                code = http.StatusForbidden
            default:
                code = http.StatusInternalServerError
            }
            http.Error(writer, http.StatusText(code), code)
        }
    }
}

func main() {
    http.HandleFunc("/list/", errWrapper(filelisting.Handler))
    err := http.ListenAndServe(":8888", nil)
    if err != nil {
        panic(err)
    }
}
image

最后页面返回就是想要的结果,而不是直接暴露错误给用户

recover

说道panic就要说recover


func tryRecover() {
    defer func() {
        r := recover()
        if err, ok := r.(error); ok {
            fmt.Println("Error occurred:", err)
        } else {
            panic(r)
        }
    }()
    panic(errors.New("this is a new error"))
}

panicrecover有点像try catch其实就是c语言的try catch
我的程序出错了(panic)但是我不想让他让程序终止,在我可控的范围内处理他(recover)
当我发现他是我知道的类型(error),我处理他,如果是我意料之外的东西,我还可以继续panic

上面的 http 服务器的例子都是系统异常直接抛出了,实际上,预料的异常应该 recover,还要自定义一些异常作为给用户看的

image
image
在 Handler 这个方法的文件里,实现 userError 这个接口,抛出一个用户异常
详细代码看github

总结

上述代码均已上传至 github, 欢迎 star
https://github.com/yejunyu/golearn


image
上一篇 下一篇

猜你喜欢

热点阅读