代码:https://github.com/NIGHTFIGHTING/go_learning/tree/master/src/errhandling/filelistingserver
运行:go run web.go
打开chrome:http://localhost:8888/li,此时server并没有挂掉
原因:panic运行defer遇见recover
修改版本1:加入defer recover处理
// 函数式编程
func errWrapper(
handler appHandler) func(
http.ResponseWriter,*http.Request) {
return func(writer http.ResponseWriter,
request *http.Request) {
// 防止访问这种地址出错,http://localhost:8888/li
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
err := handler(writer, request)
// 错误统一处理
if err != nil {
//import "github.com/gpmgo/gopm/modules/log"
//log.Warn("Error handling request: %s",
// err.Error())
log.Printf("Error handling request: %s",
err.Error())
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)
}
}
}
执行defer recover中的处理,
修改版本2:直接拦截非/list/的prefix报错,errors.New()
filelisting/handler.go
package filelisting
import (
"net/http"
"os"
"io/ioutil"
"strings"
"errors"
)
const prefix = "/list/"
func HandleFileList(writer http.ResponseWriter,
request *http.Request) error {
// 请求的url不是/list/开头
// 防止访问这种地址出错,http://localhost:8888/li
if strings.Index(
request.URL.Path, prefix) != 0 {
return errors.New("path must start " +
"with " + prefix)
}
path := request.URL.Path[len(prefix):] // /list/fib.txt
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.Write(all)
return nil
}
非/list/的prefix直接报错。问题是这个报错是可以给用户显示的,需要区分错误可否给用户展示
修改版本3:区分错误是否可以给用户展示
1.增加userError interface接口(web.go)
2.handler处理返回err != nil,判断err是否为userError(web.go)
// 函数式编程
func errWrapper(
handler appHandler) func(
http.ResponseWriter,*http.Request) {
return func(writer http.ResponseWriter,
request *http.Request) {
// 防止访问这种地址出错,http://localhost:8888/li
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
err := handler(writer, request)
// 错误统一处理
if err != nil {
//import "github.com/gpmgo/gopm/modules/log"
//log.Warn("Error handling request: %s",
// err.Error())
log.Printf("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.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 error interface {
// Error() string
// }
type userError interface {
error
Message() string
}
3.实现userError interface接口(filelisting/handler.go),并且prefix返回userError
package filelisting
import (
"net/http"
"os"
"io/ioutil"
"strings"
//"errors"
)
const prefix = "/list/"
// 实现type userError interface接口
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 {
// 请求的url不是/list/开头
// 防止访问这种地址出错,http://localhost:8888/li
if strings.Index(
request.URL.Path, prefix) != 0 {
//return errors.New("path must start " +
// "with " + prefix)
return userError("path must start " +
"with " + prefix)
}
path := request.URL.Path[len(prefix):] // /list/fib.txt
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.Write(all)
return nil
}