Go语言中统一异常处理实现方式

主要讲解Go语言中统一异常处理

        Go语言和其他语言一样,为了提升程序可读性和业务分离,应该将错误处理单独抽取出来形成一个独立的模块,弱化在业务处理时对错误的频繁判断处理

主要知识点:

  • 熟悉统一错误处理的方式
  • error 和 panic ,能自己识别到的错误就用error,panic是一种碰到错误终止程序的错误,能不用尽量不用
  • 一般使用 defer + panic + recover 结合进行统一错误处理
  • 使用 Type Assertion 判断错误类型
  • 结合函数式编程实现

        以下通过代码实现统一异常处理,代码示例为查看当前目录下文件内容,开启Web服务器,请求连接为:http://localhost:8888/list/abc.txt

        可对请求 URL进行验证,地址中 /list/ 是否存在,文件是否存在,是否具有可读权限等。

具体代码如下:

        

package main

import (
	"log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"fmt"
	"strings"
	"io/ioutil"
)

const prefix = "/list/"

type userError string

func (e userError) Error() string {
	return e.Message()
}

func (e userError) Message() string {
	return string(e)
}

func HandleFileList(writer http.ResponseWriter, request *http.Request) error {
	fmt.Println()
	//判断 请求 URL 必须以 prefix 开头
	if strings.Index(
		request.URL.Path, prefix) != 0 {
		return userError(
			fmt.Sprintf("path %s must start "+"with %s",
				request.URL.Path, prefix))
	}
	path := request.URL.Path[len(prefix):]
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	all, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}

	writer.Write(all)
	return nil
}


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


//定义一个个错误统一处理方法
func errWrapper(handler appHandler) func(http.ResponseWriter, *http.Request) {
	//闭包
	return func(writer http.ResponseWriter,request *http.Request) {
		// 如果 发现 panic,判断错误输出错误,否则 继续往上层 panic
		defer func() {
			if r := recover(); r != nil {
				log.Printf("Panic: %v", r)
				http.Error(
					writer, //向writer汇报错误
					http.StatusText(http.StatusInternalServerError), //错误描述信息(字符串)
					http.StatusInternalServerError) //系统内部错误
			}
		}()
		// 执行业务代码操作,上面定义的 defer 就是防止 业务代码中出现 panic
		err := handler(writer, request)

		// 如果业务代码执行出错
		if err != nil {
			//日志输出错误信息
			log.Printf("Error occurred handling request: %s",err.Error())

			//判断错误类型是否为 自定义错误
			if userErr, ok := err.(userError); ok {
				http.Error(writer,
					userErr.Message(),
					http.StatusBadRequest)
				return
			}

			// 判断系统错误的类型
			code := http.StatusOK
			switch {
			case os.IsNotExist(err):
				code = http.StatusNotFound  //文件无法找到错误
			case os.IsPermission(err):
				code = http.StatusForbidden // 权限不够错误
			default:
				code = http.StatusInternalServerError //其他错误
			}
			//向writer 中写入错误信息
			http.Error(writer,http.StatusText(code), code)
		}
	}
}

//type userError interface {
//	error
//	Message() string
//}

func main() {
	//为 http服务 "/" 请求 增加 处理方法
	http.HandleFunc("/",errWrapper(HandleFileList))

	err := http.ListenAndServe(":8888", nil)
	if err != nil {
		panic(err)
	}
}

猜你喜欢

转载自my.oschina.net/ruoli/blog/1816215