# URI schema
# 什么是 URI Scheme?
URI Scheme 是统一资源标识符(Uniform Resource Identifier (opens new window))的命名结构,说白了,就是定义一个资源的。 但是,这个资源是一个宽泛的概念,并不一定是我们所说的 web 资源,它可以是你本机的一个文件,也可以是网络上的视频,等等。 因此,我们有必要从一些常规的误区分辨出来,以加深我们的理解:
- 认为 URI 就是 URL。
实际上,从其名称上来看,URI(Uniform Resource Identifier) 和 URL(Uniform Resource Locator)两者名称都不一样,所以必然有区别,前者是统一资源标识符,后者是统一资源定位符,后者是网络上用于定位互联网上 Web 资源的,如 HTML 文档、图像、视频片段、程序等。
它是一个通用定义,不是“protocols”,也不是 URI protocols 或者 URL protocols。
它经常用于设计特殊的协议。如 http scheme(HTTP 协议),XML namespaces,文件标示等等。
从上面的一些结论来看,URI Scheme 实际上一个概念性的东西,是一个规范,所以符合它的规范的都可以称之为 URI Scheme,当然,我们也可以设计我们自己的 scheme,用来实现我们特殊的目的。它一般具有如下的形式:
<scheme name> : <hierarchical part> [ ? <query> ] [ # <fragment> ]
像:
- https://www.baidu.com
- file:///D:/软件/WXWork/WXWork.exe
- git://github.com/user/project-name.git
- ftp://user1:1234@地址
- ed2k://|file|%5BMA%89%|4096933888|2c55b63e88e|h=ltcxuvnxfchjkwxa|/
这些都是一个 URI Scheme。
其中:
<scheme name>
:很明显,这是 scheme 的名称,对于上面五个 scheme,它们 scheme 名分别是 https, file, git, ftp, ed2k(电驴协议),实际上,它们也代表着协议名称。<hierarchical part>
:实际上,一般情况,它包含 authority 和 path。<query>
:可选项目,一般使用;隔开或&隔开的键值对<key>
=<value>
<fragment>
:可选项目包,其它额外的标识信息
# URI Scheme 分析
具体的例子可能更好的说明问题,假设有如下两个 URI Scheme:
- foo://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose
- urn:example:animal:ferret:nose
它们的分析结果如下:
看懂了这个图,应该就可以明白 URI Scheme 到底是什么玩意了。
# Registering an Application to a URI Scheme - 向 URI Scheme 注册应用程序
微软官方文档 (opens new window) 苹果系统也有对这种 URI Scheme 的支持
桌面客户端通过向操作系统注册私有自定义协议的方式,可以在 web 页面中以 url 的方式调起桌面客户端。 因此只要知道系统某个应用程序在注册表中的名称即可利用 URI Scheme 打开该程序。 使用形式:(应用程序在注册表中的名称:[//参数])
# 注册表
电脑中 注册表 (opens new window) 被称为 Windows 操作系统的核心,它的工作原理实质是一个庞大的数据库,存放了关于计算机硬件的配置信息、系统和应用软件的初始化信息、应用软件和文档文件的关联关系、硬件设备的说明以及各种状态信息和数据,包括 Windows 操作时不断引用的信息。 例如:系统中的硬件资源、硬件信息、分配正在使用的端口、每个用户的配置文件、计算机上安装的应用程序以及每个应用程序可以创建的文件类型等。
主要作用(加粗的为本文关注点):
- 记录安装信息
- 设置硬件
- 设置软件
- 定制 Windows
- 系统安全管理
- 自动运行程序
- 网络设置
# 分支结构
注册表有五个一级分支,下面是这五个分支的名称及作用:
名称 | 作用 |
---|---|
HKEY_CLASSES_ROOT | 存储 Windows 可识别的文件类型的详细列表,以及相关联的程序。 |
HKEY_CURRENT_USER | 存储当前用户设置的信息。 |
HKEY_LOCAL_MACHINE | 包括安装在计算机上的硬件和软件的信息。 |
HKEY_USERS | 包含使用计算机的用户的信息。 |
HKEY_CURRENT_CONFIG | 这个分支包含计算机当前的硬件配置信息。 |
# 数据结构
注册表的组织方式跟文件目录比较相似,主要分为根键、子键和键值项三部分,与文件目录对应的话就是根目录、子目录和文件。分别介绍一下这三部分:
- 根键。分为 5 个,分别为 HKEY_CLASSES_ROOT, HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE,HKEY_USERS 和 HKEY_CURRENT_CONFIG,把它们理解成磁盘的五个分区可以了。
- 子键。可以有多个子键和键值项,就像一个目录中可以有多个子目录和多个文件一样。
- 键值项。可以理解为文件,它由三部分组成,分别为 :名称、类型、数据。
在注册表编辑器(Regedit.exe)中,数据结构显示如下,其中,command 键是 open 键的子键,(默认)表示该值是默认值,值名称为空,其数据类型为 REG_SZ,数据值为%systemroot%/system32/NOTEPAD.EXE "%1
下面的.reg 注册表文件执行后即可创建一个名为 test 的文件夹到 HKEY_CLASSES_ROOT 目录下,即注册了一个应用软件到系统注册表中。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\test]
"URL Protocol"="C:\WINDOWS\system32\calc.exe"
@="calc"
[HKEY_CLASSES_ROOT\test\DefaultIcon]
@="C:\WINDOWS\system32\calc.exe,1"
[HKEY_CLASSES_ROOT\test\shell]
[HKEY_CLASSES_ROOT\test\shell\open]
[HKEY_CLASSES_ROOT\test\shell\open\command]
@="\"C:\WINDOWS\system32\calc.exe\" \"%1\""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%1 表示文件自身,%2 表示默认打印机,%3 表示驱动器,%4 表示端口 @=""C:\WINDOWS\system32\calc.exe" "%1"" 表示将该文件名作为参数传递给 calc.exe 程序
# 自定义协议检测
现在我们已经知道通过 URI Scheme 打开系统中应用程序的原理。
但是还有一个小小的问题,如果系统注册表中没有目标程序或者说没有安装目标应用程序,调用 URI Scheme 将会在浏览器控制台看到错误信息:
Failed to launch 'xxxxxxxxx' because the scheme does not have a registered handler.
提示告诉我们启动 XXX 失败,因为 scheme 没有注册的处理程序。
如果真的没有应用程序报错是理所当然的,但是这会给用户一个错觉,觉得自己刚刚执行的操作是不是没有生效,于是在多次尝试后便会气急败坏,殊不知控制台一直在报出错误信息,但是这其实是开发者的失职,应当给用户提示明显的错误信息。
现在要研究如何检测自定义的协议是否存在的问题,ie 下可通过 ActiveX (opens new window) 检测注册表项,Chrome 该怎么办呢? 这个错误无法用常规 try catch 方式捕获到,有没有通用的解决办法呢?我在各种搜索后得到了一种解决方案: 有人已经将该功能封装成了通用 JS:custom-protocol-detection (opens new window)
它暴露一个自定义协议检测函数,传入 URI schema 及回调函数即可兼容大部分浏览器判断 URI 是否打开成功并通过回调函数返回结果,当然,如果失败控制台依旧会抛出错误信息,但是我们已经能通过失败回调函数来执行打开失败后要做的事情了,反手给用户就是一个错误弹框。
阅读源码后可知,实现原理应对不通浏览器大概有四种:
- 利用 Navigator 对象的 msLaunchUri (opens new window) 方法
- 在新窗口(iframe)中打开 URI
- 利用隐藏的 iframe 打开 URI
- 利用鼠标失焦事件判断:当软件存在,点击链接会打开软件,鼠标跳出。当 1s 内鼠标仍未跳出,说明软件不存在。
到这里使用 URI schema 通过系统注册表打开应用程序并检测应用程序是否安装一整套原理及流程就走通啦~!
# URI schema 传参
有时候我们想在启动应用程序的时候自动执行一些操作,比如通过 URI schema 打开百度网盘或迅雷可立即执行下载操作,打开 QQ 或者企业微信时定位到某个聊天对象,这个时候就要用到 URI schema 中的 query 了。
某些应用程序在进行编写时会预留给可执行 exe 文件某些特殊的参数,传入这些参数后便可在执行 exe 文件后执行预留的软件行为:
百度网盘: 链接:https://pan.baidu.com/s/1C9GiT4thhlCxgzSJBMWdcA (opens new window) 提取码:1121 (点击下载弹出打开百度网盘客户端对话框后在控制台 Network 中可找到 baiduyunguanjia://evoked-download/?browserId=9436efa061fe4a4fad7a3182462b4131d1e3832a78f8a2db&seq=16365099693275)