千家信息网

(实战)通过WebElement.sendKeys()来研究wire协议

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,引入:其实熟悉selenium的人肯定都对wire协议不陌生,因为我们知道,当我们在代码中使用WebDriver API 做一些操作的时候,它最终会转为一个基于wire协议的命令(Command)发送
千家信息网最后更新 2026年11月01日(实战)通过WebElement.sendKeys()来研究wire协议

引入:

其实熟悉selenium的人肯定都对wire协议不陌生,因为我们知道,当我们在代码中使用WebDriver API 做一些操作的时候,它最终会转为一个基于wire协议的命令(Command)发送到浏览器,并且请求的内容都封装在json对象中,通过WebService调用浏览器,从而所有WebDriver API的调用都最后转为对浏览器的Web Service调用。

我们这里就通过最简单的输入文本内容(WebElement.sendKeys(String))来研究下wire协议。以及上述所有细节。


Wire 协议的参考规范如下:

http://code.google.com/p/selenium/wiki/JsonWireProtocol

粗略看了下,这个wire协议可强大了,几乎可以操作自然人对浏览器能做的任何事情,比如打开,关闭,点击,关闭,定位,上传文件,最大最小化等等。

它是一套基于RESTful风格的web service.


调试实战:

比如说页面上有个输入框id叫那么叫 policy-name ,然后我们要输入的值在dataProvider对象中,那么自动化测试代码是:


sendKeys()方法如下:


当调用sendKeys方法的时候,它会吧我们要输入的内容值转为一个字符数组,这个很好理解,因为任何字符串都是一组键盘的输入的集合。所以我们的policyName的值被转为:


然后,它去在87行调用execute()方法,并且使用了Command设计模式,把我们的调用sendKeys(String) 方法名转为了一个命令DriverCommand.SEND_KEYS_TO_ELEMENT(也就是字符串 "sendKeysToElement"),然后把我们要发送的内容keysToSend变量通过ImmutableMap进行打包, 这样做的目的是为了让我们的输入的内容不可改变。


因为在我们例子中,我们使用的是Linux操作系统上的Firefox所以,它会调用FirefoxWebElement上的execute()方法,并且我们的输入内容中被ImmutableMap包装后加上了WebElement的id。


然后它接着会调用父类的execute()方法来完成这个操作:

这是最重要的方法,我们仔细分析:


从宏观上看,首先,在第436行会吧当前WebDriver到它启动的浏览器的所创建的session对应的sessionId,以及命令字符串(也就是上文中传递过来的DriverCommand.SEND_KEYS_TO_ELEMENT字符串常量),还有发送的字符串内容的包装体,都封装在一个Command对象中,封装后这个Command对象如下:


值得一提的是:这个sessionId是WebDriver每次启动浏览器时候分配的唯一的会话id,从而保证多线程并行运行时候不会出现问题,而总是吧请求发送到正确的浏览器所包含的webservice中。(关于这一点,我们在精华分析1中会讲到)


然后,它会在446行用CommandExecutor的execute()方法来执行Command从而吧命令发送到sessionId指定的浏览器内含的web service服务中,最终它使用HttpCommandExecutor来完成这个任务

(执行命令的细节,是我们所探索的最主要目的,它反映了基于wire协议的web service调用,这点我们在精华分析2中讲到)


最后,在第455-456行对于执行结果的返回进行一些后处理,于是你就可以在页面上看到自动化测试的动画了。



精华分析1:sessionId是如何产生的?

因为我们使用的是Firefox浏览器做的测试(其他浏览器也一样),当WebDriver启动浏览器的时候,它会调用webDriver = new FirefoxDriver(firefoxBinary,firefoxProfile)方法,

因为FirefoxDriver继承自RemoteWebDriver,所以调用FirefoxDriver构造器时候会调用RemoteWebDriver的构造器,其最后一行会调用startSession()方法如下:

而startSession会在开始就用Command模式,调用execute()方法创建一个新的session:

而这个execute方法,最终被HttpCommandExecutor来执行,和前面叙述一样,它会发送一个Http请求,并且所有请求细节都在Command对象中。可以从下面调试信息看到,Command是如下的信息:

它的命令name是newSession,而sessionId为空,因为还没有创建嘛。


然后info对象中包含了要发送的请求url,这里可以看出,它发送到的请求url是/session


最后从httpMethod对象中,可以看出httpMethod用的是HttpPost


所以联系以上的信息就知道,在RemoteWebDriver中,其实它是以HttpPost方法发送了一个请求对象到/session中,并且请求对象中包含了命令"newSession"还有一些desiredCapabilities信息。

我们对比wire 协议:

正如协议中描述的,这个请求是用来创建一个新的session的,我们检查参数,请求类型,请求payload完全一致。


所以最后会发送此请求,发送完的response中会包含新创建的sessionId.

然后这个sessionId就可以作为每次发送请求到的目标浏览器的标识,从而保证每次请求的都是正确的浏览器了,当然,这个sessionId就必须被包含在每次请求中。


精华分析2:HttpCommandExecutor执行命令的细节.


我们看下HttpCommandExecutor.execute()方法:


