1、defer调用:
go语言通过defer调用来实现资源管理,defer先进后出的顺序
确保调用在函数结束时发生
func tryDefer(){
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
panic("error occurred")
fmt.Println(4)
}
//输出
3
2
1
panic: error occurred
func writeFile(filename string){
file ,err := os.Create(filename)
if err != nil {
panic("err")
}
defer file.Close()
writer := bufio.NewWriter(file) //用bufio写入到内存,待内存到一定程度的时候再写入文件,会快很多
defer writer.Flush() //需要刷新,不然数据还在缓存中不会写入到文件
f := fib.Fibonacci()
for i := 0;i<20;i++{
fmt.Fprintln(writer,f())
}
}
确保调用在函数结束时发生
参数在defer语句时计算
defer列表为后进先出
何时使用defer?
Open/Close
Lock/Unlock
PrintHeader/PrintFooter
2、错误概念处理:
func writeFile(filename string){
file ,err := os.OpenFile(filename,os.O_EXCL|os.CREATE,0666)
if err != nil {
//panic("err")
//正确的处理方式
//fmt.Println("file already exists")
//return
//或者
if pathError,ok := err.(*os.PathError);!ok{
panic(err)
}else{
fmt,Println(pathError.Op,
pathError.Path,
pathError.Err)
}
return
}
defer file.Close()
writer := bufio.NewWriter(file) //用bufio写入到内存,待内存到一定程度的时候再写入文件,会快很多
defer writer.Flush() //需要刷新,不然数据还在缓存中不会写入到文件
f := fib.Fibonacci()
for i := 0;i<20;i++{
fmt.Fprintln(writer,f())
}
}
3、服务器统一错误处理:
如何实现统一的错误处理逻辑?
func main(){
http.HandleFunc("/list/",
func(writer http.ResponseWriter,
request *http.Request){
path := request.URL.Path[len("/list/"):]
file,err :=os.Open(path)
if err != nil {
//panic(err)
http.Error(writer,
err.Error(),
http.StatusInternalServerError)
return
}
defer file.Close()
all,err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
writer.Writer(all)
})
err := http.ListenAndServe(":8888",nil)
if err != nil {
panic(err)
}
}
=======================================
//提取函数:
func HandleFileLis(writer http.ResponseWriter,
request *http.Request) error{
path := request.URL.Path[len("/list/"):]
file,err :=os.Open(path)
if err != nil {
//panic(err)
http.Error(writer,
err.Error(),
http.StatusInternalServerError)
return err
}
defer file.Close()
all,err := ioutil.ReadAll(file)
if err != nil {
//panic(err)
return err
}
writer.Writer(all)
return nil
}
type appHandler func(writer http.ResponseWriter,
request *http.Request) error //定义一个函数
//函数式编程,输入函数,输出函数
func errWrapper(hander appHandler) func(http.ResponseWriter,*http.Request){
return func(writer http.ResponseWriter,
request *http.Request){
err := handler(writer,request) //统一出错处理
if err != nil {
log.Warn("Error handling request : %s",err.Error())
code = http.StatusOK
switch{
case os.IsNotExists(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
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)
}
}
4、panic和recover:
panic:
停止当前函数执行
一直向上返回,执行每一层的defer
如果没有遇见recover,程序退出
recover:
仅在defer调用中使用
获取panic的值
如果无法处理,可重新panic
func tryRecover(){
defer func(){
r := recover()
if err,ok := r.(error);ok { //断言
fmt.Println("Error occurred:",err)
}else{
panic(r)
}
}() //加括号调用
//panic(errors.New("this is an error"))
b := 0
a := 5/b
fmt.Println(a) //如果没有recover,程序就挂掉了
}
5、服务器统一错误处理2:
//函数式编程,输入函数,输出函数
func errWrapper(hander appHandler) func(http.ResponseWriter,*http.Request){
return func(writer http.ResponseWriter,
request *http.Request){
//这里加入recover
defer func(){
r := recover()
log.Printf("Panic : %v",r)
http.Error(
writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}()
err := handler(writer,request) //统一出错处理
if err != nil {
log.Warn("Error handling request : %s",err.Error())
code = http.StatusOK
switch{
case os.IsNotExists(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(
writer,
http.StatusText(code),
code)
}
}
}
--------------------------------------------------------
const prefix = "/list/"
func HandleFileLis(writer http.ResponseWriter,
request *http.Request) error{
if strings.Index(request.URL.Path,prefix) != 0 {
return errors.New("path must start " + "with " + prefix) //出错信息不能以大写开头
}
path := request.URL.Path[len(prefix):]
file,err :=os.Open(path)
if err != nil {
//panic(err)
http.Error(writer,
err.Error(),
http.StatusInternalServerError)
return err
}
defer file.Close()
all,err := ioutil.ReadAll(file)
if err != nil {
//panic(err)
return err
}
writer.Writer(all)
return nil
}
//有些错误是可以给用户看到,有些不能给用户看到
type userError interface{
error
Message() string
}
type userError string
func (e userError) Error() string {
return e.Message()
}
func (e userError) Message() string {
return string(e)
}
//函数式编程,输入函数,输出函数
func errWrapper(hander appHandler) func(http.ResponseWriter,*http.Request){
return func(writer http.ResponseWriter,
request *http.Request){
//这里加入recover
defer func(){
r := recover()
log.Printf("Panic : %v",r)
http.Error(
writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}()
err := handler(writer,request) //统一出错处理
if err != nil {
log.Warn("Error 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.IsNotExists(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(
writer,
http.StatusText(code),
code)
}
}
}
func HandleFileLis(writer http.ResponseWriter,
request *http.Request) error{
if strings.Index(request.URL.Path,prefix) != 0 {
return userError("path must start " + "with " + prefix) //出错信息不能以大写开头
}
path := request.URL.Path[len(prefix):]
file,err :=os.Open(path)
if err != nil {
//panic(err)
http.Error(writer,
err.Error(),
http.StatusInternalServerError)
return err
}
defer file.Close()
all,err := ioutil.ReadAll(file)
if err != nil {
//panic(err)
return err
}
writer.Writer(all)
return nil
}
error vs panic
意料之中的:使用error。如:文件打不开
意料之外的:使用panic。如:数组越界
defer + panic + recover
Type Assertion
函数式编程的应用