一.defer调用:实现资源管理
- 确保调用在函数结束时发生
- 参数在defer语句时计算
- defer列表为后进先出
何时使用defer调用
- Open/Close
- Lock/Unlock
- PrintHeader/PrintFooter
package main
import "fmt"
func tryDefer(){
defer fmt.Println(1)
defer fmt.Println(2)//defer 相当于栈:先进后出
fmt.Println(3)
//结果:3 2 1
}
func main() {
tryDefer()
}
package main
import (
"../../functional/fib"
"bufio"
"fmt"
"os"
)
func tryDefer() {
defer fmt.Println(1)
defer fmt.Println(2) //defer 相当于栈:先进后出
fmt.Println(3)
//结果:3 2 1
//return
panic("error occurred")
fmt.Println(4)
//3
//2
//1
//panic: error occurred
}
//参数在defer语句时计算
func tryDefer2() {
for i := 0; i < 100; i++ {
defer fmt.Println(i) //
if i == 10 {
panic("print too many")
}
}
}
func writeFile(filename string) {
file, err := os.Create(filename)
if err != nil {
panic(err)
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
f := fib.Fibonacci()
for i := 0; i < 10; i++ {
fmt.Fprintln(writer, f())
}
}
func main() {
//tryDefer()
tryDefer2()
writeFile("fib.txt")
}
二.错误处理理念
func writeFile(filename string) {
//file, err := os.Create(filename)
file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666) //O_EXCL 如果文件存在的话打开不了
// panic: open fib.txt: file exists
err = errors.New("this is a custum error")
if err != nil {
//fmt.Println("Error:",err.Error())
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()
writer := bufio.NewWriter(file)
defer writer.Flush()
f := fib.Fibonacci()
for i := 0; i < 10; i++ {
fmt.Fprintln(writer, f())
}
}
三.服务器统一出错处理
web.go
package main
import (
"./filelisting"
"log"
"net/http"
"os"
)
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) {
err := handler(writer, request)
if err != nil {
log.Printf("Error handing request:%s",err.Error()) //2020/08/29 17:17:36 Error handing request:open fib2.txt: permission denied
code := http.StatusOK
switch {
case os.IsNotExist(err):
//http.Error(writer, http.StatusText(http.StatusNotFound), http.StatusNotFound)
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.HandleFileList))
err := http.ListenAndServe(":8081", nil)
if err != nil {
panic(err)
}
}
handler.go
package filelisting
import (
"io/ioutil"
"net/http"
"os"
)
func HandleFileList(writer http.ResponseWriter, request *http.Request) error {
path := request.URL.Path[len("/list/"):]
file, err := os.Open(path)
if err != nil {
//panic(err)
/*http.Error(writer,
err.Error(),
http.StatusInternalServerError)
return*/
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
//panic(err)
return err
}
writer.Write(all)
return nil
}
四.panic和recover
panin功能作用
- 停止当前函数执行
- 一直向上返回,执行每一层的defer
- 如果没有遇见recover,程序退出
recover功能作用
- 仅在defer调用中使用
- 获取panic的值
- 如果无法处理,可重新panic
package main
import (
"fmt"
)
func tryRecover() {
defer func() {
r := recover()
if err, ok := r.(error); ok {
fmt.Println("Error occurred:", err)
} else {
fmt.Println(r)
}
}()
//panic(errors.New("this is an error")) //Error occurred: this is an error,下面不执行,panic停止当前函数执行
b := 0
a := 5 / b
fmt.Println(a) //Error occurred: runtime error: integer divide by zero
}
func main() {
tryRecover()
}
五.服务器统一出错处理
- 意料之中的:使用error,如文件打不开
- 意料之外的:使用panic,如数组越界