常见则不疑:URL

要是说哪个Web开发者不知道URL,可以说是天方夜谭了。但是要是问哪位详细的了解过URL,可能真就剩下寥寥数人了。

老张实际工作中发现有些同事真的从来没有去主动了解过URL。URL历史悠久,URL应用广泛,URL形式多样且标准宽泛,URL熟悉且陌生。

今天老张把URL的讲解放在《Web开发进阶》系列的第一篇,给大家介绍一下URL。

01

URI:URL和URN

我们常说的 URL Uniform Resource Locator,统一资源定位符 其实是URI Uniform Resource Identifier,统一资源标识符 的子集 。除了URL, URI还有另一种形式——URN Uniform Resource Name,统一资源名

通过 URI, 客户端就可以 指定 他们想要获取的 互联网资源 ,但是URL和URN的本质是有区别的。

  • URL描述了特定服务器上某资源的特定位置。

  • URN与特定的服务器无关,仅需通过资源名即可定位并访问资源。

比起URN,URL更为我们所熟知。 作为一个开发者可以很容易的分辨出下面三个URL指向不同的资源位置

http://www.example.com:8080/1.html

http://www.example.com/1.html

http://www.example.com/2.htm

习惯了URL,可能很多人都不知道URN的存在,更好奇为什么URN不需要指定服务器位置。 其实, 下载用的 磁力链接就是URN,下面的示例应该能够很好帮助理解:

<span><span>magnet:?xt=urn:btih:EP3XFJ7BOAFA6GFJTNZKRQ6CIN7A5AB5</span></span>

只需要有了这段神秘代码,我们就可以下载相应的资源,而不需要关心资源实际存在于互联网的哪个角落。

02

URL的常见形式

除了HTTP之外,还有多种多样的协议也使用URL(比如FTP)来定位资源。大多数协议使用的URL格式都可以满足以下格式:

<span>&lt;scheme&gt;<span>://&lt;user&gt;</span><span>:&lt;password&gt;</span>@&lt;host&gt;<span>:&lt;post&gt;/&lt;path&gt;</span>;&lt;params&gt;?&lt;query&gt;#&lt;frag&gt;</span>

  • scheme: 协议名。指明客户端访问服务器时使用的协议类型,常见的有HTTP、HTTPS、FTP、mailto以及telnet等。

  • user: 用户名。常见于FTP协议。

  • password: 密码。和user一起用于鉴权。

  • host: 主机地址。可以是ip,也可以域名。

  • port: 端口。缺省时使用默认值,不同协议的默认值有所区别。

  • path: 路径。一般来说符合UNIX文件路径规范。

  • params: 参数。多个参数之间同样使用 ”:” 分割。

  • query: 查询参数。不同协议之间其形式可能有所区别。

  • frag: 片段。主要用于客户端。

以上是URL通用形式的介绍,几乎囊括了请求互联网资源所需要的所有信息。具体到HTTP,其格式就要简单许多。

03

HTTP协议的URL形式

RCF1945 《超文本传输协议——HTTP/1.0》 )给出了HTTP_URL的标准形式:

http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]

其中port是可以省略的,如果省略,则使用默认值80。并且该文档指明了协议实现需要支持ip形式的host,见过带有IPV4地址的URL,不知道大家见过带有IPV6地址的URL吗?

注意,虽然标准描述并没有提到frag,但是实际各浏览器都是支持锚点的,甚至有的还支持user。

04

实现一个URL解析和组装函数

文本形式的URL虽然扩展性很强,但是同HTML一样,其对机器的友好性却远不如二进制形式,加上RFC属于规范,本身并不包含强制性,所以HTTP_URL具体实现之间会有所差别。

综上,司空见惯的URL解析起来就显得没那么简单了。老张在这里用Python实现了一个玩具版的URL解析和组装函数,仅用于帮助大家理解本篇文章,请勿用于实际开发。

"""

@auther: zhang3

"""

__all__ = ["parse_http_url", "unparse_http_url"]



def unparse_http_url(scheme, host, port=80, path="", query="", frag=""):

"""

根据入参拼接http_URL

"""

if scheme.lower() != "http":

raise ValueError("only support http scheme")

url = "%s://" % scheme


if not host:

raise ValueError("host is needed")

url += host


if not port or port in [80, "80"]:

pass

else:

url += ":%s" % port


if path:

if not path.startswith("/"):

raise ValueError("illegal path")

url += path


if query:

url += "?" + query

if frag:

url += "#" + frag


return url



def parse_http_url(url):

"""

将url解析为 http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query [ "#" frag ]]]

仅支持http协议格式


>>> parse_http_url("http://www.example.com:8080/1.html")

('http', 'www.example.com', 8080, '/1.html', '', '')


>>> parse_http_url("http://www.example.com/1.html?name=zhang3")

('http', 'www.example.com', 80, '/1.html', 'name=zhang3', '')


>>> parse_http_url("http://www.example.com/2.html#anchor")

('http', 'www.example.com', 80, '/2.html', '', 'anchor')

"""

if not url.lower().startswith("http://"):

raise ValueError("scheme must be http")

scheme, url = url.split("://")

loc, url = _split_loc(url)

host, port = _split_host_port(loc)

query = frag = ""

if "#" in url:

url, frag = url.split("#")


if "?" in url:

url, query = url.split("?")


path = url


return scheme, host, port, path, query, frag




def _split_loc(url):

delim_index = len(url)

for delim in "/?#":

i = url.find(delim)

if i >= 0:

delim_index = min(i, delim_index)

return url[:delim_index], url[delim_index:]



def _split_host_port(loc):

host, port_ = "", ""

if loc.startswith("["):

i = loc.find("]")

if i < 0:

raise ValueError("illegal IPV6 host")

host, port_ = loc[:i+1], loc[i+2:]

elif ":" in loc:

host, port_ = loc.split(":")

else:

host = loc


if port_:

if not port_.isdigit():

raise ValueError("illegal port")

port = int(port_)

else:

port = 80


return host, port



if __name__=='__main__':

import doctest

doctest.testmod()


在命令行执行几条测试命令,效果如下:

>>>url = "http://www.example.com/1.html?name=zhang3"
 
>>>parse_http_url(url)
('http', 'www.example.com', 80, '/1.html', 'name=zhang3', '')
 
>>>unparse_http_url(*parse_http_url(url))
'http://www.example.com/1.html?name=zhang3'
 

备周则意怠,常见则不疑。

——《三十六计 · 瞒天过海》

在看点这里

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章