Go资源与出错处理
Posted ycx95
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go资源与出错处理相关的知识,希望对你有一定的参考价值。
2018-07-25
1. 资源管理:defer
确保调用在函数结束时发生
参数在defer语句时计算
defer列表为先进后出
error vs panic : 意料之中用error比如文件打不开,意料之外用panic比如数组越界
注意:示例用到defer+panic+recover 用到Type Assertion 函数式编程很重要
例子1
创建目录:errorhandling下面再创建目录defer
//defer里面是相当于有一个栈 先进后出 defer好处:不怕中间有return
func tryDefer() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) //第一种:return panic("eror occurred") //第二种 fmt.Println(4) }//输出是3 2 1
例子2
详情参加functional https://www.cnblogs.com/ycx95/p/9362175.html
package main import ( "fmt" "os" "bufio" "imooc.com/ccmouse/learngo/functional/fib" ) func tryDefer() { for i := 0; i < 100; i++ { defer fmt.Println(i) if i == 30 { //由于现进后出 输出就是:30 29 ... 1 // 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) //err只是一个值 可以针对值进行处理 if err != nil { //错误处理 细致的错误处理 如果文件存在:输出会提示文件存在 if pathError, ok := err.(*os.PathError); !ok { panic(err) } else { fmt.Printf("%s, %s, %s ", pathError.Op, pathError.Path, pathError.Err) } return } defer file.Close() //file写完了要关闭 writer := bufio.NewWriter(file) // 直接file写文件很慢,因此用bufio包装一下 defer writer.Flush() //导进新创建的fib.txt文件里面 f := fib.Fibonacci() for i := 0; i < 20; i++ { //写前20个fib数列 fmt.Fprintln(writer, f()) } } func main() { tryDefer() writeFile("fib.txt") //运行程序这里会创建一个fib.txt文件 }
2. 错误处理
错误处理一:人为处理
制造一个错误(程序会挂掉)
将原来的: file,err := os.Create(filename) //Create是openfile加了一些状态 直接修改为: file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE|os.O_WRONLY, 0666) //0666是一个权限
处理挂掉的错误:如何对err进行细化处理(详见上面代码writefile的error处理部分
自己写error:err = errors.New("this is a custom error“) 其实error就是一个interface
错误处理: file,err := os.Open("abc.txt") if err!= nil { if pathError,ok := err.(*os.PathError);ok { fmt.Println(pathError.Err) }else{ fmt.Println("unknown error",err) }
错误处理二:统一错误处理
新建目录:errorhanding目录下创建 filelistingserver 目录
handler.go ( 位置 errorhandling/filelistingserver/filelisting)
package filelisting import ( "fmt" "io/ioutil" "net/http" "os" "strings" ) const prefix = "/list/" type userError 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 { 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 //有错就return出去外面有处理 } defer file.Close() all, err := ioutil.ReadAll(file) if err != nil { return err } writer.Write(all) return nil }
web.go(位置 errorhandling/filelistingserver)
package main import ( "log" //这里视频中所写是 github.com/gpmgo/gopm/modules/log "net/http" _ "net/http/pprof" "os" "imooc.com/ccmouse/learngo/errhandling/filelistingserver/filelisting" ) //返回一个err type appHandler func(writer http.ResponseWriter, request *http.Request) error //包装errHandler返回的err,然后返回一个函数 特点:输入是一个函数 输出也是一个函数 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 { log.Printf("Panic: %v", r) http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } }() err := handler(writer, request) //处理err进行处理 if err != nil { log.Printf("Error occurred "+ "handling request: %s", err.Error()) // user error if userErr, ok := err.(userError); ok { http.Error(writer, userErr.Message(), http.StatusBadRequest) return } // system error code := http.StatusOK 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) } } } type userError interface { error Message() string } func main() { http.HandleFunc("/", errWrapper(filelisting.HandleFileList)) err := http.ListenAndServe(":8888", nil) //开服务器 打开服务器输入:localhost:8888/list/fib.txt if err != nil { panic(err) } }
//测试时候:管理员目录时候cp fib.txt fib2.txt 浏览器可以访问 让别人没有权限:chmod 500 fib2.txt 浏览器输出:Forbidden
errwrapper_test.go(位置 errorhandling/filelistingserver)
package main import ( "errors" "fmt" "io/ioutil" "net/http" "net/http/httptest" "os" "strings" "testing" ) func errPanic(_ http.ResponseWriter, _ *http.Request) error { panic(123) } type testingUserError string func (e testingUserError) Error() string { return e.Message() } func (e testingUserError) Message() string { return string(e) } func errUserError(_ http.ResponseWriter, _ *http.Request) error { return testingUserError("user error") } func errNotFound(_ http.ResponseWriter, _ *http.Request) error { return os.ErrNotExist } func errNoPermission(_ http.ResponseWriter, _ *http.Request) error { return os.ErrPermission } func errUnknown(_ http.ResponseWriter, _ *http.Request) error { return errors.New("unknown error") } func noError(writer http.ResponseWriter, _ *http.Request) error { fmt.Fprintln(writer, "no error") return nil } var tests = []struct { h appHandler code int message string }{ {errPanic, 500, "Internal Server Error"}, {errUserError, 400, "user error"}, {errNotFound, 404, "Not Found"}, {errNoPermission, 403, "Forbidden"}, {errUnknown, 500, "Internal Server Error"}, {noError, 200, "no error"}, } func TestErrWrapper(t *testing.T) { for _, tt := range tests { f := errWrapper(tt.h) response := httptest.NewRecorder() request := httptest.NewRequest( http.MethodGet, "http://www.imooc.com", nil) f(response, request) verifyResponse(response.Result(), tt.code, tt.message, t) } } func TestErrWrapperInServer(t *testing.T) { for _, tt := range tests { f := errWrapper(tt.h) server := httptest.NewServer( http.HandlerFunc(f)) resp, _ := http.Get(server.URL) verifyResponse( resp, tt.code, tt.message, t) } } func verifyResponse(resp *http.Response, expectedCode int, expectedMsg string, t *testing.T) { b, _ := ioutil.ReadAll(resp.Body) body := strings.Trim(string(b), " ") if resp.StatusCode != expectedCode || body != expectedMsg { t.Errorf("expect (%d, %s); "+ "got (%d, %s)", expectedCode, expectedMsg, resp.StatusCode, body) } }
3. panic
注意:不能经常用
停止当前函数执行,一直向上返回,执行每一层的defer,如果没有遇见recover则程序退出
4.recover
与panic对应
仅在defer调用中使用,获取panic的值,如果无法处理可重新panic
例子:
package main import ( "fmt" ) func tryRecover() { defer func() { //这里写一个匿名函数 注意最后加的() r := recover() //哈哈,这里是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) //的确是err } else { panic(fmt.Sprintf( "I don‘t know what to do: %v", r)) } }() // 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() }
以上是关于Go资源与出错处理的主要内容,如果未能解决你的问题,请参考以下文章
将 GraphQL 片段与 Apollo Hooks 一起使用时出错
npm : 无法加载文件 D:softcodeProcess ode ode_global pm.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.micr +(代码片段