-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net/url: add JoinPath, URL.JoinPath #47005
Comments
Change https://golang.org/cl/332209 mentions this issue: |
I actually just implemented something like this for my request client library: earthboundkid/requests@cbdbf91 I think a slightly better name is |
I also propose that it should be a method on URL, rather than package level function. // JoinPaths joins the provided path to any existing Path
// and cleans the result of any ./ or ../ elements.
func (u *URL) JoinPaths(paths ...string) {
if len(paths) == 0 {
return
}
paths = append([]string{url.Path}, paths...)
url.Path = path.Join(paths...)
return
} |
One complication: I think it's quite reasonable to expect that paths which begin with / will be interpreted as absolute (which is what URL.ResolveRef does), but that isn't what you implemented and might be surprising to users either way. |
Like this topic It continues to be paid attention to. Path.Join cannot be used directly. Generally, the string type is converted to the url type, then the error is checked, and then ResolveReference or path.join, and finally the complete url path is output. For ordinary users, this process is very repetitive |
I agree that this is probably worth doing on some level, but it's not totally clear what semantics users expect. For example, if we used ResolveReference as the standard, we get very different results: func Join(u *url.URL, path string) {
*u = *u.ResolveReference(&url.URL{Path: path})
}
func main() {
u, _ := url.Parse("http://example.com")
fmt.Println(u)
for _, p := range []string{"a/", "b/", "c", "d", "../e", "/f"} {
Join(u, p)
fmt.Println(u)
}
} Results:
|
Yes, using ResolveReference will indeed confuse, |
Here is a note on surprising behavior with Python’s url.parse: https://utcc.utoronto.ca/~cks/space/blog/python/UrllibParsePartialURLs
This could be a problem for url.Join as well. Either assuming that // is wrong and should be / or assuming it’s right could lead to surprising behavior depending on the context. |
If we do add this, it seems like Join should be restricted to manipulating the path, as in the top StackOverflow answer:
That is, we should not be doing anything with or like ResolveReference, which has to deal with full URL syntax as the second argument. Maybe we should make that clear by calling it url.JoinPath? So the API would be:
Do I have that right? |
This proposal has been added to the active column of the proposals project |
@rsc Thank you for your answer Does this API need to return a new object URL? Or modify the Path directly on the original object, which of these two methods is more suitable? func (u *URL) JoinPath(elem ...string) (*URL, error) 1func (u *URL) JoinPath(elem ...string) *URL{
url := URL{
Scheme : u.Scheme
Opaque : u.Opaque
User : u.User
Host : u.Host
Path : u.Path
RawPath : u.RawPath
ForceQuery : u.ForceQuery
RawQuery : u.RawQuery
Fragment : u.Fragment
RawFragment : u.RawFragment
}
if len(elem) > 0 {
elem = append([]string{u.Path}, elem...)
url.Path = path.Join(elem...)
}
return &url
}
func JoinPath(baseUrl string, elem ...string) (result string, err error) {
url, err := Parse(baseUrl)
if err != nil {
return
}
urlAfterJoin := url.JoinPath(elem...)
result = urlAfterJoin.String()
return
} 2func (u *URL) JoinPath(elem ...string) {
if len(elem) > 0 {
elem = append([]string{u.Path}, elem...)
u.Path = path.Join(elem...)
}
}
func JoinPath(baseUrl string, elem ...string) (result string, err error) {
url, err := Parse(baseUrl)
if err != nil {
return
}
url.JoinPath(elem...)
result = url.String()
return
} |
I don't believe we have any methods on URL today that modify the receiver (except Unmarshal). Any other objections to the API in #47005 (comment)? |
@rsc hi, I’m not sure if I really understand what you mean, I resubmitted the code, please help me review it again, thank you |
This is a cleaner implementation, but I'm not sure if package url can import path without causing a dependency cycle somewhere: func (u *URL) JoinPath(elem ...string) *URL{
u2 := *u
if len(elem) > 0 {
elem = append([]string{u.Path}, elem...)
u2.Path = path.Join(elem...)
}
return &u2
}
func JoinPath(baseUrl string, elem ...string) (result string, err error) {
u, err := Parse(baseUrl)
if err != nil {
return
}
return u.JoinPath(elem...).String()
} |
It should be OK for net/url to import path. path definitely doesn't import url. |
Based on the discussion above, this proposal seems like a likely accept. |
No change in consensus, so accepted. 🎉 |
concatenates baseUrl and the elements Fixes golang#47005 Change-Id: I71d638be9d451435dddf135091d8b1db54d174fd
Change https://golang.org/cl/374654 mentions this issue: |
concatenates baseUrl and the elements Fixes golang#47005 Change-Id: I71d638be9d451435dddf135091d8b1db54d174fd
Thank you for adding this, looks useful! Is it intended that the new method strips trailing This is not an important distinction for filesystem paths but sometimes makes an important difference for URLs, depending on the server, so I can imagine it surprising the user. The behavior isn't documented for JoinPath in the linked change. |
Hmm, it does seem like it should preserve the trailing slash, IMO. File a new issue so we can decide this before 1.19 ships. |
Done, thanks! #52074 |
Url Join is often used
Unsatisfactory using path.Join: http://www -> http:/www
I want to add Join method to url package
unit test
The text was updated successfully, but these errors were encountered: