文件目录结构
test.go
package main
import (
"bufio"
"fmt"
"os"
"./fib"
)
/*
go语言资源管理
1.go使用defer调用来实现资源管理{
确保调用在函数结束发生的地方
参数在defer语句时计算
defer列表为先进后出
}
2.建了什么东西 都要想到defer掉{
open/close
lock/Unlock
PrintHeader/PrintFooter
}
错误处理
*/
func tryDefer() {
// defer里面有一个栈 多个语句先进后出 不怕中间出现return || panic
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
for i := 0; i < 100; i++ {
defer fmt.Println(i)
if i == 30 {
panic("printed too many")
}
}
}
func writeFile(filename string) {
file, err := os.Open(filename) // 新建文件要记住关掉 close()
if err != nil {
panic(err)
}
// err = errors.New("this is a custom error") // 自己新建error
// if err != nil {
// fmt.Println("Error: ", err)
// 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) // 要flush
defer writer.Flush()
f := fib.Fib()
for i := 0; i < 20; i++ {
fmt.Fprintln(writer, f())
}
}
func main() {
writeFile("fib.txt")
// tryDefer()
}
web.go
package main
import (
"fmt"
"log"
"net/http"
"os"
"./filelisting"
)
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) {
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 {
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 // 404
case os.IsPermission(err):
code = http.StatusForbidden // 403 没有权限
default:
code = http.StatusInternalServerError
}
http.Error(writer, http.StatusText(code), code)
}
}
}
func tryRecover() {
defer func() {
r := recover()
if err, ok := r.(error); ok {
fmt.Println("Error occurred:", err)
} else {
panic(err)
}
}()
// panic(errors.New("this is an error"))
// panic(123)
}
type userError interface {
error
Message() string
}
// 在url里面打一个文件地址 把文件内容显示出来
func main() {
// tryRecover()
http.HandleFunc("/",
errWrapper(filelisting.HandleFileList))
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
/*
panic 遇到错误未知 不建议经常用{
1.停止当前函数执行
2.一直向上返回,执行每一层的defer
3.如果没有遇见recover,程序退出
}
recover{
仅在defer函数中调用
获取panic的值
如果无法处理,可以重新panic
}
*/
/*
error vs panic{
1.意料之中的用error。如文件打不开
2.意料之外的使用panic。如数组越界
}
案例总合使用{
defer + panic + recover
type Assertion
函数式编程
}
*/
handle.go
package filelisting
import (
"io/ioutil"
"net/http"
"os"
"strings"
)
const prefix = "/list/"
type userError string
func (e userError) Error() string {
return e.Message()
}
func (e userError) Message() string {
return string(e)
}
// HandleFileList http业务
func HandleFileList(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):] // /list/fib.txt 切片
file, err := os.Open(path)
if err != nil {
return err
// panic(err) // 如果出错 程序并不会挂掉 会被保护起来 recover会保护服务
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
return err
}
writer.Write(all)
return nil
}
fib.go
package fib
// Fib 斐波那契数列
func Fib() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}