Beego 使用教程 7:Web 文件上传下载和错误处理
beego 是一个用于Go编程语言的开源、高性能的 web 框架
beego 被用于在Go语言中企业应用程序的快速开发,包括RESTful API、web应用程序和后端服务。它的灵感来源于Tornado, Sinatra 和 Flask
beego 官网:http://beego.gocn.vip/
上面的 beego 官网如果访问不到,看这篇文章《beego 官网文档本地环境搭建》
注意:本文的 beego 文档使用的就是本地环境搭建的文档,因为官网文档已经不可用了
beego 官方 github 仓库:https://github.com/beego/beego
上一讲,讲了 web 输入处理,需要的朋友可以查看《Beego 使用教程 6:Web 输入处理》
这一讲,讲解 web 文件上传下载和错误处理,代码使用上一讲的代码
目录
1、文件上传
2、文件下载
3、错误处理
3.1、重定向
3.2、中止此次请求并抛出异常
3.3、自定义 401、403、404、500、503 错误处理
3.4、Controller 定义 Error
3.5、从 panic 中恢复
1、文件上传
文件上传之后一般是放在系统的内存里面,如果文件的 size 大于设置的缓存内存大小,那么就放在临时文件中,默认的缓存内存是 64M,你可以通过如下来调整这个缓存内存大小:
web.MaxMemory = 1<<22
或者在配置文件中通过如下设置:
maxmemory = 1<<22
与此同时,Beego 提供了另外一个参数,MaxUploadSize来限制最大上传文件大小——如果你一次长传多个文件,那么它限制的就是这些所有文件合并在一起的大小
默认情况下,MaxMemory应该设置得比MaxUploadSize小,这种情况下两个参数合并在一起的效果则是:
如果文件大小小于MaxMemory,则直接在内存中处理;
如果文件大小介于MaxMemory和MaxUploadSize之间,那么比MaxMemory大的部分将会放在临时目录;
文件大小超出MaxUploadSize,直接拒绝请求,返回响应码 413
在 controller 目录下新建 file.go,内容是下面代码
package controller
import (
"fmt"
"github.com/beego/beego/v2/server/web"
"log"
)
type FileController struct {
web.Controller
}
func (f *FileController) Upload() {
file, header, er := f.GetFile("file")
if er != nil {
log.Fatal("getfile err ", er)
}
defer file.Close()
fmt.Println(header.Filename)
f.SaveToFile("file", "E:\tmp\file\"+header.Filename)
f.Ctx.WriteString("上传成功")
}
修改 main.go 为下面代码,添加 Upload 的路由
package main
import (
"beego-demo/controller"
"beego-demo/filter"
"fmt"
"github.com/beego/beego/v2/core/config"
"github.com/beego/beego/v2/server/web"
)
func main() {
//通过config获取自定义配置
workername, _ := config.String("workername")
fmt.Println(workername)
//执行定时任务
//go job.DemoTask()
//注册自动路由
//web.AutoPrefix("api", &controller.UserController{})
web.CtrlGet("/name", (*controller.UserController).Name)
web.CtrlGet("/get/:id", (*controller.UserController).GetUserById)
web.CtrlGet("/get/:id/:num", (*controller.UserController).GetUserByIdAndNum)
//返回页面
web.CtrlGet("/page/index", (*controller.PageController).Index)
web.CtrlGet("/page/f", (*controller.PageController).F)
web.CtrlGet("/page/main", (*controller.PageController).Main)
//web输入参数
web.CtrlGet("/pathparam/:name", (*controller.ParamController).PathParam)
web.CtrlGet("/getparam", (*controller.ParamController).GetParam)
web.CtrlPost("/postparam", (*controller.ParamController).PostParam)
web.CtrlPost("/bindparam", (*controller.ParamController).BindParam)
//上传文件
web.CtrlPost("/upload", (*controller.FileController).Upload)
//注册函数式路由
controller.RegisterFunctionalRoutes()
//web命名空间
controller.RegisterNamespaceRoutes()
//过滤器
filter.RegisterFilters()
//开启 Admin 管理后台
web.BConfig.Listen.EnableAdmin = true
web.BConfig.Listen.AdminAddr = "localhost"
web.BConfig.Listen.AdminPort = 8088
//web.BConfig.WebConfig.ViewsPath = "pages"
//开启post 请求 bind绑定请求体
web.BConfig.CopyRequestBody = true
//查看已注册路由
tree := web.PrintTree()
methods := tree["Data"].(web.M)
for k, v := range methods {
fmt.Printf("%s => %v
", k, v)
}
//自定义模板函数
web.AddFuncMap("bookName", bookName)
web.Run()
}
// 自定义模板函数添加书名号
func bookName(in string) (out string) {
out = "《" + in + "》"
return
}
修改 login.html 为下面代码
登录
post 请求
运行效果
浏览器访问:http://localhost:9090/page/main
2、文件下载
Beego 直接提供了一个下载文件的方法Download
func (output *BeegoOutput) Download(file string, filename ...string) {}
修改 controller 目录下 file.go 为下面代码
package controller
import (
"fmt"
"github.com/beego/beego/v2/server/web"
"log"
)
type FileController struct {
web.Controller
}
func (f *FileController) Upload() {
file, header, er := f.GetFile("file")
if er != nil {
log.Fatal("getfile err ", er)
}
defer file.Close()
fmt.Println(header.Filename)
f.SaveToFile("file", "E:\tmp\file\"+header.Filename)
f.Ctx.WriteString("上传成功")
}
func (f *FileController) Download() {
f.Ctx.Output.Download("E:\tmp\file.png", "1.png")
}
修改 main.go 为下面代码,添加 Download 的路由
package main
import (
"beego-demo/controller"
"beego-demo/filter"
"fmt"
"github.com/beego/beego/v2/core/config"
"github.com/beego/beego/v2/server/web"
)
func main() {
//通过config获取自定义配置
workername, _ := config.String("workername")
fmt.Println(workername)
//执行定时任务
//go job.DemoTask()
//注册自动路由
//web.AutoPrefix("api", &controller.UserController{})
web.CtrlGet("/name", (*controller.UserController).Name)
web.CtrlGet("/get/:id", (*controller.UserController).GetUserById)
web.CtrlGet("/get/:id/:num", (*controller.UserController).GetUserByIdAndNum)
//返回页面
web.CtrlGet("/page/index", (*controller.PageController).Index)
web.CtrlGet("/page/f", (*controller.PageController).F)
web.CtrlGet("/page/main", (*controller.PageController).Main)
//web输入参数
web.CtrlGet("/pathparam/:name", (*controller.ParamController).PathParam)
web.CtrlGet("/getparam", (*controller.ParamController).GetParam)
web.CtrlPost("/postparam", (*controller.ParamController).PostParam)
web.CtrlPost("/bindparam", (*controller.ParamController).BindParam)
//上传文件
web.CtrlPost("/upload", (*controller.FileController).Upload)
//下载文件
web.CtrlGet("/download", (*controller.FileController).Download)
//注册函数式路由
controller.RegisterFunctionalRoutes()
//web命名空间
controller.RegisterNamespaceRoutes()
//过滤器
filter.RegisterFilters()
//开启 Admin 管理后台
web.BConfig.Listen.EnableAdmin = true
web.BConfig.Listen.AdminAddr = "localhost"
web.BConfig.Listen.AdminPort = 8088
//web.BConfig.WebConfig.ViewsPath = "pages"
//开启post 请求 bind绑定请求体
web.BConfig.CopyRequestBody = true
//查看已注册路由
tree := web.PrintTree()
methods := tree["Data"].(web.M)
for k, v := range methods {
fmt.Printf("%s => %v
", k, v)
}
//自定义模板函数
web.AddFuncMap("bookName", bookName)
web.Run()
}
// 自定义模板函数添加书名号
func bookName(in string) (out string) {
out = "《" + in + "》"
return
}
浏览器请求:http://localhost:9090/download
运行效果
3、错误处理
3.1、重定向
在 controller 目录下新建 error.go,error.go 内容是下面代码
package controller
import (
"github.com/beego/beego/v2/server/web"
)
type ErrorHandlerController struct {
web.Controller
}
func (this *ErrorHandlerController) GetUserName() {
this.Redirect("/page/main", 302)
}
在 main.go 中添加 GetUserName 路由
package main
import (
"beego-demo/controller"
"beego-demo/filter"
"fmt"
"github.com/beego/beego/v2/core/config"
"github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/context"
)
func main() {
//通过config获取自定义配置
workername, _ := config.String("workername")
fmt.Println(workername)
//执行定时任务
//go job.DemoTask()
//注册自动路由
//web.AutoPrefix("api", &controller.UserController{})
web.CtrlGet("/name", (*controller.UserController).Name)
web.CtrlGet("/get/:id", (*controller.UserController).GetUserById)
web.CtrlGet("/get/:id/:num", (*controller.UserController).GetUserByIdAndNum)
//返回页面
web.CtrlGet("/page/index", (*controller.PageController).Index)
web.CtrlGet("/page/f", (*controller.PageController).F)
web.CtrlGet("/page/main", (*controller.PageController).Main)
//web输入参数
web.CtrlGet("/pathparam/:name", (*controller.ParamController).PathParam)
web.CtrlGet("/getparam", (*controller.ParamController).GetParam)
web.CtrlPost("/postparam", (*controller.ParamController).PostParam)
web.CtrlPost("/bindparam", (*controller.ParamController).BindParam)
//上传文件
web.CtrlPost("/upload", (*controller.FileController).Upload)
//下载文件
web.CtrlGet("/download", (*controller.FileController).Download)
//错误处理
web.CtrlGet("/getUserName", (*controller.ErrorHandlerController).GetUserName)
//注册函数式路由
controller.RegisterFunctionalRoutes()
//web命名空间
controller.RegisterNamespaceRoutes()
//过滤器
filter.RegisterFilters()
//开启 Admin 管理后台
web.BConfig.Listen.EnableAdmin = true
web.BConfig.Listen.AdminAddr = "localhost"
web.BConfig.Listen.AdminPort = 8088
//web.BConfig.WebConfig.ViewsPath = "pages"
//开启post 请求 bind绑定请求体
web.BConfig.CopyRequestBody = true
//查看已注册路由
tree := web.PrintTree()
methods := tree["Data"].(web.M)
for k, v := range methods {
fmt.Printf("%s => %v
", k, v)
}
//自定义模板函数
web.AddFuncMap("bookName", bookName)
web.Run()
}
// 自定义模板函数添加书名号
func bookName(in string) (out string) {
out = "《" + in + "》"
return
}
运行效果
浏览器访问:http://localhost:9090/getUserName
3.2、中止此次请求并抛出异常
在 error.go 中修改为下面代码
package controller
import (
"github.com/beego/beego/v2/server/web"
)
type ErrorHandlerController struct {
web.Controller
}
func (this *ErrorHandlerController) GetUserName() {
this.Redirect("/page/main", 302)
}
func (this *ErrorHandlerController) GetUserAge() {
//this.Abort("404") 之后的代码不会再执行
this.Abort("404")
this.Ctx.WriteString("我的年龄是18岁")
}
在 main.go 中添加 GetUserAge 的路由
package main
import (
"beego-demo/controller"
"beego-demo/filter"
"fmt"
"github.com/beego/beego/v2/core/config"
"github.com/beego/beego/v2/server/web"
)
func main() {
//通过config获取自定义配置
workername, _ := config.String("workername")
fmt.Println(workername)
//执行定时任务
//go job.DemoTask()
//注册自动路由
//web.AutoPrefix("api", &controller.UserController{})
web.CtrlGet("/name", (*controller.UserController).Name)
web.CtrlGet("/get/:id", (*controller.UserController).GetUserById)
web.CtrlGet("/get/:id/:num", (*controller.UserController).GetUserByIdAndNum)
//返回页面
web.CtrlGet("/page/index", (*controller.PageController).Index)
web.CtrlGet("/page/f", (*controller.PageController).F)
web.CtrlGet("/page/main", (*controller.PageController).Main)
//web输入参数
web.CtrlGet("/pathparam/:name", (*controller.ParamController).PathParam)
web.CtrlGet("/getparam", (*controller.ParamController).GetParam)
web.CtrlPost("/postparam", (*controller.ParamController).PostParam)
web.CtrlPost("/bindparam", (*controller.ParamController).BindParam)
//上传文件
web.CtrlPost("/upload", (*controller.FileController).Upload)
//下载文件
web.CtrlGet("/download", (*controller.FileController).Download)
//错误处理
web.CtrlGet("/getUserName", (*controller.ErrorHandlerController).GetUserName)
web.CtrlGet("/getUserAge", (*controller.ErrorHandlerController).GetUserAge)
//注册函数式路由
controller.RegisterFunctionalRoutes()
//web命名空间
controller.RegisterNamespaceRoutes()
//过滤器
filter.RegisterFilters()
//开启 Admin 管理后台
web.BConfig.Listen.EnableAdmin = true
web.BConfig.Listen.AdminAddr = "localhost"
web.BConfig.Listen.AdminPort = 8088
//web.BConfig.WebConfig.ViewsPath = "pages"
//开启post 请求 bind绑定请求体
web.BConfig.CopyRequestBody = true
//查看已注册路由
tree := web.PrintTree()
methods := tree["Data"].(web.M)
for k, v := range methods {
fmt.Printf("%s => %v
", k, v)
}
//自定义模板函数
web.AddFuncMap("bookName", bookName)
web.Run()
}
// 自定义模板函数添加书名号
func bookName(in string) (out string) {
out = "《" + in + "》"
return
}
运行效果
浏览器访问:http://localhost:9090/getUserAge
3.3、自定义 401、403、404、500、503 错误处理
web 框架默认支持 401、403、404、500、503 这几种错误的处理。用户可以自定义相应的错误处理
在 error.go 中修改为下面代码
package main
import (
"beego-demo/controller"
"beego-demo/filter"
"fmt"
"github.com/beego/beego/v2/core/config"
"github.com/beego/beego/v2/server/web"
"html/template"
"net/http"
)
func main() {
//通过config获取自定义配置
workername, _ := config.String("workername")
fmt.Println(workername)
//执行定时任务
//go job.DemoTask()
//注册自动路由
//web.AutoPrefix("api", &controller.UserController{})
web.CtrlGet("/name", (*controller.UserController).Name)
web.CtrlGet("/get/:id", (*controller.UserController).GetUserById)
web.CtrlGet("/get/:id/:num", (*controller.UserController).GetUserByIdAndNum)
//返回页面
web.CtrlGet("/page/index", (*controller.PageController).Index)
web.CtrlGet("/page/f", (*controller.PageController).F)
web.CtrlGet("/page/main", (*controller.PageController).Main)
//web输入参数
web.CtrlGet("/pathparam/:name", (*controller.ParamController).PathParam)
web.CtrlGet("/getparam", (*controller.ParamController).GetParam)
web.CtrlPost("/postparam", (*controller.ParamController).PostParam)
web.CtrlPost("/bindparam", (*controller.ParamController).BindParam)
//上传文件
web.CtrlPost("/upload", (*controller.FileController).Upload)
//下载文件
web.CtrlGet("/download", (*controller.FileController).Download)
//错误处理
web.CtrlGet("/getUserName", (*controller.ErrorHandlerController).GetUserName)
web.CtrlGet("/getUserAge", (*controller.ErrorHandlerController).GetUserAge)
//注册函数式路由
controller.RegisterFunctionalRoutes()
//web命名空间
controller.RegisterNamespaceRoutes()
//过滤器
filter.RegisterFilters()
//开启 Admin 管理后台
web.BConfig.Listen.EnableAdmin = true
web.BConfig.Listen.AdminAddr = "localhost"
web.BConfig.Listen.AdminPort = 8088
//web.BConfig.WebConfig.ViewsPath = "pages"
//开启post 请求 bind绑定请求体
web.BConfig.CopyRequestBody = true
//查看已注册路由
tree := web.PrintTree()
methods := tree["Data"].(web.M)
for k, v := range methods {
fmt.Printf("%s => %v
", k, v)
}
//自定义模板函数
web.AddFuncMap("bookName", bookName)
//自定义401返回
web.ErrorHandler("401", page401)
//自定义404返回
web.ErrorHandler("404", page404)
web.Run()
}
// 自定义模板函数添加书名号
func bookName(in string) (out string) {
out = "《" + in + "》"
return
}
func page401(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("401.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/401.html")
data := make(map[string]interface{})
data["content"] = "没有访问权限"
t.Execute(rw, data)
}
func page404(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("404.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/404.html")
data := make(map[string]interface{})
data["content"] = "页面没找到"
t.Execute(rw, data)
}
自定义401 和 404 返回,笔者返回自己定义的页面
下面是 401 和 404 页面,在 views 目录下新建 401.html 和 404.html
401.html 代码
Title
我是401
{{.content}}
404.html 代码
Title
我是404
{{.content}}
运行测试
浏览器访问:http://localhost:9090/getUserAge
3.4、自定义字符串错误类型处理函数
除了上面的 401、403、404、500、503 这几种错误的处理之外,Beego 更加人性化的还有一个设计就是支持用户自定义字符串错误类型处理函数
修改 main.go 为下面代码
package main
import (
"beego-demo/controller"
"beego-demo/filter"
"fmt"
"github.com/beego/beego/v2/core/config"
"github.com/beego/beego/v2/server/web"
"html/template"
"net/http"
)
func main() {
//通过config获取自定义配置
workername, _ := config.String("workername")
fmt.Println(workername)
//执行定时任务
//go job.DemoTask()
//注册自动路由
//web.AutoPrefix("api", &controller.UserController{})
web.CtrlGet("/name", (*controller.UserController).Name)
web.CtrlGet("/get/:id", (*controller.UserController).GetUserById)
web.CtrlGet("/get/:id/:num", (*controller.UserController).GetUserByIdAndNum)
//返回页面
web.CtrlGet("/page/index", (*controller.PageController).Index)
web.CtrlGet("/page/f", (*controller.PageController).F)
web.CtrlGet("/page/main", (*controller.PageController).Main)
//web输入参数
web.CtrlGet("/pathparam/:name", (*controller.ParamController).PathParam)
web.CtrlGet("/getparam", (*controller.ParamController).GetParam)
web.CtrlPost("/postparam", (*controller.ParamController).PostParam)
web.CtrlPost("/bindparam", (*controller.ParamController).BindParam)
//上传文件
web.CtrlPost("/upload", (*controller.FileController).Upload)
//下载文件
web.CtrlGet("/download", (*controller.FileController).Download)
//错误处理
web.CtrlGet("/getUserName", (*controller.ErrorHandlerController).GetUserName)
web.CtrlGet("/getUserAge", (*controller.ErrorHandlerController).GetUserAge)
web.CtrlGet("/getUserAddr", (*controller.ErrorHandlerController).GetUserAddr)
//注册函数式路由
controller.RegisterFunctionalRoutes()
//web命名空间
controller.RegisterNamespaceRoutes()
//过滤器
filter.RegisterFilters()
//开启 Admin 管理后台
web.BConfig.Listen.EnableAdmin = true
web.BConfig.Listen.AdminAddr = "localhost"
web.BConfig.Listen.AdminPort = 8088
//web.BConfig.WebConfig.ViewsPath = "pages"
//开启post 请求 bind绑定请求体
web.BConfig.CopyRequestBody = true
//查看已注册路由
tree := web.PrintTree()
methods := tree["Data"].(web.M)
for k, v := range methods {
fmt.Printf("%s => %v
", k, v)
}
//自定义模板函数
web.AddFuncMap("bookName", bookName)
//自定义401返回
web.ErrorHandler("401", page401)
//自定义404返回
web.ErrorHandler("404", page404)
web.ErrorHandler("dbError", dbError)
web.Run()
}
// 自定义模板函数添加书名号
func bookName(in string) (out string) {
out = "《" + in + "》"
return
}
func page401(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("401.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/401.html")
data := make(map[string]interface{})
data["content"] = "没有访问权限"
t.Execute(rw, data)
}
func page404(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("404.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/404.html")
data := make(map[string]interface{})
data["content"] = "页面没找到"
t.Execute(rw, data)
}
func dbError(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("dberror.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/dberror.html")
data := make(map[string]interface{})
data["content"] = "我是自定义字符串错误类型处理函数"
t.Execute(rw, data)
}
在 views 目录下新建 dberror.html ,dberror.html 内容是下面代码
Title
dberror
{{.content}}
修改 error.go 为下面代码
package controller
import (
"github.com/beego/beego/v2/server/web"
)
type ErrorHandlerController struct {
web.Controller
}
func (this *ErrorHandlerController) GetUserName() {
this.Redirect("/page/main", 302)
}
func (this *ErrorHandlerController) GetUserAge() {
//this.Abort("404") 之后的代码不会再执行
this.Abort("404")
this.Ctx.WriteString("我的年龄是18岁")
}
func (this *ErrorHandlerController) GetUserAddr() {
this.Abort("dbError")
}
运行效果
浏览器访问:http://localhost:9090/getUserAddr
3.4、Controller 定义 Error
从 1.4.3 版本开始,支持 Controller 方式定义 Error 错误处理函数,这样就可以充分利用系统自带的模板处理,以及 context 等方法
在 controller 目录下新建 er.go ,内容是下面代码
package controller
import (
"fmt"
"github.com/beego/beego/v2/server/web"
)
type ErrorController struct {
web.Controller
}
func (c *ErrorController) Error501() {
fmt.Println("501.html")
c.Data["content"] = "server error"
c.TplName = "501.html"
}
通过上面的代码我们可以看到,函数都是有一定规律的,都是 Error 开头,后面的名字就是我们调用 Abort 的名字,例如 Error501 函数其实调用对应的就是 Abort(“501”)
只要在 web.Run 之前采用 controller.ErrorController 注册这个错误处理函数就可以了
下面是 main.go 的代码
package main
import (
"beego-demo/controller"
"beego-demo/filter"
"fmt"
"github.com/beego/beego/v2/core/config"
"github.com/beego/beego/v2/server/web"
"html/template"
"net/http"
)
func main() {
//通过config获取自定义配置
workername, _ := config.String("workername")
fmt.Println(workername)
//执行定时任务
//go job.DemoTask()
//注册自动路由
//web.AutoPrefix("api", &controller.UserController{})
web.CtrlGet("/name", (*controller.UserController).Name)
web.CtrlGet("/get/:id", (*controller.UserController).GetUserById)
web.CtrlGet("/get/:id/:num", (*controller.UserController).GetUserByIdAndNum)
//返回页面
web.CtrlGet("/page/index", (*controller.PageController).Index)
web.CtrlGet("/page/f", (*controller.PageController).F)
web.CtrlGet("/page/main", (*controller.PageController).Main)
//web输入参数
web.CtrlGet("/pathparam/:name", (*controller.ParamController).PathParam)
web.CtrlGet("/getparam", (*controller.ParamController).GetParam)
web.CtrlPost("/postparam", (*controller.ParamController).PostParam)
web.CtrlPost("/bindparam", (*controller.ParamController).BindParam)
//上传文件
web.CtrlPost("/upload", (*controller.FileController).Upload)
//下载文件
web.CtrlGet("/download", (*controller.FileController).Download)
//错误处理
web.CtrlGet("/getUserName", (*controller.ErrorHandlerController).GetUserName)
web.CtrlGet("/getUserAge", (*controller.ErrorHandlerController).GetUserAge)
web.CtrlGet("/getUserAddr", (*controller.ErrorHandlerController).GetUserAddr)
web.CtrlGet("/getUserGender", (*controller.ErrorHandlerController).GetUserGender)
//注册错误处理函数
web.ErrorController(&controller.ErrorController{})
//注册函数式路由
controller.RegisterFunctionalRoutes()
//web命名空间
controller.RegisterNamespaceRoutes()
//过滤器
filter.RegisterFilters()
//开启 Admin 管理后台
web.BConfig.Listen.EnableAdmin = true
web.BConfig.Listen.AdminAddr = "localhost"
web.BConfig.Listen.AdminPort = 8088
//web.BConfig.WebConfig.ViewsPath = "pages"
//开启post 请求 bind绑定请求体
web.BConfig.CopyRequestBody = true
//查看已注册路由
tree := web.PrintTree()
methods := tree["Data"].(web.M)
for k, v := range methods {
fmt.Printf("%s => %v
", k, v)
}
//自定义模板函数
web.AddFuncMap("bookName", bookName)
//自定义401返回
web.ErrorHandler("401", page401)
//自定义404返回
web.ErrorHandler("404", page404)
web.ErrorHandler("dbError", dbError)
web.Run()
}
// 自定义模板函数添加书名号
func bookName(in string) (out string) {
out = "《" + in + "》"
return
}
func page401(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("401.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/401.html")
data := make(map[string]interface{})
data["content"] = "没有访问权限"
t.Execute(rw, data)
}
func page404(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("404.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/404.html")
data := make(map[string]interface{})
data["content"] = "页面没找到"
t.Execute(rw, data)
}
func dbError(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("dberror.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/dberror.html")
data := make(map[string]interface{})
data["content"] = "我是自定义字符串错误类型处理函数"
t.Execute(rw, data)
}
修改 error.go 为下面代码
package controller
import (
"github.com/beego/beego/v2/server/web"
)
type ErrorHandlerController struct {
web.Controller
}
func (this *ErrorHandlerController) GetUserName() {
this.Redirect("/page/main", 302)
}
func (this *ErrorHandlerController) GetUserAge() {
//this.Abort("404") 之后的代码不会再执行
this.Abort("404")
this.Ctx.WriteString("我的年龄是18岁")
}
func (this *ErrorHandlerController) GetUserAddr() {
this.Abort("dbError")
}
func (this *ErrorHandlerController) GetUserGender() {
this.Abort("501")
}
在 views 目录下新建 501.html,代码是下面内容
Title
我是501
运行测试
浏览器请求:http://localhost:9090/getUserGender
3.5、从 panic 中恢复
如果你希望用户在服务器处理请求过程中,即便发生了 panic 依旧能够返回响应,那么可以使用 Beego 的恢复机制。该机制是默认开启的。依赖于配置项
web.BConfig.RecoverPanic = true
如果你需要关闭,那么将这个配置项设置为 false
就可以
如果你想自定义panic
之后的处理行为,那么可以重新设置 web.BConfig.RecoverFunc
web.BConfig.RecoverFunc = func(context *context.Context, config *web.Config) {
if err := recover(); err != nil {
context.WriteString(fmt.Sprintf("you panic, err: %v", err))
}
}
千万要注意:你永远需要检测 recover
的结果,并且将从 panic
中恢复过来的逻辑放在检测到recover
返回不为 nil
的代码里面
注意:自定义panic
之后的处理行为会覆盖前面的自定义错误处理
修改 main.go 为下面代码
package main
import (
"beego-demo/controller"
"beego-demo/filter"
"fmt"
"github.com/beego/beego/v2/core/config"
"github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/context"
"html/template"
"net/http"
)
func main() {
//通过config获取自定义配置
workername, _ := config.String("workername")
fmt.Println(workername)
//执行定时任务
//go job.DemoTask()
//注册自动路由
//web.AutoPrefix("api", &controller.UserController{})
web.CtrlGet("/name", (*controller.UserController).Name)
web.CtrlGet("/get/:id", (*controller.UserController).GetUserById)
web.CtrlGet("/get/:id/:num", (*controller.UserController).GetUserByIdAndNum)
//返回页面
web.CtrlGet("/page/index", (*controller.PageController).Index)
web.CtrlGet("/page/f", (*controller.PageController).F)
web.CtrlGet("/page/main", (*controller.PageController).Main)
//web输入参数
web.CtrlGet("/pathparam/:name", (*controller.ParamController).PathParam)
web.CtrlGet("/getparam", (*controller.ParamController).GetParam)
web.CtrlPost("/postparam", (*controller.ParamController).PostParam)
web.CtrlPost("/bindparam", (*controller.ParamController).BindParam)
//上传文件
web.CtrlPost("/upload", (*controller.FileController).Upload)
//下载文件
web.CtrlGet("/download", (*controller.FileController).Download)
//错误处理
web.CtrlGet("/getUserName", (*controller.ErrorHandlerController).GetUserName)
web.CtrlGet("/getUserAge", (*controller.ErrorHandlerController).GetUserAge)
web.CtrlGet("/getUserAddr", (*controller.ErrorHandlerController).GetUserAddr)
web.CtrlGet("/getUserGender", (*controller.ErrorHandlerController).GetUserGender)
//注册错误处理函数
web.ErrorController(&controller.ErrorController{})
//注册函数式路由
controller.RegisterFunctionalRoutes()
//web命名空间
controller.RegisterNamespaceRoutes()
//过滤器
filter.RegisterFilters()
//开启 Admin 管理后台
web.BConfig.Listen.EnableAdmin = true
web.BConfig.Listen.AdminAddr = "localhost"
web.BConfig.Listen.AdminPort = 8088
//web.BConfig.WebConfig.ViewsPath = "pages"
//开启post 请求 bind绑定请求体
web.BConfig.CopyRequestBody = true
//查看已注册路由
tree := web.PrintTree()
methods := tree["Data"].(web.M)
for k, v := range methods {
fmt.Printf("%s => %v
", k, v)
}
//自定义模板函数
web.AddFuncMap("bookName", bookName)
//自定义401返回
web.ErrorHandler("401", page401)
//自定义404返回
web.ErrorHandler("404", page404)
web.ErrorHandler("dbError", dbError)
web.BConfig.RecoverFunc = func(context *context.Context, config *web.Config) {
if err := recover(); err != nil {
context.WriteString(fmt.Sprintf("you panic, err: %v", err))
}
}
web.Run()
}
// 自定义模板函数添加书名号
func bookName(in string) (out string) {
out = "《" + in + "》"
return
}
func page401(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("401.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/401.html")
data := make(map[string]interface{})
data["content"] = "没有访问权限"
t.Execute(rw, data)
}
func page404(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("404.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/404.html")
data := make(map[string]interface{})
data["content"] = "页面没找到"
t.Execute(rw, data)
}
func dbError(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("dberror.html").ParseFiles(web.BConfig.WebConfig.ViewsPath + "/dberror.html")
data := make(map[string]interface{})
data["content"] = "我是自定义字符串错误类型处理函数"
t.Execute(rw, data)
}
运行效果
请求之前的错误页面
从 panic 中恢复更像是统一的错误处理
下一讲:《Beego 使用教程 8:Session 和 Cookie》
至此完