我注意到,与通过ajax请求相比,我的手机(OnePlus 3,Android 8.0.0)上的Chrome(64.0.3282.137)在发送网页时发送的用户代理略有不同。

请求网页时发送此用户代理:
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
您能告诉我为什么该模型包含在 native 调用中,而不包含在ajax调用中吗?

最佳答案

我分析了 Chrome 源代码以获得一些见解。我在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(),该代码块负责根据可在此处找到的平台添加操作系统的信息。
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())不能在负责发送http请求的net模块中直接使用。

当我研究net(http)模块的代码时,我发现他们正在获取useragent *并通过一系列字符串操作和空格 trim 功能对其进行处理。 http_request_headers.cc中的AddHeadersFromString()是用于将useragent字符串添加到请求 header 的接口(interface)。

注意*:但是我认为 header 数据不是来自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应用程序的体系结构。最初加载页面时,值实际上是由chrome应用程序设置的(​​非常大的Java代码库,但是我认为我们需要查看的核心文件是LoadUrlParams.java),它具有发送http请求的不同实现(此处是用户代理不是由同一个net(http)模块 trim 的,而是由Java实现照顾的,这仅在第一次加载期间发生。但是其他所有后续调用都使用浏览器的net(http)模块。

文件引用链接:
https://cs.chromium.org/chromium/src/content/common/user_agent.cc?sq=package:chromium&dr=CSs&l=80

https://cs.chromium.org/chromium/src/net/http/http_request_headers.cc?type=cs&q=AddHeadersFromString&l=155

https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java?q=createLoadDataParamsWithBaseUrl&dr=CSs

我仅包含此答案,以提供可能发生问题的原因之一。如果我有更多时间,我会看看是否可以进行测试并证明这一点。
最后一点,这个答案并没有提供解决问题的任何解决方案。它只是给出了原因的原因。

[更新]

一种非常便宜的技巧是查看navigator.useragent是否具有oneplus值,并在请求上设置ajax header 并将其发送。这将覆盖浏览器添加用户代理 header 的机制。
XMLHttpRequest.setRequestHeader(header, value)

关于javascript - 请求网站时,Javascript用户代理(ajax)与发送的用户代理不同,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47904396/

10-11 20:21