主要讲解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)
}
}