Golang在路由之前修改HTTP请求参数,例如URL路径

huangapple go评论79阅读模式
英文:

Golang Modify HTTP Request Parameters Such As URL Path Before Routing

问题

在某些情况下,将纯URI作为路径的后缀传递而不是查询参数是一种常见做法。以下是来自Internet Archive的Wayback Machine的一个示例。

https://web.archive.org/web/20150825082012/http://example.com/

在这个示例中,用户请求一个在2015-08-25 08:20:12捕获的http://example.com/的副本。如果我们要在Go中实现类似的服务,可能会有一个如下的路由器:

http.HandleFunc("/web/", returnArchivedCopy)

然后在returnArchivedCopy处理函数中,我们将分割r.URL.Path(其中r是Request对象)以提取日期时间和目标URL。然而,在这种URL方案中存在一个问题;Go的net/http包在路径部分上调用cleanPath函数来对其进行清理。这个清理过程会执行各种清理任务,比如从路径中消除...,并将多个斜杠替换为一个斜杠。这个操作在Unix系统中是有意义的,因为文件路径中的///是相同的。然而,在上述描述的用例中,这会导致问题,因为http://example变成了http:/example,服务器内部会返回一个重定向响应给客户端,其中包含经过清理的路径。

我想知道,在这种情况下我有哪些选择?有没有办法告诉HTTP在仍然利用默认(或稍作修改的)服务器、多路复用器和处理程序的所有默认行为的情况下,不对请求路径进行清理?或者有没有办法在请求参数(在这种情况下是路径)到达多路复用器的路由模式之前修改它们?如果后者是可能的,我们可以尝试执行类似URL编码的操作,以避免重定向,然后在处理函数中解码URL以提取所需的部分。

我已经尝试了一些自定义处理程序和多路复用器,但我对Go还不太熟悉,因此不太确定如何在更改请求后将路由委托给默认处理程序。

英文:

It's common practice in some cases to pass plain URIs as suffix of the path instead of a query parameter. Here is an example from Internet Archive's Wayback Machine.

https://web.archive.org/web/20150825082012/http://example.com/

In this example, user is requesting a copy of http://example.com/ as captured at 2015-08-25 08:20:12. If we were to implement similar service in Go, we probably would have a router as follows:

http.HandleFunc("/web/", returnArchivedCopy)

Then in the returnArchivedCopy handler function, we will split r.URL.Path (where r is the Request object) to extract the date-time and the target URL. However there is a problem in this style of URL scheme; Go's net/http package calls cleanPath function on the path portion to sanitize it. This sanitization process does various cleanup tasks such as eeliminating . and .. from the path and replace multiple slashes with a single one. This later operation makes sense when because in Unix systems // in the file path are same as /. However this causes an issue in the above described use case as http://example becomes http:/example and the server internally returns a redirect response to the client with the sanitized path.

I am wondering, what are my options in this case? Is there a way to ask HTTP not to sanitize the request path while still utilizing all the default behavior that is shipped with the default (or slightly modified) server, multiplexer, and handler? Or is there a way to modify the request parameters (path in this case) before it hits the multiplexer's routing patterns. If the later is possible, we might try to perform something like URL encoding to avoid the redirect and later decode the URL back in the handler function before extracting desired bits.

I have experimented with some custom handlers and multiplexers, but I am new to Go, hence I was not quite sure how to delegate the routing back to the default handlers after making changes in the request.

答案1

得分: 5

你可以实现一个包装器mux,它会回退到默认的mux,这里有一个非常简单的示例:

func main() {
    http.HandleFunc("/blah", func(w http.ResponseWriter, req *http.Request) {
        w.Write([]byte("w00t"))
    })
    http.ListenAndServe(":9090", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        p := strings.SplitN(req.URL.RequestURI()[1:] /*trim the first slash*/, "/", 3)
        if len(p) != 3 || p[0] != "web" {
            http.DefaultServeMux.ServeHTTP(w, req)
            return
        }

        t, err := time.Parse("20060102150405", p[1])
        if err != nil {
            http.Error(w, "invalid time", 400)
            return
        }
        url := p[2]
        fmt.Fprintf(w, "requested url %v @ %v", url, t)
    }))
}

希望对你有帮助!

英文:

You can implement a wrapper mux, that falls back to the default one, here's a very simple example:

func main() {
	http.HandleFunc("/blah", func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte("w00t"))
	})
	http.ListenAndServe(":9090", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		p := strings.SplitN(req.URL.RequestURI()[1:] /*trim the first slash*/, "/", 3)
		if len(p) != 3 || p[0] != "web" {
			http.DefaultServeMux.ServeHTTP(w, req)
			return
		}

		t, err := time.Parse("20060102150405", p[1])
		if err != nil {
			http.Error(w, "invalid time", 400)
			return
		}
		url := p[2]
		fmt.Fprintf(w, "requested url %v @ %v", url, t)
	}))
}

huangapple
  • 本文由 发表于 2015年8月28日 07:59:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/32261308.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定