我注意到我的手机上的Chrome(64.0.3282.137)(OnePlus 3,Android 8.0.0)在请求网页时发送的用户代理略有不同,与通过ajax请求相反。
请求网页时会发送此用户代理:
Mozilla/5.0 (Linux; Android 8.0.0; ONEPLUS A3003 Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36
发送此用户代理进行ajax调用,并在调用navigator.userAgent
时返回:
Mozilla/5.0 (Linux; Android 8.0.0; Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36
差异:ONEPLUS A3003
你能告诉我为什么模型包含在本机调用中,而不是在ajax调用中吗?
其他信息:使用"请求桌面版网站" -feature已启用,两种情况下用户代理均为
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Safari/537.36
。
答案 0 :(得分:6)
我分析了铬源代码以获得一些见解。我能用c ++中的新手能力达到某种程度。
在此代码块中检测到客户端或平台的用户代理(文件:useragent.cc)。
std::string BuildUserAgentFromProduct(const std::string& product) {
std::string os_info;
base::StringAppendF(
&os_info,
"%s%s",
getUserAgentPlatform().c_str(),
BuildOSCpuInfo().c_str());
return BuildUserAgentFromOSAndProduct(os_info, product);
}
你可以在代码块中看到BuildOSCpuInfo(),它负责根据可以在这里找到的平台添加os实际信息
std::string android_build_codename = base::SysInfo::GetAndroidBuildCodename();
std::string android_device_name = base::SysInfo::HardwareModelName(); // this line in particular adds the ONEPLUS A3003
但是这个函数(BuildUserAgentFromProduct())不是直接在net模块中使用,它负责发送http请求。
当我调查net(http)模块的代码时,我看到他们正在获得useragent *并通过一系列字符串操作和空白区域修剪功能来处理它。 http_request_headers.cc中的AddHeadersFromString()是将useragent字符串添加到请求标头的接口。
注意*:但我认为标题数据不是来自useragent.cc,因为我无法在任何地方找到此函数的调用。但我可能在这里错了。
**我相信这是OSInfo的价值被修改的地方。任何无法识别的空格字符或最初预期的错误格式都可以提供此结果。
注意**:我无法测试上述语句并证明它,因为Chromium中使用的String在StringPiece的名称中有一个包装器(* wrapper只是我正在使用的术语,技术上它可以用我不知道的不同方式调用。)我不知道如何用C ++编写StringPiece的代码。
下面给出了一个如何出错的非常简单的例子。
int main()
{
std::string s = " ONEPLUS\rA3003\rBuild/OPR6.170623.013";
std::string delimiter = "\r\n"; //this is the delimeter used in chromium source code.
std::string token = s.substr(0, s.find(delimiter,0));
std::cout << token << std::endl;
return 0;
}
https://www.onlinegdb.com/SkTrbFJDz
为什么初始用户代理字符串具有该值并且后续http请求没有该值的原因在于android中的chrome app的体系结构。当最初加载页面时,值实际上是由chrome应用程序设置的(一个非常大的java代码库但我认为我们需要看到的核心文件是LoadUrlParams.java),它具有发送http请求的不同实现(这里useragent不是由相同的net(http)模块修剪而是由Java实现来处理,这只在第一次加载时发生。但任何其他后续调用都使用浏览器的net(http)模块。
文件参考链接: https://cs.chromium.org/chromium/src/content/common/user_agent.cc?sq=package:chromium&dr=CSs&l=80
我只是将这个答案包括在内,说明问题可能发生的原因之一。如果我有更多的时间,我会看到我是否能以某种方式进行测试并证明这一点。 最后一点注意这个答案没有给出解决问题的任何解决方案。它只是说明了原因。
[更新]
一个非常便宜的技巧是查看navigator.useragent是否具有oneplus值并在请求上设置ajax标头并发送它。这将覆盖浏览器添加用户代理标头的机制。
XMLHttpRequest.setRequestHeader(header, value)
答案 1 :(得分:0)
在第一个userAgent中,浏览器通过在发出请求之前修改userAgent将设备识别为移动设备;因此ONEPLUS A3003
。但是在第二种情况下,由于w3规范(Find it here),你无法修改userAgent;因此遗漏了ONEPLUS A3003
。
当您使用“请求桌面版网站”功能时,浏览器无需修改userAgent,因此您将获得相同的userAgent。
注意:该Chrome浏览器的默认userAgent为:
Mozilla/5.0 (Linux; Android 8.0.0; Build/OPR6.170623.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36