From 519725bb3c075ee2462c929f5997cb068e18466a Mon Sep 17 00:00:00 2001
From: Ondřej Surý
If you have written any Go code you have probably encountered the built-in
@@ -13,20 +10,14 @@ indicate an abnormal state. For example, the
The following code uses
You can get a lot done in Go knowing just this about the
You can construct one of these values with the
Here's how you might use
A caller passing a negative argument to
The fmt package formats an
In many cases
A sophisticated caller can then use a
@@ -164,13 +125,7 @@ As another example, the json package specifies
returns when it encounters a syntax error parsing a JSON blob.
The
(This is a slightly simplified version of some
@@ -217,14 +165,7 @@ web crawler might sleep and retry when it encounters a temporary error and give
up otherwise.
Simplifying repetitive error handling
@@ -244,23 +185,7 @@ application with an HTTP handler that retrieves a record from the datastore and
formats it with a template.
This function handles errors returned by the
Then we can change our
This is simpler than the original version, but the http.Handler interface's
The
With this basic error handling infrastructure in place, we can make it more
@@ -341,24 +248,19 @@ To do this we create an
Next we modify the appHandler type to return
(It's usually a mistake to pass back the concrete type of an error rather than
-os.Open
function
returns a non-nil error
value when it fails to open a file.
func Open(name string) (file *File, err error)
+{{code "/doc/progs/error.go" `/func Open/`}}
os.Open
to open a file. If an error
occurs it calls log.Fatal
to print the error message and stop.
f, err := os.Open("filename.ext")
- if err != nil {
- log.Fatal(err)
- }
- // do something with the open *File f
+{{code "/doc/progs/error.go" `/func openFile/` `/STOP/`}}
error
@@ -59,15 +50,7 @@ The most commonly-used error
implementation is the
errors package's unexported errorString
type.
// errorString is a trivial implementation of error.
-type errorString struct {
- s string
-}
-
-func (e *errorString) Error() string {
- return e.s
-}
+{{code "/doc/progs/error.go" `/errorString/` `/STOP/`}}
errors.New
@@ -75,23 +58,13 @@ function. It takes a string that it converts to an errors.errorStringerror
value.
// New returns an error that formats as the given text.
-func New(text string) error {
- return &errorString{text}
-}
+{{code "/doc/progs/error.go" `/New/` `/STOP/`}}
errors.New
:
func Sqrt(f float64) (float64, error) {
- if f < 0 {
- return 0, errors.New("math: square root of negative number")
- }
- // implementation
-}
+{{code "/doc/progs/error.go" `/func Sqrt/` `/STOP/`}}
Sqrt
receives a non-nil
@@ -101,11 +74,7 @@ A caller passing a negative argument to Sqrt
receives a non-nil
Error
method, or by just printing it:
f, err := Sqrt(-1)
- if err != nil {
- fmt.Println(err)
- }
+{{code "/doc/progs/error.go" `/func printErr/` `/STOP/`}}
error
value
@@ -126,10 +95,7 @@ rules and returns it as an error
created by
errors.New
.
if f < 0 {
- return 0, fmt.Errorf("math: square root of negative number %g", f)
- }
+{{code "/doc/progs/error.go" `/fmtError/` `/STOP/`}}
fmt.Errorf
is good enough, but since
@@ -143,12 +109,7 @@ argument passed to Sqrt
. We can enable that by defining a new
error implementation instead of using errors.errorString
:
type NegativeSqrtError float64
-
-func (f NegativeSqrtError) Error() string {
- return fmt.Sprintf("math: square root of negative number %g", float64(f))
-}
+{{code "/doc/progs/error.go" `/type NegativeSqrtError/` `/STOP/`}}
type SyntaxError struct {
- msg string // description of error
- Offset int64 // error occurred after reading Offset bytes
-}
-
-func (e *SyntaxError) Error() string { return e.msg }
+{{code "/doc/progs/error.go" `/type SyntaxError/` `/STOP/`}}
Offset
field isn't even shown in the default formatting of the
@@ -178,14 +133,7 @@ error, but callers can use it to add file and line information to their error
messages:
if err := dec.Decode(&val); err != nil {
- if serr, ok := err.(*json.SyntaxError); ok {
- line, col := findLine(f, serr.Offset)
- return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
- }
- return err
- }
+{{code "/doc/progs/error.go" `/func decodeError/` `/STOP/`}}
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
- time.Sleep(1e9)
- continue
- }
- if err != nil {
- log.Fatal(err)
- }
+{{code "/doc/progs/error.go" `/func netError/` `/STOP/`}}
func init() {
- http.HandleFunc("/view", viewRecord)
-}
-
-func viewRecord(w http.ResponseWriter, r *http.Request) {
- c := appengine.NewContext(r)
- key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
- record := new(Record)
- if err := datastore.Get(c, key, record); err != nil {
- http.Error(w, err.Error(), 500)
- return
- }
- if err := viewTemplate.Execute(w, record); err != nil {
- http.Error(w, err.Error(), 500)
- }
-}
+{{code "/doc/progs/error2.go" `/func init/` `/STOP/`}}
datastore.Get
@@ -276,23 +201,13 @@ To reduce the repetition we can define our own HTTP appHandler
type that includes an error
return value:
type appHandler func(http.ResponseWriter, *http.Request) error
+{{code "/doc/progs/error3.go" `/type appHandler/`}}
viewRecord
function to return errors:
func viewRecord(w http.ResponseWriter, r *http.Request) error {
- c := appengine.NewContext(r)
- key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
- record := new(Record)
- if err := datastore.Get(c, key, record); err != nil {
- return err
- }
- return viewTemplate.Execute(w, record)
-}
+{{code "/doc/progs/error3.go" `/func viewRecord/` `/STOP/`}}
ServeHTTP
method on appHandler
:
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if err := fn(w, r); err != nil {
- http.Error(w, err.Error(), 500)
- }
-}
+{{code "/doc/progs/error3.go" `/ServeHTTP/` `/STOP/`}}
ServeHTTP
method calls the appHandler
function
@@ -323,10 +233,7 @@ Now when registering viewRecord
with the http package we use the
http.HandlerFunc
).
func init() {
- http.Handle("/view", appHandler(viewRecord))
-}
+{{code "/doc/progs/error3.go" `/func init/` `/STOP/`}}
appError
struct containing an
error
and some other fields:
type appError struct {
- Error error
- Message string
- Code int
-}
+{{code "/doc/progs/error4.go" `/type appError/` `/STOP/`}}
*appError
values:
type appHandler func(http.ResponseWriter, *http.Request) *appError
+{{code "/doc/progs/error4.go" `/type appHandler/`}}
error
, for reasons to be discussed in another article, but
-it's the right thing to do here because ServeHTTP
is the only
+error
,
+for reasons discussed in the Go FAQ,
+but it's the right thing to do here because ServeHTTP
is the only
place that sees the value and uses its contents.)
Code
and log the full Error
to the developer
console:
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if e := fn(w, r); e != nil { // e is *appError, not os.Error. - c := appengine.NewContext(r) - c.Errorf("%v", e.Error) - http.Error(w, e.Message, e.Code) - } -}+{{code "/doc/progs/error4.go" `/ServeHTTP/` `/STOP/`}}
Finally, we update viewRecord
to the new function signature and
have it return more context when it encounters an error:
func viewRecord(w http.ResponseWriter, r *http.Request) *appError { - c := appengine.NewContext(r) - key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil) - record := new(Record) - if err := datastore.Get(c, key, record); err != nil { - return &appError{err, "Record not found", 404} - } - if err := viewTemplate.Execute(w, record); err != nil { - return &appError{err, "Can't display record", 500} - } - return nil -}+{{code "/doc/progs/error4.go" `/func viewRecord/` `/STOP/`}}
This version of viewRecord
is the same length as the original, but
--
cgit v1.2.3