Reputation: 11978
I'm writing a REST API in Go using https://github.com/emicklei/go-restful
In my JSON output I want to output absolute path to other REST resources, but I don't know how I can obtain all the relevant parts to build the Path, such as transport (http/https), host, port, root path.
Do I need to keep track of this myself, do I fetch it from the Request
somehow?
Upvotes: 1
Views: 2523
Reputation: 36
After playing with this for a while, I ended up trying it both ways. It turns out that at least some of the information can be extracted from Request
(note: I presume that if the route were defined with an absolute url then all of this information could be had from Request.URL
)
package main
import (
"github.com/emicklei/go-restful"
"io"
"net/http"
)
func main() {
ws := new(restful.WebService)
ws.Route(ws.GET("/hello").To(hello))
restful.Add(ws)
http.ListenAndServe(":8080", nil)
}
func hello(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "Protocol: " + req.Request.Proto + "\nHost: " + req.Request.Host + "\nPath: " + req.Request.URL.Path)
}
The above prints the following to the browser. I suppose one could use this to construct urls to jsonify, although it's not clear how the base path would be parsed out without some prior knowledge of either the base path or the specific path, or some syntactic convention for distinguishing the two.
Protocol: HTTP/1.1
Host: localhost:8080
Path: /hello
The second thing I tried was to define a URL
at package level and use that to build a custom parse function. url.URL
maintains a struct representation of a URL; A relative (or absolute) path can be parsed and merged with an existing URL
using URL.parse(string)
. Conveniently, URL
can output a string representation of itself via it's String() method.
The custom parse function simply holds on to a copy of the package level URL
, and each time it is called with some specific path it pastes the new path on to the end of the URL.Path
and returns a new URL
that's identical except for the new concatenated path (because that's what URL.parse()
does when we hand it a relative path). URL.String()
can then be called on the new URL to stringify the URL
into the string that we want to marshal.
package main
import (
"github.com/emicklei/go-restful"
"io"
"net/http"
"net/url"
"fmt"
"encoding/json"
)
var myurl = url.URL{
Scheme:"http",
Host: "localhost:8080",
Path:"basepath",
}
var parse = getParseFn(myurl)
func main() {
ws := new(restful.WebService)
ws.Route(ws.GET("/jsonUrls").To(jsonUrls))
restful.Add(ws)
http.ListenAndServe(":8080", nil)
}
func jsonUrls(req *restful.Request, resp *restful.Response) {
urls := []string{}
for _, s := range []string{"get", "put", "whatever"} {
u, err := parse(s)
if err != nil {
fmt.Println(err)
}
urls = append(urls, u.String())
}
json_urls, err := json.Marshal(urls)
if err != nil {
fmt.Println(err)
}
io.WriteString(resp, string(json_urls))
}
func getParseFn (myUrl url.URL) func (string) (*url.URL, error) {
parse := func (s string) (*url.URL, error) {
u, err := myUrl.Parse(myUrl.Path + "/" + s)
return u, err
}
return parse
}
This prints the following in the browser:
["http://localhost:8080/basepath/get","http://localhost:8080/basepath/put","http://localhost:8080/basepath/whatever"]
Upvotes: 2