Skip to content

Commit 496cb86

Browse files
committed
Format and remove 11.1.md spaces
1 parent 3070c4a commit 496cb86

File tree

1 file changed

+103
-103
lines changed

1 file changed

+103
-103
lines changed

zh/11.1.md

Lines changed: 103 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,59 @@
22
Go语言主要的设计准则是:简洁、明白,简洁是指语法和C类似,相当的简单,明白是指任何语句都是很明显的,不含有任何隐含的东西,在错误处理方案的设计中也贯彻了这一思想。我们知道在C语言里面是通过返回-1或者NULL之类的信息来表示错误,但是对于使用者来说,不查看相应的API说明文档,根本搞不清楚这个返回值究竟代表什么意思,比如:返回0是成功,还是失败,而Go定义了一个叫做error的类型,来显式表达错误。在使用时,通过把返回的error变量与nil的比较,来判定操作是否成功。例如`os.Open`函数在打开文件失败时将返回一个不为nil的error变量
33
```Go
44

5-
func Open(name string) (file *File, err error)
5+
func Open(name string) (file *File, err error)
66
```
77
下面这个例子通过调用`os.Open`打开一个文件,如果出现错误,那么就会调用`log.Fatal`来输出错误信息:
88
```Go
99

10-
f, err := os.Open("filename.ext")
11-
if err != nil {
12-
log.Fatal(err)
13-
}
10+
f, err := os.Open("filename.ext")
11+
if err != nil {
12+
log.Fatal(err)
13+
}
1414
```
1515
类似于`os.Open`函数,标准包中所有可能出错的API都会返回一个error变量,以方便错误处理,这个小节将详细地介绍error类型的设计,和讨论开发Web应用中如何更好地处理error。
1616
## Error类型
1717
error类型是一个接口类型,这是它的定义:
1818
```Go
1919

20-
type error interface {
21-
Error() string
22-
}
20+
type error interface {
21+
Error() string
22+
}
2323
```
2424
error是一个内置的接口类型,我们可以在/builtin/包下面找到相应的定义。而我们在很多内部包里面用到的 error是errors包下面的实现的私有结构errorString
2525
```Go
2626

27-
// errorString is a trivial implementation of error.
28-
type errorString struct {
29-
s string
30-
}
27+
// errorString is a trivial implementation of error.
28+
type errorString struct {
29+
s string
30+
}
3131

32-
func (e *errorString) Error() string {
33-
return e.s
34-
}
32+
func (e *errorString) Error() string {
33+
return e.s
34+
}
3535
```
3636
你可以通过`errors.New`把一个字符串转化为errorString,以得到一个满足接口error的对象,其内部实现如下:
3737
```Go
3838

39-
// New returns an error that formats as the given text.
40-
func New(text string) error {
41-
return &errorString{text}
42-
}
39+
// New returns an error that formats as the given text.
40+
func New(text string) error {
41+
return &errorString{text}
42+
}
4343
```
4444
下面这个例子演示了如何使用`errors.New`:
4545
```Go
4646

47-
func Sqrt(f float64) (float64, error) {
48-
if f < 0 {
49-
return 0, errors.New("math: square root of negative number")
50-
}
51-
// implementation
47+
func Sqrt(f float64) (float64, error) {
48+
if f < 0 {
49+
return 0, errors.New("math: square root of negative number")
5250
}
51+
// implementation
52+
}
5353
```
5454
在下面的例子中,我们在调用Sqrt的时候传递的一个负数,然后就得到了non-nil的error对象,将此对象与nil比较,结果为true,所以fmt.Println(fmt包在处理error时会调用Error方法)被调用,以输出错误,请看下面调用的示例代码:
5555
```Go
5656

57-
f, err := Sqrt(-1)
57+
f, err := Sqrt(-1)
5858
if err != nil {
5959
fmt.Println(err)
6060
}
@@ -63,28 +63,28 @@ error是一个内置的接口类型,我们可以在/builtin/包下面找到相
6363
通过上面的介绍我们知道error是一个interface,所以在实现自己的包的时候,通过定义实现此接口的结构,我们就可以实现自己的错误定义,请看来自Json包的示例:
6464
```Go
6565

