Golang Web开发中的自定义错误页面路由 Go语言处理404与500异常

5次阅读

go http server 默认不支持自定义404/500页面,需通过自定义handler捕获未注册路由并用defer+recover拦截panic,同时静态资源须统一挂载至/Static/并使用绝对路径引用。

Golang Web开发中的自定义错误页面路由 Go语言处理404与500异常

Go HTTP Server 默认不支持自定义 404 和 500 页面

Go 标准库的 http.ServeMux 在找不到路由时直接返回硬编码的 “404 page not found”,且不调用你写的 handler;发生 panic 时更不会自动转到错误页面,而是直接中断响应或返回空内容。这不是 bug,是设计使然——http.ServeMux 不处理错误传播,也不拦截 panic。

所以你写了个 http.HandleFunc("/404", ...),但用户访问不存在路径时根本不会走到那里。

  • 所有未注册路径都由 http.ServeMux 内部兜底,不经过你的中间逻辑
  • http.Error 只能手动触发,无法自动捕获 panic 或 404
  • 标准 http.Server 没有类似 expressapp.use((err, req, res, next) => {...}) 错误中间件机制

用自定义 ServeMux 替换默认 mux 处理 404

核心思路:自己实现 http.Handler,在 ServeHTTP 中先查路由,查不到就调用你的 404 handler,而不是依赖默认行为。

别直接改 http.DefaultServeMux,它被多处隐式引用,容易出错;应该新建一个 http.ServeMux 实例,再包一层。

立即学习go语言免费学习笔记(深入)”;

  • 创建新 http.ServeMux,注册所有正常路由
  • 用匿名函数包装它的 ServeHTTP 方法,在 HandlerFunc 里检查是否命中——没命中就执行自定义 404 逻辑
  • 404 handler 必须手动设置 w.WriteHeader(http.StatusNotFound),否则状态码仍是 200
func main() { 	mux := http.NewServeMux() 	mux.HandleFunc("/", homeHandler) 	mux.HandleFunc("/api/", apiHandler)  	// 包装 mux,接管未命中路径 	http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 		if _, pattern := mux.Handler(r); pattern == "" { 			notFoundHandler(w, r) 			return 		} 		mux.ServeHTTP(w, r) 	})) }

用 defer + recover 捕获 panic 实现 500 页面

Go HTTP handler 中 panic 会终止当前 goroutine,但默认不返回任何响应体,客户端可能卡住或收到空响应。必须显式 recover 并写入错误页面。

不能只在 main 入口 recover——那是 server 启动时的 panic;每个 handler 执行上下文是独立的,recover 必须放在 handler 内部。

  • 在每个顶层 handler 函数开头加 defer func() 块,里面做 recover()
  • recover 后要立即调用 w.WriteHeader(http.StatusInternalServerError),否则状态码还是 200
  • 避免在 recover 里调用可能 panic 的代码(比如模板渲染),否则二次 panic 无法被捕获
  • 生产环境建议记录 panic stack,但不要直接返回给前端(debug.PrintStack() 仅用于开发)
func riskyHandler(w http.ResponseWriter, r *http.Request) { 	defer func() { 		if err := recover(); err != nil { 			w.WriteHeader(http.StatusInternalServerError) 			fmt.Fprint(w, "<h1>Server Error</h1>") 			log.Printf("panic: %v", err) 		} 	}()  	// 可能 panic 的业务逻辑 	panic("something went wrong") }

HTML 模板复用与静态资源路径陷阱

你写了漂亮的 404.html500.html,但用 html/template 渲染时发现 CSS 不加载、图片 404——大概率是静态资源路径没配对。

Go 的 http.FileServer 默认以文件系统路径为根,而模板里写的 /static/css/app.css 是 URL 路径,两者映射关系必须显式声明。

  • 静态资源目录建议统一挂载在 /static/,用 http.StripPrefix("/static/", http.FileServer(...)) 配置
  • 模板中所有资源链接必须以 /static/ 开头,不能用相对路径(如 ./css/app.css),因为不同 URL 深度下相对路径解析结果不同
  • 如果用了子路径部署(比如 /myapp/404),模板里的路径需动态注入 base URL,不能写死
  • 注意 http.ServeFile 不支持目录列表,别误用它服务整个 static/ 目录

复杂点在于:404 页面本身也是 HTML,但它可能被任意无效路径触发(比如 /a/b/c/d/e),此时浏览器会尝试从该路径下加载 ./static/...,导致 404 嵌套。唯一稳的方式是所有资源走绝对路径 /static/xxx,并确保 FileServer 正确挂载。

text=ZqhQzanResources