为什么Swift的URL的init?(string: String, relativeTo: URL?)只添加协议?

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

Why does Swift URL's init?(string: String, relativeTo: URL?) add only the protocol?

问题

当我执行以下代码时:

let baseUrl = URL(string: "ftp://www.foobar.com/foo/bar/")
let finalUrl = URL(string: "//barfoo.com/bar/foo", relativeTo: baseUrl)
print(finalUrl?.absoluteString ?? "ooops")

它会打印出 ftp://barfoo.com/bar/foo

这是预期的行为吗?这个行为在哪里有记录?

[编辑]
我只是好奇,因为我期望基本URL中的所有内容,包括服务器和路径,都会被使用,就像这样:

let baseUrl = URL(string: "ftp://www.foobar.com/foo/bar/")
let finalUrl = URL(string: "boo/far", relativeTo: baseUrl)
print(finalUrl?.absoluteString ?? "ooops")

这会打印出 ftp://www.foobar.com/foo/bar/boo/far

为什么在第一个示例中忽略了基本URL中的服务器和其他内容?这是因为开头的 // 吗?这是某个RFC的一部分还是在其他地方有记录的?

英文:

When I do

let baseUrl = URL(string: "ftp://www.foobar.com/foo/bar/")
let finalUrl = URL(string: "//barfoo.com/bar/foo", relativeTo: baseUrl)
print(finalUrl?.absoluteString ?? "ooops")

it prints ftp://barfoo.com/bar/foo

Is this expected behaviour? Is this documented somewhere?

[EDIT]
I was just wondering, because I'd expect that all the stuff, including server and path would be taken from the base url, like here:

let baseUrl = URL(string: "ftp://www.foobar.com/foo/bar/")
let finalUrl = URL(string: "boo/far", relativeTo: baseUrl)
print(finalUrl?.absoluteString ?? "ooops")

which prints ftp://www.foobar.com/foo/bar/boo/far.

Why is the server and everything else from the base url being ignored in the first example? Is that due to the // at the beginning? And is this part of some RFC or something or documented somewhere else?

答案1

得分: 3

正如你所指出的,这是由于开头的//。根据URI RFC中的规定,URI的格式如下:

  URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
  hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

当你以//开头时,表示你要替换掉授权和路径,而它也确实这样做了。正如第3.3节所描述的,path-absolute不能以//开头:

                / path-absolute   ; 以"/"开头但不以"//"开头

如果你的意图是替换路径而不修改授权(用户信息、主机、端口),那么你可以传递一个绝对路径(以/开头),而不是授权和路径(以//开头):

let finalUrl = URL(string: "/barfoo.com/bar/foo", relativeTo: baseUrl)
// ftp://www.foobar.com/barfoo.com/bar/foo

如果你想要追加一个路径,你可以传递一个无根路径(不以/开头):

let finalUrl = URL(string: "barfoo.com/bar/foo", relativeTo: baseUrl)
// ftp://www.foobar.com/foo/barfoo.com/bar/foo

对于比这更复杂的操作,你应该考虑使用URLComponents,它允许你更明确、更安全地查询和修改各个部分。

英文:

As you note, this is due to the // at the beginning. From the URI RFC, the format of a URI is:

  URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
  hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

When you begin your URI with //, you're indicating that it should replace the authority and path, which it does. As described in Section 3.3, a path-absolute cannot begin with //:

                / path-absolute   ; begins with "/" but not "//"

If your intention is to replace the path without modifying the authority (userinfo, host, port), then you can pass an absolute path (beginning with /) rather than an authority and path (beginning with //):

let finalUrl = URL(string: "/barfoo.com/bar/foo", relativeTo: baseUrl)
// ftp://www.foobar.com/barfoo.com/bar/foo

And if you want to append a path, you can pass a rootless path (not beginning with a /):

let finalUrl = URL(string: "barfoo.com/bar/foo", relativeTo: baseUrl)
// ftp://www.foobar.com/foo/barfoo.com/bar/foo

For operations more complex that this, you should definitely look at URLComponents instead, which allows you to query and modify parts more explicitly and safely.

huangapple
  • 本文由 发表于 2023年8月8日 22:06:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76860343.html
匿名

发表评论

匿名网友

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

确定