zoukankan      html  css  js  c++  java
  • Go资源与出错处理

    1、defer延迟语句

    确保调用在函数结束时发生:延迟语句被用于执行一个函数调用,在这个函数之前,延迟语句返回

    参数在defer语句时计算

    defer列表为先进后出

    error vs panic : 意料之中用error比如文件打不开,意料之外用panic比如数组越界

    延迟函数

    可在函数中添加多个defer语句,函数执行到最后,defer语句会按照逆序执行,最后该函数返回。

    注:比如打开资源操作遇见错误需提前返回,返回前应当关闭相应资源,这时候可用defer。

    注意:示例用到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

    其他

    • 延迟方法

    defer也可以用于延迟一个方法调用。main函数中如果有defer将不会执行下面语句。

    • 延迟参数

    延迟函数的参数在执行延迟语句时被执行。

    func printA(a int) {
        fmt.Println("value of a in deferred function", a)
    }
    func main() {
        a := 5
        defer printA(a)
        a = 10
        fmt.Println("value of a before deferred function call", a)
    }
    输出:
    value of a before deferred function call 10
    value of a in deferred function 5
    • 堆栈延迟

    一个函数有多个延迟调用时,它们被添加到一个堆栈中,并在后进先出中顺序中执行。

    func main() {
        name := "Naveen"
        fmt.Printf("Orignal String: %s
    ", string(name))
        fmt.Printf("Reversed String: ")
        for _, v := range []rune(name) {
            defer fmt.Printf("%c", v)
        }
    }
    
    输出:
    Orignal String: Naveen
    Reversed String: neevaN

    例子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. 错误处理

    Go中错误也是一种类型,错误用内置的error类型表示。错误可存储在变量中,从函数中返回。

    注:一个打开文件的功能函数 func Open(name string) (file *File, err error) 若文件成功打开,则将返回文件处理,否则返回一个非nil错误。

    错误类型表示

    type error interface {
        Error() string  //任何实现该接口的类型都可以作为一个错误调用  该方法提供了对错误的描述
    }

    错误处理一:人为处理

    制造一个错误(程序会挂掉)

    将原来的:
    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

    Go没有像Java一样的异常机制,不能抛出异常,而是使用了panic和recover机制。

    注意:不能经常用

    panic是一个内建函数,停止当前函数执行,一直向上返回,执行每一层的defer,如果没有遇见recover则程序退出

    4.recover

    与panic对应,可让进入令人恐慌的流程中的goroutine恢复过来,仅在延迟函数中有效。正常执行过程中,调用recover返回nil,无任何效果。

    仅在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()
    }
    

      

  • 相关阅读:
    最全的常用正则表达式大全——包括校验数字、字符、一些特殊的需求等等
    xcode6+ios8最新真机调试教程
    新手学习ios开发的辅助工具
    IOS事件传递之hitTest:withEvent
    <转>Singletons in Objective-C
    iOS机身振动提醒
    (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(NSLineBreakMode)lineBreakMode
    对数组进行排序
    字符串去掉空格问题
    NSArray排序方法
  • 原文地址:https://www.cnblogs.com/ycx95/p/9366750.html
Copyright © 2011-2022 走看看