前面( 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😁