Skip to content

Latest commit

 

History

History
209 lines (153 loc) · 8.95 KB

File metadata and controls

209 lines (153 loc) · 8.95 KB

11.1 Error handling

Go language major design criterion is : simple to understand , simple is the syntax similar to C is fairly simple to understand refers to any statement is obvious , does not contain any hidden things in the error-handling program design also implement this idea . We know that there is in the C language by returning -1 or NULL to indicate the kind of error message , but for users who do not view the API documentation , the return value never know exactly what does it mean , for example: return 0 is success or failure , and Go defines a type called the error to explicitly express errors. When in use, by the returned error variable nil comparison to determine whether the operation was successful. For example os.Open function opens the file will return a failure of the error variable is not nil

func Open (name string) (file * File, err error)

Here's an example by calling os.Open open a file if an error occurs, it will call the log.Fatal to output an error message :

f, err := os.Open("filename.ext")
if err != nil {
	log.Fatal(err)
}

Similar to the os.Open function, the standard package of all possible errors of the API will return an error variable to facilitate error handling, this section will detail error type design, and discuss how to develop Web applications to better handle error.

Error type

error is an interface type, with this definition:

type error interface {
	Error() string
}

error is a built-in interface type, we can / builtin / pack below to find the appropriate definition. And we have a lot of internal error is used inside the package packet errors following implementation structure errorString private

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}

You can errors.New put a string into errorString, in order to get a meet the interface error object whose internal implementation is as follows:

// New returns an error that formats as the given text.
func New(text string) error {
	return &errorString{text}
}

The following example demonstrates how to use errors.New:

func Sqrt(f float64) (float64, error) {
	if f < 0 {
		return 0, errors.New("math: square root of negative number")
	}
	// implementation
}

In the following example, we call the Sqrt when passing a negative number , and then you get the error object is non-nil , nil compared with this object , the result is true, so fmt.Println (fmt package when dealing with error calls the error method ) is called to output error , look at the following sample code to call :

f, err := Sqrt(-1)
if err != nil {
    fmt.Println(err)
}	

Custom Error

Through the above description we know that error is an interface, so in the realization of their package, by defining the structure implements this interface , we can realize their error definitions , see the package from Json Example:

type SyntaxError struct {
    msg string // error description
    Offset int64 // where the error occurred
}

func (e * SyntaxError) Error() string {return e.msg}

Error Offset field in the call time will not be printed , but we can get through a type assertion error type , then you can print an appropriate error message , see the following examples:

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
}

Note that, the function returns a custom error, the return value is set to recommend error type, rather than a custom error types, particular note should not be pre-declare custom error types of variables. For example:

func Decode() *SyntaxError {
    // error , which may lead to the upper caller err! = nil judgment is always true.
    var err * SyntaxError // pre- declare error variable
    if an error condition {
        err = & SyntaxError {}
    }
    return err // error , err always equal non- nil, causes the upper caller err! = nil judgment is always true
}

Cause see http://golang.org/doc/faq#nil_error

The above example shows how a simple custom Error type. But if we need more sophisticated error handling it? At this point, we have to refer to net package approach:

package net

type Error interface {
    error
    Timeout() bool   // Is the error a timeout?
    Temporary() bool // Is the error temporary?
}

Place the call through the type assertion err is not net.Error, to refine error handling , such as the following example , if a temporary error occurs on the network , it will sleep 1 seconds Retry :

if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
	time.Sleep(1e9)
	continue
}
if err != nil {
	log.Fatal(err)
}

Error handling

Go in the error handling on the use of the C check the return value similar manner , rather than most other mainstream languages used in unexpected ways , which caused a code written on a big disadvantage : error handling code redundancy , for this kind of situation is that we detect function to reduce reuse similar code .

Look at the following example code:

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)
	}
}

The above example shows a template to get data and call has detected an error when an error occurs , call a unified processing function http.Error, returned to the client 500 error code and display the corresponding error data . But when more and more HandleFunc joined, so that error-handling logic code will be more and more, in fact, we can customize the router to reduce the code ( realization of the ideas you can refer to Chapter III of the HTTP Detailed ) .

type appHandler func(http.ResponseWriter, *http.Request) error

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if err := fn(w, r); err != nil {
		http.Error(w, err.Error(), 500)
	}
}

Above we defined a custom router , and then we can adopt the following approach to registration function :

func init() {
	http.Handle("/view", appHandler(viewRecord))
}

When requested /view when we can become as logical processing code, and the first implementation has been compared to a lot simpler .

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)
}

The above example error handling when all errors are returned to the user 500 error code, and then print out the corresponding error code , in fact, we can put this definition more friendly error messages when debugging is also convenient positioning problem, we can custom return types of errors :

type appError struct {
	Error   error
	Message string
	Code    int
}

So that our custom router can be changed as follows :

type appHandler func(http.ResponseWriter, *http.Request) *appError

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)
	}
}

Such custom error after modifying our logic can be changed as follows :

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
}

As shown above, in our view when you can visit according to different situations for different error codes and error messages , although this first version of the code and the amount is almost, but this show is more obvious error , the error message prompts more friendly , scalability is also better than the first one .

Summary

In programming , fault tolerance is a very important part of the work , in Go it is achieved through error handling , error although only one interface, but it can have many variations but we can according to their needs to achieve different treatment Finally introduce error handling scheme , we hope that in how to design better programs on the Web error handling to bring some ideas .

Links