您的位置:首页 > 游戏 > 游戏 > 房屋装修设计培训学校_国际知名设计公司csc_百度投诉中心热线_seo排名官网

房屋装修设计培训学校_国际知名设计公司csc_百度投诉中心热线_seo排名官网

2025/1/5 13:44:38 来源:https://blog.csdn.net/Coffeemaker88/article/details/144304429  浏览:    关键词:房屋装修设计培训学校_国际知名设计公司csc_百度投诉中心热线_seo排名官网
房屋装修设计培训学校_国际知名设计公司csc_百度投诉中心热线_seo排名官网

资源管理与出错处理

Golang 通过 defer 调用来确保调用在函数结束或 panic 时发生,从而进行资源管理。

对语句使用 defer 关键字,则这条语句将会在函数结束时才运行,比如:

package mainimport "fmt"func tryDefer() {defer fmt.Println(1)defer fmt.Println(2)fmt.Println(3)
}func main() {tryDefer()
}

输出的结果是:

3
2
1

一个更典型的例子如下,在该例当中我们实现了一个函数,用于向给定文件当中写入前二十个 Fibonacci 数:

func writeFile(filename string) {file, err := os.Create(filename)if err != nil {panic(err)}defer file.Close() // 打开的 file 使用 defer 进行 close// 直接读写文件比较慢, 使用 bufio 来对文件进行读写writer := bufio.NewWriter(file)defer writer.Flush() // 将 buffer 当中的内容写入到文件f := fib.Fibonacci()for i := 0; i < 20; i++ {fmt.Fprintln(writer, f())}// 函数结束时, 将会首先运行 writer.Flush(), 再运行 file.Close()
}

何使使用 defer 调用?

  • Open/Close
  • Lock/Unlock
  • PrintHeader/PrintFooter

错误处理

现在我们对之前的 writeFile 函数进行修改,修改的结果如下:

func writeFile(filename string) {file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666)if err != nil {panic(err)}defer file.Close() // 打开的 file 使用 defer 进行 close// 直接读写文件比较慢, 使用 bufio 来对文件进行读写writer := bufio.NewWriter(file)defer writer.Flush() // 将 buffer 当中的内容写入到文件f := fib.Fibonacci()for i := 0; i < 20; i++ {fmt.Fprintln(writer, f())}
}

👆 如果此时要写入的目标文件存在,那么程序会在第一个 panic 挂掉。但是我们不希望程序只给出出错的信息,我们希望在函数执行的过程中给出错误信息,并试图对错误进行修正,或是将函数返回以避免函数进一步执行得到不理想的结果:

if err != nil {// fmt.Println("file already exists")fmt.Println("Error:", err.Error())return
}

现在我们关心的是,err 当中本身包含哪些内容。根据 Golang 提供的文档,得知 err 本质上是一个 *PathError 类型。可以通过下述方式得到 err 当中包含哪些内容:

if err != nil {//fmt.Println("file already exists")if pathError, ok := err.(*os.PathError); !ok {panic(err)} else {fmt.Println(pathError.Op, pathError.Path, pathError.Err)}return
}

得到的结果如下:

open fib.txt The file exists.

其中 open 是 pathError.Op,fib.txt 是 pathError.Path,The file exists 是 PathError.Err。

我们自己也可以新建一些 error,比如:

err = errors.New("ths is a custom error")

error 本身是一个 interface,实现 interface 的方法即可定义我们自己的 error:

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {Error() string
}

如何实现统一的错误处理逻辑?

现在我们想要实现一个统一的错误处理逻辑,上面的向文件中写内容的例子过于简单,现在我们试图实现一个文件显示服务器,它能够打开给定的 url 并将内容写入到文件当中:

package mainimport ("io""net/http""os"
)func main() {http.HandleFunc("/list/",func(writer http.ResponseWriter, request *http.Request) {path := request.URL.Path[len("/list/"):] // /list/fib.txtfile, err := os.Open(path)if err != nil {panic(err)}defer file.Close()all, err := io.ReadAll(file)if err != nil {panic(err)}writer.Write(all)})err := http.ListenAndServe(":8888", nil)if err != nil {panic(err)}
}

在浏览器打开 localhost:8888/list/fib.txt,即可显示:
在这里插入图片描述
但是如果给定一个错误的文件地址,程序仍然会 panic,我们希望对上面的错误 err 进行封装:

if err != nil {http.Error(writer,err.Error(),http.StatusInternalServerError)return
}

在这里插入图片描述
这种情况下,会将错误显示到浏览器,即直接展示给用户,这还不够好,我们希望对 err 的处理进行进一步的封装。

首先,我们对文件的目录进行组织。将服务器运行的代码放在 web.go 当中,将 web.go 放在 filelistingserver 目录下。在此基础上,在 filelistingserver 目录下新建 filelisting 目录,将 handler.go 放在 filelisting 目录下,handler 当中的内容是:

package filelistingimport ("io""net/http""os"
)func HandleFileList(writer http.ResponseWriter, request *http.Request) error {path := request.URL.Path[len("/list/"):] // /list/fib.txtfile, err := os.Open(path)if err != nil {return err}defer file.Close()all, err := io.ReadAll(file)if err != nil {return err}writer.Write(all)return nil
}

👆 可以看到,它是对 http.HandleFunc(即最开始代码当中 main 函数体的第一行)第二个参数的抽象,在 HandleFileList 当中完成了服务的主要业务逻辑,但是在这个函数当中不对错误信息进行处理,而是统一地将错误信息进行返回,在其它地方对错误进行处理。

再来看目前的 web.go 文件的代码:

package mainimport ("learngo/errhandling/filelistingserver/filelisting""net/http""os"
)type appHandler func(writer http.ResponseWriter, request *http.Request) errorfunc errWrapper(handler appHandler) func(http.ResponseWriter, *http.Request) {return func(writer http.ResponseWriter, request *http.Request) {err := handler(writer, request)if err != nil {code := http.StatusOKswitch {case os.IsNotExist(err):code = http.StatusNotFounddefault:code = http.StatusInternalServerError}http.Error(writer, http.StatusText(code), code)}}
}func main() {http.HandleFunc("/list/", errWrapper(filelisting.HandleFileList))err := http.ListenAndServe(":8888", nil)if err != nil {panic(err)}
}

web.go 当中新定义了一个名为 appHandler 的类型,它是func(writer http.ResponseWriter, request *http.Request) error的别名。而函数 errWrapper 试图对错误信息进行打包,它的输入就是一个函数,这个函数的别名正是 appHandler,它返回的是函数func(http.ResponseWriter, *http.Request),在它的返回值当中定义了一个匿名函数,这个匿名函数会对每一个错误类型进行详细的处理,并返回状态码,比如 StatusNotFound 对应的状态码是 404。通过对 web.go 进行错误包装处理,此时在 localhost:8888 错误的给定一个文件路径,将不会返回系统内部 panic 的错误,而是直接显示 Not Found,即:将错误进行封装,返回的错误信息是开发者希望用户看到的,而不是直接返回系统内部的错误信息。

errWrapper 是对函数式编程的典型应用,它的输入是一个函数,输出也是一个函数,相当于把输入的函数包装成了输出函数,听起来与 Python 的装饰器非常的相似。

error vs. panic

尽可能地不要使用 panic(意料之外的错误使用 panic,比如数组越界),意料之中的错误使用 error(比如:文件无法打开)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com