一、defer
package main
import (
"fmt"
"os"
"bufio"
)
func tryDefer() {
for i := 0; i < 100; i++ {
defer fmt.Println(i)
if i == 30 {
// Uncomment panic to see
// how it works with defer
panic("printed too many")
}
}
}
func writeFile(filename string) {
file, err := os.OpenFile(filename,
os.O_EXCL|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
if pathError, ok := err.(*os.PathError); !ok {//错误处理,向下转型
panic(err)
} else {
fmt.Printf("%s, %s, %s\n",
pathError.Op,
pathError.Path,
pathError.Err)
}
return
}
defer file.Close()//无论return、panic最后都会被执行
writer := bufio.NewWriter(file)
defer writer.Flush()//先进后执行,后进先执行,退出时先执行Flush,再执行Close
f := Fibonacci()
for i := 0; i < 20; i++ {
fmt.Fprintln(writer, f())
}
}
func main() {
tryDefer()
writeFile("fib.txt")
}
输出:
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
panic: printed too many
goroutine 1 [running]:
main.tryDefer()
/Users/jltxgcy/go/src/learn/errhandling/defer/defer.go:24 +0x111
main.main()
/Users/jltxgcy/go/src/learn/errhandling/defer/defer.go:56 +0x20
defer执行的顺序是先进后执行,无论是提前return还是panic,defer都会执行,用于关闭资源等,通常是成对出现。
二、recovery
package main
import (
"fmt"
)
func tryRecover() {
defer func() {
r := recover()
if r == nil {
fmt.Println("Nothing to recover. " +
"Please try uncomment errors " +
"below.")
return
}
if err, ok := r.(error); ok {
fmt.Println("Error occurred:", err)
} else {
panic(fmt.Sprintf(
"I don't know what to do: %v", r))//panic(123)
}
}()
// Uncomment each block to see different panic
// scenarios.
// Normal error
panic(errors.New("this is an error"))
// Division by zero
//b := 0
//a := 5 / b
//fmt.Println(a)
// Causes re-panic
//panic(123)
}
func main() {
tryRecover()
}
输出:
Error occurred: this is an error
打开:
b := 0
a := 5 / b
fmt.Println(a)
输出:
Error occurred: runtime error: integer divide by zero
打开:
panic(123)
输出:
panic: 123 [recovered]
panic: I don't know what to do: 123
goroutine 1 [running]:
main.tryRecover.func1()
/Users/jltxgcy/go/src/learn/errhandling/recover/recover.go:19 +0x1f6
panic(0x10ac4a0, 0x10eb4d0)
/usr/local/go/src/runtime/panic.go:679 +0x1b2
main.tryRecover()
/Users/jltxgcy/go/src/learn/errhandling/recover/recover.go:35 +0x5f
main.main()
/Users/jltxgcy/go/src/learn/errhandling/recover/recover.go:39 +0x20
总结:
recover是用来接收panic的错误的,类似于try catch,用于捕捉panic的信息,不至于让panic输出信息那么难看。
如上panic(123),123并不是error类型,所以打印panic: I don't know what to do: 123。
三、错误处理实例
1、目录结构
filelistingserver(目录)
filelisting(目录)
--handler.go
--web.go
handle.go
package filelisting
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
const prefix = "/list/"
type userError string//string也能实现接口
func (e userError) Error() string {
return e.Message()
}
func (e userError) Message() string {
return string(e)
}
func HandleFileList(writer http.ResponseWriter,
request *http.Request) error {
fmt.Println()
if strings.Index(
request.URL.Path, prefix) != 0 {//localhost:8888/abc.txt,不以list开始
return userError(
fmt.Sprintf("path %s must start "+
"with %s",
request.URL.Path, prefix))
}
path := request.URL.Path[len(prefix):]
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
}
web.go
package main
import (
"log"
"net/http"
_ "net/http/pprof"
"os"
"learn/errhandling/filelistingserver/filelisting"
)
type appHandler func(writer http.ResponseWriter,
request *http.Request) error
func errWrapper(
handler appHandler) func(
http.ResponseWriter, *http.Request) {//参数和返回值都是函数
return func(writer http.ResponseWriter,
request *http.Request) {
// panic
defer func() {
if r := recover(); r != nil {//统一处理panic
log.Printf("Panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
err := handler(writer, request)//HandleFileList
if err != nil {
log.Printf("Error occurred "+
"handling request: %s",
err.Error())
// user error
if userErr, ok := err.(userError); ok {//localhost:8888/abc.txt,不以/list开始
http.Error(writer,
userErr.Message(),
http.StatusBadRequest)
return
}
// system error
code := http.StatusOK
switch {
case os.IsNotExist(err)://本地没有abc.txt
code = http.StatusNotFound
case os.IsPermission(err)://本地abc.txt没有权限访问
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer,
http.StatusText(code), code)
}
}
}
type userError interface {//定义接口
error
Message() string
}
func main() {
http.HandleFunc("/",
errWrapper(filelisting.HandleFileList))
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
启动server,在浏览器输入localserver:8888/list/abc.txt,就可以显示本地目录下的abc.txt。