地鼠新手在这里。请善良:-)
我在一个运行Apache + FastCGI的共享服务器上有一个账户,但我无法控制它。不过,它与Go语言的接口很流畅。我更习惯于在net/http
上使用Go语言,但弄清楚如何在net/http/fcgi
上使用它似乎很简单。下面是我的最小测试应用程序:
package main
import (
"fmt"
"net/http"
"net/http/fcgi"
)
func handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-type", "text/plain; charset=utf-8")
fmt.Fprintln(w, "This was generated by Go running as a FastCGI app")
}
func main() {
/*
*
* Everything that is done here should be setup code etc. which is retained between calls
*
*/
http.HandleFunc("/", handler)
// This is what actually concurrently handles requests
if err := fcgi.Serve(nil, nil); err != nil {
panic(err)
}
}
现在这个完美无瑕地工作着:在将其编译成go-fcgi-test.fcgi
并放置在适当的目录下之后,Go代码将从http://my.shared.web.server/go-fcgi-test.fcgi
这样的URL运行,为了简单起见,我省略了大部分实际处理--但这对于提取表单参数、ENV变量(在Go语言1.9下!)等非常有效,所以我知道基本设置一定没问题。
让我们看一个稍微复杂一点的例子:
package main
import (
"fmt"
"net/http"
"net/http/fcgi"
)
func handler1(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-type", "text/plain; charset=utf-8")
fmt.Fprintln(w, "This comes from handler1")
}
func handler2(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-type", "text/plain; charset=utf-8")
fmt.Fprintln(w, "This comes from handler2")
}
func main() {
http.HandleFunc("/first", handler1)
http.HandleFunc("/", handler2)
if err := fcgi.Serve(nil, nil); err != nil {
panic(err)
}
}
现在,在这个场景中,我期望http://my.shared.web.server/go-fcgi-test.fcgi
输出This comes from handler2
,事实上,这正是所发生的。
但是为什么http://my.shared.web.server/go-fcgi-test.fcgi/first
实际上也调用handler2
,也就是说handler1
被完全忽略了呢?注意handler2
* 确实 * 得到了URL的/first
位-- Apache * 没有 * 将其剥离--因为我可以读取r.URL.Path[1:]
并确认这是发送给Go应用程序的整个路径。
我在网上找到的所有使用类似FastCGI框架的例子都只显示了一个处理程序。这是FastCGI包本身的限制吗?是FastCGI协议的限制(但是为什么整个路径都正确发送了呢?)?是Apache配置中做了什么强加了这个限制(记住,我不能触摸Apache配置)?还是我做错了什么?
(For为了完整起见,我应该补充一点,是的,我已经尝试了上面的几种变体,重命名Go应用程序,使用子文件夹,使用 * 几个 * 处理程序,而不仅仅是一个,等等。)
我的真实的场景实际上是一个小应用程序,它应该作为使用net/http
的独立Web服务器运行 *,或者在独立模式不受支持甚至被禁止的情况下作为FastCGI应用程序运行(这是共享环境的一些提供者的情况)。由于实际处理对于任一种情况是完全相同的,唯一的区别是调用fcgi.Serve()
而不是http.ListenAndServe()
。但是,如果能够在FastCGI下使用具有不同处理程序的net/http
包的路由能力,那就太好了。
提前感谢你的见解。即使答案是“是的,这 * 正是 * FastCGI实现在Go语言下的工作方式--只有一个处理程序!”,这仍然是有用的--这意味着我只需要围绕我自己的代码,以不同的方式做事(基本上,基于通过Form接口传递的参数创建我自己的路由器/调度器--没什么大不了的,这是可行的!)
2条答案
按热度按时间2ic8powd1#
我知道这是一个老帖子,但我自己才刚开始玩围棋和fcgi,就遇到了同样的问题。
简短的回答是肯定的,使用多个处理程序确实有意义。示例中的缺陷是您没有考虑到
go-fcgi-test.fcgi
是URL的一部分。当Go语言的ServeMux处理URL时,它使用的是请求的完整URL,而不仅仅是fcgi进程处理的部分,在
http://my.shared.web.server/go-fcgi-test.fcgi/first
的情况下,程序会寻找与/go-fcgi-test.fcgi/first
最接近的匹配项,即/
。yr9zkbsy2#
我只是为了历史的目的而保留以下几点。提醒你一下,这并不是“完全错误”;但这是基于我对传递给ServeMux的实际URL的无知。现在我知道了:)
在阅读了@Kodiak提供的答案后,我重新阅读了ServeMux的文档,并看到了这一段:
注意,由于以斜杠结尾的模式命名了一个根目录子树,因此模式“/”匹配所有未被其他注册模式匹配的路径,而不仅仅是Path ==“/"的URL。
如果子树已经注册,并且接收到命名子树根 * 而没有其尾部斜杠 * 的请求,则ServeMux将该请求重定向到子树根(添加尾部斜杠)。此行为可通过单独注册不带尾部斜杠的路径来覆盖。例如,注册“/images/”会导致ServeMux将对“/images”的请求重定向到“/images/",除非“/images”已单独注册。
(斜体为我所用)
我的“假设”是
ServeMux
的行为与nginx
和/或Apache的rewrite
模块的基于规则的模式匹配特性非常相似,即规则是根据它们注册的顺序处理的,因此我希望首先匹配/first
(双关语),只有在没有找到匹配时,才匹配/
。但这并不是文档所说的那样,相反,注册的顺序并没有真实的的区别;
ServeMux
,在我给出的场景中,因为我忘了添加一个尾部斜杠,将 * 总是 “退回”到"/"
的处理程序,这不是因为匹配算法的错误或反常, 而是因为这是Go语言开发人员编写的预期行为 *!换句话说,如果您有"/"
的处理程序,它充当每个非斜线终止的子树的捕获器。我只是没能正确阅读文档!(或者也许早在2017年,这一段不够清楚)