7-5 服务器统一出错处理2

代码: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
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值