我仍在学习如何使用Go进行网页开发,但当我试图创建一个简单的网站时,我面临着以下困难:

package main

import (
   "fmt"
   "html/template"
   "net/http"
)

func main() {

   fs := http.FileServer(http.Dir(""))

   http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
       tmpl, _ := template.ParseFiles("index.html")
       tmpl.Execute(w, nil)
   })

   /**
   *    This route will return a 404 error
   */
   http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
       fmt.Fprint(w, "Test page")
   })

   /**
   *    If I replace fs to nil, /test route will be work, but non-existent routes
   *    will be return index.html template (home router) instead 404 error. Why?
   */

   http.ListenAndServe(":80", fs)
}

http.FileServerhttp.HandleFunc之间存在冲突.

例如,当我写下:http.ListenAndServe(":80", nil)时,所有的路由(http.HandleFunc)都会起作用,但如果我try 这样做:

http.ListenAndServe(":80", http. FileServer(http.Dir("")))

无路由可用(http.HandleFunc("/"))除外.为什么?

以及如何覆盖404错误页面?我希望Go有一个类似http.HandleError的方法,可以接受http.ResponseWriterhttp.Request的接口,但我找不到任何类似的东西.

推荐答案

如果我将fs替换为nil,/test路由将有效,但不存在路由将返回index.html模板(主路由),而不是404错误.为什么?

判断文档中是否有http.ListenAndServe:

func ListenAndServe(addr string, handler Handler) error
The handler is typically nil, in which case the DefaultServeMux is used.

如果handler为零,则将使用默认处理程序,并且为http.HandleFunc:

HandleFunc在DefaultServeMux中注册给定模式的处理程序函数.ServeMux的文档解释了如何匹配模式.

因此,在您的代码中,您使用DefaultServeMux注册了两个路由;调用http.ListenAndServe(":80", nil)使用默认处理程序(您向其添加了路由),因此/test可以工作(下面有更多信息!).然而,当您运行http.ListenAndServe(":80", fs)时,您将传入一个特定的处理程序(fs),因此所有请求都将被发送到该处理程序(它将try 从您的本地文件系统提供文件).

从这一点开始,我将假设正在使用http.ListenAndServe(":80", nil)个处理程序(因为添加处理程序然后不使用它们并没有真正的意义).

上面提到ServeMux个,让我们来判断一下文档:

模式命名固定的有根路径,如"/folic.ico",或有根子树,如"/Images/"(请注意尾部的斜杠).较长的模式优先于较短的模式,因此,如果同时为"/Images/"和"/Image/Thumbnages/"注册了处理程序,则将为以"/Images/Thumbnailes/"开头的路径调用后一个处理程序,而前者将接收对"/Images/"子树中任何其他路径的请求.

因此,Mux接受请求并确定应该调用哪个处理程序来处理该请求(请注意,只会调用一个处理程序).匹配基于模式的长度(因此,在您的示例中,/test/长,因此优先).这意味着对/test的请求将触发fmt.Fprint(w, "Test page"),而其他一切都将调用加载index.html的处理程序.重要的是要注意,您没有添加引用fs的处理程序,因此它是未使用的(并且代码不会编译-playground).

希望这能解释正在发生的事情;就实现你想要的结果而言,这里有一些问题应该会有所帮助:

Go相关问答推荐

go测试10m后如何避免超时

有没有办法让sqlc生成可以使用pgxpool的代码

如何用mpb创建两行进度条?

Kubo,来自 IpfsNode.Bootstrap 的无效内存地址或零指针取消引用

用于提取 <*n 的正则表达式(其中 n 是一个数字)

xml.Unmarshal 不支持的类型 struct

为什么 net/http 不遵守超过 30 秒的超时持续时间?

Opensearch 错误 ping 弹性服务器:由未知权威签署的 x509 证书

golang yaml 马歇尔网址

go - 仅在函数即将返回错误时清理资源

整理时转换值

函数实现接口时的模式名称是什么?

访问传递给可变参数函数的通用 struct 的特定字段

如何仅提取时间作为持续时间

Go 中的 HTTP 请求验证中间件

使用 Golang SQL 驱动程序连接到snowflake

同时调用应该只获取一次数据

Terraform 自定义提供程序 - 数据源架构

try 创建新的 etcdv3 客户端时出现pc error: code = Unavailable desc = error reading from server: EOF

Go 错误:cannot use generic type without instantiation