Go 网络开发中的常用的应用层协议库都是基于 net 标准库开发的,比如 gorilla/websocket 和 go-grpc,还有最常用的 http 库。
当请求一个不存在的 host 时会返回一个错误,如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $  cat  main.gopackage main import ( 	"fmt" 	"net/http" ) func main() { 	if _, err := http.Get("http://unknownhost.example"); err != nil { 		fmt.Println(err) 	} } $  go run main.go Get "http://unknownhost.example": dial tcp: lookup unknownhost.example: no such host 
这个错误本质其实是 *net.DNSError,在 Go 早期开发中,会经常使用接口断言匹配错误。
比如这样 if v, ok := err.(*net.DNSError);ok {} 的形式。
如果这样使用的话,实际上不会匹配断言匹配到,在 debug 模式可以看到最后返回其实是 *url.Error,然后内部才实际包裹了 (*net.DNSError) 错误,并实现 Unwarp() error 方法。
1 2 3 4 5 6 type  Error struct  {	Op  string  	URL string  	Err error   } 
在 go 1.13 之后,errors.As 可以进行解包错误,那么我们就可以直接使用下面方式即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package  mainimport  (	"errors"  	"fmt"  	"net"  	"net/http"  ) func  main () 	if  _, err := http.Get("http://unknownhost.example" ); err != nil  { 		if  v := (*net.DNSError)(nil ); errors.As(err, &v) { 			fmt.Println("can't found" , v.Name) 		} 	} } 
除了 *net.DNSError 之外,还有下面内置错误结构体:
net.InvalidAddrError 
net.UnknownNetworkError 
net.AddrError 
net.DNSConfigError 
 
上面错误除了实现了 Error() 接口,还实现了 Timeout() bool 和 Temporary() bool 接口,也就是实现了 net.Error 接口
1 2 3 4 5 6 type  Error interface  {    error      Timeout() bool         Temporary() bool   } 
同时也实现了 Unwarp() 接口,用于错误解包。那么我们就可以区分请求的错误是网络错误,还是服务器错误。
比如在 jsonrpc 协议中定义了 Error 结构体,使用下面方式进行判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 type  JsonError struct  {	Code int          `json:"code"`  	Msg  string       `json:"message"`  	Data interface {} `json:"data"`  } func  (e *JsonError) string  {	return  e.Msg } func  main () 	if  _, err := jsonrpc.Call("http://jsonrpc.example" ); err != nil  { 		if  v := (net.Error)(nil ); errors.As(err, &v) { 			fmt.Println("is timeout?" , v.Timeout()) 			return  		} 		if  v := (*Error)(nil ); errors.As(err, &v) { 			fmt.Println("jsonrpc error: Code:" , v.Code, "Msg" , v.Msg) 		} 	} } 
如果按照以前的方式不断通过接口断言的解包错误的形式,那么代码很快会形成类似”回调地狱“的模样。
所以,在实际应用层开发上,直接可以使用 fmt.Errorf("Stack: %w",err) 包装错误,最后调用方最后使用 errros.As 或 errros.Is 解包按需处理错误。