Golang学习笔记十八

资源管理与错误处理-错误处理二

Posted by CDz on February 16, 2019

前面( Golang学习笔记十六)我们对错误处理进行了同一的处理,但其实还是有漏洞,我们定义的server是/list开头,如果这两个逻辑并不是一个人写的,一个定义为/,一个定义为/list/那么程序就会出现错误.

当启动函数:

func main() {
    //前缀是/时,访问http://localhost:8888/abc页面就会奔溃.
    http.HandleFunc("/", errWrapper(filelisting.Handerlist))
    e := http.ListenAndServe(":8888", nil)
    if e != nil {
        panic(e)
    }
}

改进同一错误处理

在错误处理函数errWrapper中添加recover.

func errWrapper(
    handler appHandler) func(
    writer http.ResponseWriter, request *http.Request) {
    return func(writer http.ResponseWriter, request *http.Request) {
        //错误处理函数也需要recover
        defer func() {
            if r := recover(); r != nil {
                http.Error(writer,
                    http.StatusText(http.StatusInternalServerError),
                    http.StatusInternalServerError)
            }
        }()

        e := handler(writer, request)
        if e != nil {
            log.Printf("Error occurred handling request:%s",e)
            code := http.StatusOK
            switch {
            case os.IsNotExist(e):
                code = http.StatusNotFound
            case os.IsPermission(e):
                code=http.StatusForbidden//没有权限
            default:
                code = http.StatusInternalServerError
            }
            http.Error(writer,http.StatusText(code),code)
        }
    }
}

但是这个错误信息,我们其实是可以预见的,也就是说可以给用户看.

所以我们可以定义一个用户错误类型(一个接口):

type UserError interface {//先是有error这个接口,然后自己的方法Message
    error
    Message() string
}

我们在handler.go文件中定义了一个包装string的结构,实现这个接口:

type userError string

func (u userError) Error() string {
    return u.Message()
}

func (u userError) Message() string {
    return string(u)
}

然后在Handerlist函数中判断:

const prefix string = "/list/"
func Handerlist(writer http.ResponseWriter, request *http.Request) error{
    //此处做/list/判断
    if strings.Index(request.URL.Path,prefix) != 0 { 
        //返回自定义的userError
        return userError("path must start "+prefix)
    }

    path := request.URL.Path[len(prefix):]
    file, e := os.Open(path)
    if e!=nil {
        return e
    }
    defer file.Close()
    bytes, e := ioutil.ReadAll(file)
    if e != nil {
        return e
    }
    writer.Write(bytes)
    return nil
}

通过上面的改造,我们就可以将自定义可以让用户查看的错误,返回到页面上去.

总结

panic与error:

上面时候使用panic什么时候使用error,通过上面的列子,panic是在我们不知道什么错误的情况下才可以panic,我们能以预料到的一些错误,都需要使用error.

  • 意料之中的使用error
  • 意料之外的使用panic

错误处理综合案例技术点:

  • defer+panic+recover
  • type assertion
  • 函数式编程

作者所有的学习源码在 go学习源码github地址,如果觉得有用的话帮小智贡献一个star😁