英文:
How to compose a new ServeMux into a larger struct?
问题
在我之前创建的使用DefaultServeMux的golang应用程序中,如果我有一个像"/users/"
这样的路由,由func user(name string)
处理,例如,如果我发送一个请求到/users/jim
,请求仍然会由"/users/"
路由处理。在我当前创建的应用程序中,我没有使用DefaultServeMux
,而是仅传递一个实现ServeHTTP的处理程序,然后根据请求的URL进行切换。然而,现在,如果我发送一个不完全匹配的路由请求,处理程序函数就不会被调用。例如,如果我发送一个POST请求到"/api/jim"
,即使"/api"
被处理,我也会收到404错误。
我想保持我的应用程序不变(包含对数据库的引用),但也能处理不完全匹配的路由。
问题:假设我可以创建一个新的ServeMux来处理不完全匹配的路由,但如何将其与具有对数据库连接的Handler结构体组合起来?
type Handler struct{
DB *DB
}
func main() {
fmt.Println("Hello, playground")
db, err := sql.Open("postgres", dbinfo)
defer db.Close()
h := &Handler{
DB: db,
}
log.Fatal(http.ListenAndServe(":8888", h))
}
func (h *Handler)ServeHTTP(w http.ResponseWriter, r *http.Request){
switch r.URL.Path{
case "/":
h.serveRoot(w, r)
case "/api/":
h.apiRouter(w, r)
}
}
func (h *Handler)serveRoot(w http.ResponseWriter, r *http.Request){
h.DB.DoSomethingWithDB()
}
func (h *Handler)apiRouter(w http.ResponseWriter, r *http.Request){
switch r.URL.Path{
case "/":
h.serveRoot(w, r)
case "/api/":
h.apiRouter(w, r)
}
}
更新
由于与问题无关的原因,我不能使用DefaultServeMux。
英文:
In previous golang applications I've made that use the DefaultServeMux, if I had a route like this "/users/"
route that was handled by func user(name string)
, for example, and I sent a request to /users/jim
, the request would still be handled by the "/users/"
route. In the current application I'm making, I'm not using DefaultServeMux
but merely passing a handler that implements ServeHTTP and then switching on the request url. However, now, if I send a request to a route that doesn't exactly match, the handler function doesn't get called. For example, if I send a post request to "/api/jim"
, I get a 404 error even though "/api"
is handled.
I want to keep my application the way it is (with a reference to the DB in the handler) but also be able to handle routes that don't match exactly.
Question: Assuming I could create a new ServeMux to handle routes that don't exactly match, but how can I compose that with my type Handler Struct that has the reference to the database connection?
type Handler struct{
DB *DB
}
func main() {
fmt.Println("Hello, playground")
db, err := sql.Open("postgres", dbinfo)
defer db.Close()
h := &Handler{
DB: db,
}
log.Fatal(http.ListenAndServe(":8888", h))
}
func (h *Handler)ServeHTTP(w http.ResponseWriter, r *http.Request){
switch r.URL.Path{
case "/":
h.serveRoot(w, r)
case "/api/":
h.apiRouter(w, r)
}
}
func (h *Handler)serveRoot(w http.ResponseWriter, r *http.Request){
h.DB.DoSomethingWithDB()
}
func (h *Handler)apiRouter(w http.ResponseWriter, r *http.Request){
switch r.URL.Path{
case "/":
h.serveRoot(w, r)
case "/api/":
h.apiRouter(w, r)
}
}
Update
For reasons that aren't relevant to the question, I can't use DefaultServeMux
答案1
得分: 3
您的自定义处理程序结构、ServeMux、HandlerFunc和第三方路由器都是http.Handler
类型的。您可以根据需要进行组合和层叠。
由于ServeMux是一个Handler,您可以像任何其他处理程序一样将其分配给路径,并为各个处理程序注册单独的路径。以下是使用多个http.ServeMux
的示例(如果选择,可以在单独的包中定义)。这里有3个独立的处理程序(通过HandlerFunc
定义),通过2个ServeMux
进行路由。
在一个名为"router"的包中创建了一个ServeMux:
var Sub = http.NewServeMux()
func subHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("handled / in /sub")
w.Write([]byte("/sub/\n"))
}
func init() {
Sub.HandleFunc("/", subHandler)
}
现在我们可以从"router"导入ServeMux,并在顶级处理程序中使用它以及其他一些处理程序:
import "router"
func rootHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("handled /")
w.Write([]byte("/\n"))
}
func topHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("handled /top")
w.Write([]byte("/top/\n"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", rootHandler)
mux.HandleFunc("/top/", topHandler)
// 现在将Sub路由插入到"/top/sub/"下
mux.Handle("/top/sub/", http.StripPrefix("/top", router.Sub))
server := &http.Server{Addr: ":9999", Handler: mux}
log.Fatal(server.ListenAndServe())
}
您可以根据需要选择如何注册这些处理程序,可以像在main
包中导入和路由它们这样,也可以通过其他注册模式(例如database/sql
驱动程序)。
此外,还有许多第三方路由包可用,可以使此过程更加简单、高性能,或提供更高级的模式匹配方法。
英文:
Your custom handler struct, a ServeMux, a HandlerFunc, and 3rd party routers are all of type http.Handler
. You can compose and layer them as needed.
Since a ServeMux is a Handler, you can assign it to a path just like any other handler, and register separate paths for various handlers. Here's an example of using multiple http.ServeMux
(which could be defined in separate packages if you choose). This has 3 separate handlers (defined via a HandlerFunc
), routed over 2 ServeMux
.
Here we have a ServeMux create in a package "router"
var Sub = http.NewServeMux()
func subHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("handled / in /sub")
w.Write([]byte("/sub/\n"))
}
func init() {
Sub.HandleFunc("/", subHandler)
}
Now we can import the ServeMux from "router", and use it in our top level Handler, along with some others:
import "router"
func rootHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("handled /")
w.Write([]byte("/\n"))
}
func topHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("handled /top")
w.Write([]byte("/top/\n"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", rootHandler)
mux.HandleFunc("/top/", topHandler)
// now insert the Sub routes under "/top/sub/"
mux.Handle("/top/sub/", http.StripPrefix("/top", router.Sub))
server := &http.Server{Addr: ":9999", Handler: mux}
log.Fatal(server.ListenAndServe())
}
How you choose to register these is up to you, either via importing and routing them like this in the main
package, or via some other registration pattern (like e.g the database/sql
drivers).
There's also no shortage of 3rd party routing packages to make this easier, higher performance, or provide more advanced methods of pattern matching.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论