首先,它会在第279行吧command的名字(命令名,也就是我们的DriverCommand.SEND_KEYS_TO_ELEMENT)转为一个url形式的命令。回想,我们用的是REST ,所以命令也要用路径表达式的方式表现出来,转换后,CommandInfo如下:


所以sendKeysToElement 命令被转为POST /session/:sessionId/element/:id/value的url形式。


我们对比Wire协议的说明:

所以,这里我们转换对了,的确我们sendKeysToELement的最终目的是发送一组键盘敲击动作序列到指定元素。


然后,它在第281行从刚才的CommandInfo对象中分离出Http动作:


并且这个getMethod方法内部还会吧我们的url中由名字参数(:sessionId),(:id)表示的url全部替换为真实值,并且前面拼接上由remoteServer实例变量指定的服务器请求url

因为从调试信息上看,info中的动词(verb)的名字叫"POST",所以它最终会被转为httpMethod为HttpPost。而这个uri被变量具体化后被转为:

这里可以看出(:sessionId),(:id)都被替换了,其中sessionId来自于浏览器的sessionId,具体可参见精华分析1.


然后在283行吧HttpPost设置为Http Accept头。


接着根据不同的HttpMethod进行不同的处理,因为我们的请求是httpPost 请求,所以它会在第286行利用BeanToJsonConverter()吧我们封装在Command对象中的内容转成json格式的payload ,并且接下来设置payload的编码格式以及Content-Type内容。


转换之后的json变为:


最后,在297行通过调用fallbackExecute来发送浏览器中,从这里可以看出,这个的确是一个RESTful的Web Service调用。


当处理完之后,其结果封装在HttpResponse对象中,我们要对它进行后处理,从调试信息看,这个Response是一个标准的HttpResonse


我们发现了一个很有趣的东西,这里发现这个server是httpd.js,这就说明,其实真正消费我们Http请求的是浏览器内置的一段httpd.js的脚本,这也和我们理论模型(浏览器包含了一段js来专门处理基于wire协议的请求)完全符合,可以猜想这段js就是模拟输入值到输入框中的动画。

我在selenium官网找到了这个js文件,其内容在:http://code.google.com/p/selenium/source/browse/firefox/src/extension/components/httpd.js?spec=svn004f447f8b359859da694f79569d7e5b03470dd7&r=004f447f8b359859da694f79569d7e5b03470dd7


当我们拿到Response对象后,我们要进行后处理,我们后处理不感兴趣,就不分析了。



总结:

从这里我们可以获取许多有用的信息

(1)从架构的角度来看,当我们用WebDriver API 调用来书写自动化测试的代码时候,最终这些方法调用都会被selenium框架内部转为一个基于wire协议的web service调用。采用的设计模式是Command模式,这个web service的服务端是在任何浏览器中都包含的,并且用于服务的其实是httpd.js这段代码。

(2)wire协议几乎可以模拟自然人对浏览器能做的任何事情,比如打开,关闭,点击,关闭,定位,上传文件,最大最小化等等。它是一套基于RESTful风格的web service.

(3)每次web service调用的时候,都必须有一个sessionId作为请求url一部分,这个sessionId用于唯一标识请求要送到的浏览器,并且是唯一的uuid,从而保证在多线程环境中工作的正确性。它的产生在于初始化浏览器的WebDriver时候,会发送一个Command为newSession的web service到浏览器中,这个请求路径是/session,并且payload中包含了目标浏览器的desireCapability信息,这样,这个web service的调用就会返回一个sessionId,然后包含在后续的所有操作中。

(4)在具体执行某个API 调用,比如sendKeys,它会转为web service的调用,调用的url会包含sessionId和其他一些相关信息,以名字参数的形式,而调用过程开始时,这些名字参数会被代替为实际参数。然后Command中封装的所有参数信息会被转为一个json对象作为web service的payload , 请求类型也会根据你的实际请求动作的需要指定,最后请求调用的过程就是一个web service调用的过程。它的服务端被各种浏览器实现,并且包含在一段叫httpd.js的代码中。




浏览器 浏览 方法 对象 命令 信息 内容 时候 输入 分析 处理 参数 字符 封装 代码 名字 字符串 精华 服务 模式 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技公司排名 科技赋能下的互联网金融 科技互联网政务服务 疫情对互联网科技公司影响 麻工互联网科技 成都互联网科技公司 互联网科技上市公司 互联网科技咨询 互联网养老 桔豆科技 深圳卡尔迅互联网科技有限公司怎么样 互联网科技项目计划书材料类 黑科技闪耀互联网之光 阿大互联网科技有限公司 移动互联网年度科技创新 注册互联网科技公司经营范围大全 坤平互联网科技 黑云互联网科技有限公司 山东大美朱楼互联网科技有限公司 北京军民互联网科技公司 广西泛欧互联网科技有限公司 联恩商钥互联网科技有限公司 成都仁贤领冠互联网科技有限公司 博兴的互联网电子科技公司 互联网科技实习生岗位职责 每天都有哪些好的互联网科技 珠海东科互联网科技有限公司 吉林省码上互联网科技电话 长沙易易互联网科技有限公司 华人运通互联网科技公司 上海旺乐尼互联网科技有限公司
0