66-
type SyntaxError struct {
67-
msg string // 错误描述
68-
Offset int64 // 错误发生的位置
69-
}
66+
type SyntaxError struct {
67+
msg string // 错误描述
68+
Offset int64 // 错误发生的位置
69+
}
7070

71-
func (e *SyntaxError) Error() string { return e.msg }
71+
func (e *SyntaxError) Error() string { return e.msg }
7272
```
7373
Offset字段在调用Error的时候不会被打印,但是我们可以通过类型断言获取错误类型,然后可以打印相应的错误信息,请看下面的例子:
7474
```Go
7575

76-
if err := dec.Decode(&val); err != nil {
77-
if serr, ok := err.(*json.SyntaxError); ok {
78-
line, col := findLine(f, serr.Offset)
79-
return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
80-
}
81-
return err
76+
if err := dec.Decode(&val); err != nil {
77+
if serr, ok := err.(*json.SyntaxError); ok {
78+
line, col := findLine(f, serr.Offset)
79+
return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
8280
}
81+
return err
82+
}
8383
```
8484
需要注意的是,函数返回自定义错误时,返回值推荐设置为error类型,而非自定义错误类型,特别需要注意的是不应预声明自定义错误类型的变量。例如:
8585
```Go
8686

87-
func Decode() *SyntaxError { // 错误,将可能导致上层调用者err!=nil的判断永远为true。
87+
func Decode() *SyntaxError { // 错误,将可能导致上层调用者err!=nil的判断永远为true。
8888
var err *SyntaxError // 预声明错误变量
8989
if 出错条件 {
9090
err = &SyntaxError{}
@@ -97,117 +97,117 @@ Offset字段在调用Error的时候不会被打印,但是我们可以通过类
9797
上面例子简单的演示了如何自定义Error类型。但是如果我们还需要更复杂的错误处理呢?此时,我们来参考一下net包采用的方法:
9898
```Go
9999

100-
package net
100+
package net
101101

102-
type Error interface {
103-
error
104-
Timeout() bool // Is the error a timeout?
105-
Temporary() bool // Is the error temporary?
106-
}
102+
type Error interface {
103+
error
104+
Timeout() bool // Is the error a timeout?
105+
Temporary() bool // Is the error temporary?
106+
}
107107

108108
```
109109
在调用的地方,通过类型断言err是不是net.Error,来细化错误的处理,例如下面的例子,如果一个网络发生临时性错误,那么将会sleep 1秒之后重试:
110110
```Go
111111

112-
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
113-
time.Sleep(1e9)
114-
continue
115-
}
116-
if err != nil {
117-
log.Fatal(err)
118-
}
112+
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
113+
time.Sleep(1e9)
114+
continue
115+
}
116+
if err != nil {
117+
log.Fatal(err)
118+
}
119119
```
120120
## 错误处理
121121
Go在错误处理上采用了与C类似的检查返回值的方式,而不是其他多数主流语言采用的异常方式,这造成了代码编写上的一个很大的缺点:错误处理代码的冗余,对于这种情况是我们通过复用检测函数来减少类似的代码。
122122

123123
请看下面这个例子代码:
124124
```Go
125125

126-
func init() {
127-
http.HandleFunc("/view", viewRecord)
128-
}
126+
func init() {
127+
http.HandleFunc("/view", viewRecord)
128+
}
129129

130-
func viewRecord(w http.ResponseWriter, r *http.Request) {
131-
c := appengine.NewContext(r)
132-
key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
133-
record := new(Record)
134-
if err := datastore.Get(c, key, record); err != nil {
135-
http.Error(w, err.Error(), 500)
136-
return
137-
}
138-
if err := viewTemplate.Execute(w, record); err != nil {
139-
http.Error(w, err.Error(), 500)
140-
}
130+
func viewRecord(w http.ResponseWriter, r *http.Request) {
131+
c := appengine.NewContext(r)
132+
key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
133+
record := new(Record)
134+
if err := datastore.Get(c, key, record); err != nil {
135+
http.Error(w, err.Error(), 500)
136+
return
137+
}
138+
if err := viewTemplate.Execute(w, record); err != nil {
139+
http.Error(w, err.Error(), 500)
141140
}
141+
}
142142
```
143143
上面的例子中获取数据和模板展示调用时都有检测错误,当有错误发生时,调用了统一的处理函数`http.Error`,返回给客户端500错误码,并显示相应的错误数据。但是当越来越多的HandleFunc加入之后,这样的错误处理逻辑代码就会越来越多,其实我们可以通过自定义路由器来缩减代码(实现的思路可以参考第三章的HTTP详解)。
144144
```Go
145145

146-
type appHandler func(http.ResponseWriter, *http.Request) error
146+
type appHandler func(http.ResponseWriter, *http.Request) error
147147

148-
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
149-
if err := fn(w, r); err != nil {
150-
http.Error(w, err.Error(), 500)
151-
}
148+
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
149+
if err := fn(w, r); err != nil {
150+
http.Error(w, err.Error(), 500)
152151
}
152+
}
153153
```
154154
上面我们定义了自定义的路由器,然后我们可以通过如下方式来注册函数:
155155
```Go
156156

157-
func init() {
158-
http.Handle("/view", appHandler(viewRecord))
159-
}
157+
func init() {
158+
http.Handle("/view", appHandler(viewRecord))
159+
}
160160
```
161161
当请求/view的时候我们的逻辑处理可以变成如下代码,和第一种实现方式相比较已经简单了很多。
162162
```Go
163163

164-
func viewRecord(w http.ResponseWriter, r *http.Request) error {
165-
c := appengine.NewContext(r)
166-
key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
167-
record := new(Record)
168-
if err := datastore.Get(c, key, record); err != nil {
169-
return err
170-
}
171-
return viewTemplate.Execute(w, record)
164+
func viewRecord(w http.ResponseWriter, r *http.Request) error {
165+
c := appengine.NewContext(r)
166+
key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
167+
record := new(Record)
168+
if err := datastore.Get(c, key, record); err != nil {
169+
return err
172170
}
171+
return viewTemplate.Execute(w, record)
172+
}
173173
```
174174
上面的例子错误处理的时候所有的错误返回给用户的都是500错误码,然后打印出来相应的错误代码,其实我们可以把这个错误信息定义的更加友好,调试的时候也方便定位问题,我们可以自定义返回的错误类型:
175175
```Go
176176

177-
type appError struct {
178-
Error error
179-
Message string
180-
Code int
181-
}
177+
type appError struct {
178+
Error error
179+
Message string
180+
Code int
181+
}
182182
```
183183
这样我们的自定义路由器可以改成如下方式:
184184
```Go
185185

186-
type appHandler func(http.ResponseWriter, *http.Request) *appError
186+
type appHandler func(http.ResponseWriter, *http.Request) *appError
187187

188-
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
189-
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
190-
c := appengine.NewContext(r)
191-
c.Errorf("%v", e.Error)
192-
http.Error(w, e.Message, e.Code)
193-
}
188+
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
189+
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
190+
c := appengine.NewContext(r)
191+
c.Errorf("%v", e.Error)
192+
http.Error(w, e.Message, e.Code)
194193
}
194+
}
195195
```
196196
这样修改完自定义错误之后,我们的逻辑处理可以改成如下方式:
197197
```Go
198198

199-
func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
200-
c := appengine.NewContext(r)
201-
key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
202-
record := new(Record)
203-
if err := datastore.Get(c, key, record); err != nil {
204-
return &appError{err, "Record not found", 404}
205-
}
206-
if err := viewTemplate.Execute(w, record); err != nil {
207-
return &appError{err, "Can't display record", 500}
208-
}
209-
return nil
199+
func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
200+
c := appengine.NewContext(r)
201+
key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
202+
record := new(Record)
203+
if err := datastore.Get(c, key, record); err != nil {
204+
return &appError{err, "Record not found", 404}
205+
}
206+
if err := viewTemplate.Execute(w, record); err != nil {
207+
return &appError{err, "Can't display record", 500}
210208
}
209+
return nil
210+
}
211211
```
212212
如上所示,在我们访问view的时候可以根据不同的情况获取不同的错误码和错误信息,虽然这个和第一个版本的代码量差不多,但是这个显示的错误更加明显,提示的错误信息更加友好,扩展性也比第一个更好。
213213

0 commit comments

Comments
 (0)