urlpath
urlpath
is a Golang library for matching paths against a template, or
constructing paths using a template. It's meant for applications that take in
REST-like URL paths, and need to validate and extract data from those paths.
This is easiest explained with an example:
import "github.com/ucarion/urlpath"
var getBookPath = urlpath.New("/shelves/:shelf/books/:book")
func main() {
inputPath := "/shelves/foo/books/bar"
match, ok := getBookPath.Match(inputPath)
if !ok {
return
}
fmt.Println(match.Params["shelf"])
fmt.Println(match.Params["book"])
}
One slightly fancier feature is support for trailing segments, like if you have
a path that ends with a filename. For example, a GitHub-like API might need to
deal with paths like:
/ucarion/urlpath/blob/master/src/foo/bar/baz.go
You can do this with a path that ends with "*". This works like:
path := urlpath.New("/:user/:repo/blob/:branch/*")
match, ok := path.Match("/ucarion/urlpath/blob/master/src/foo/bar/baz.go")
fmt.Println(match.Params["user"])
fmt.Println(match.Params["repo"])
fmt.Println(match.Params["branch"])
fmt.Println(match.Trailing)
Additionally, you can call Build
to construct a path from a template:
path := urlpath.New("/:user/:repo/blob/:branch/*")
res, ok := path.Build(urlpath.Match{
Params: map[string]string{
"user": "ucarion",
"repo": "urlpath",
"branch": "master",
},
Trailing: "src/foo/bar/baz.go",
})
fmt.Println(res)
How it works
urlpath
operates on the basis of "segments", which is basically the result of
splitting a path by slashes. When you call urlpath.New
, each of the segments
in the input is treated as either:
- A parameterized segment, like
:user
. All segments starting with :
are
considered parameterized. Any corresponding segment in the input (even the
empty string!) will be satisfactory, and will be sent to Params
in the
outputted Match
. For example, data corresponding to :user
would go in
Params["user"]
. - An exact-match segment, like
users
. Only segments exactly equal to users
will be satisfactory. - A "trailing" segment,
*
. This is only treated specially when it's the last
segment -- otherwise, it's just a usual exact-match segment. Any leftover data
in the input, after all previous segments were satisfied, goes into Trailing
in the outputted Match
.
Performance
Although performance wasn't the top priority for this library, urlpath
does
typically perform better than an equivalent regular expression. In other words,
this:
path := urlpath.New("/test/:foo/bar/:baz")
matches := path.Match(...)
Will usually perform better than this:
r := regexp.MustCompile("/test/(?P<foo>[^/]+)/bar/(?P<baz>[^/]+)")
matches := r.FindStringSubmatch(...)
The results of go test -benchmem -bench .
:
goos: darwin
goarch: amd64
pkg: github.com/ucarion/urlpath
BenchmarkMatch/without_trailing_segments/urlpath-8 1436247 819 ns/op 784 B/op 10 allocs/op
BenchmarkMatch/without_trailing_segments/regex-8 693924 1816 ns/op 338 B/op 10 allocs/op
BenchmarkMatch/with_trailing_segments/urlpath-8 1454750 818 ns/op 784 B/op 10 allocs/op
BenchmarkMatch/with_trailing_segments/regex-8 592644 2365 ns/op 225 B/op 8 allocs/op
Do your own benchmarking if performance matters a lot to you. See
BenchmarkMatch
in urlpath_test.go
for the code that gives these results.