在JavaScript中模拟用户代理?


76

我正在寻找一种以编程方式实时更改navigator.userAgent的方法。在尝试获得自动化javascript单元测试器的失败尝试中,我放弃了尝试尝试使用fireunit。立即,我陷入了使用实际浏览器进行JavaScript测试的一堵墙。

具体来说,我需要更改navigator.userAgent以模拟数百个userAgent字符串,以确保对给定函数进行正确的检测和覆盖。navigator.userAgent是只读的,所以我似乎卡住了!如何模拟navigator.userAgent?用户代理切换器(插件)可以切换FF的用户代理,但是我可以在JavaScript中进行切换吗?


Answers:


115

尝试:

navigator.__defineGetter__('userAgent', function(){
    return 'foo' // customized user agent
});

navigator.userAgent; // 'foo'

在FF2和FF3中进行了尝试。


4
可以使用此解决方案设置iframe的用户代理吗?
曼德勒2012年

为我工作。(在Windows上为IE)
Tyler Long

3
__defineGetter__ 已弃用,请defineProperty 改用。在下面检查我的答案。
泰勒·朗


我最近创建了一个要点,以使只读属性可写(您可以找到一个示例来覆盖navigator.userAgent)。这很丑陋,不建议使用,但我在单元测试中使用了它,以便即时更改用户代理,因此请当心。要点:gist.github.com/moehlone/bed7dd6cb38fc55bd640
PhilippMöhler'16

25

添加到Crescent Fresh的解决方案后,重新定义navigator.userAgent吸气剂在Safari 5.0.5(在Windows 7和Mac OS X 10.6.7上)中似乎不起作用。

需要创建一个继承自该navigator对象的新对象,并定义一个新的userAgentgetter以将原始userAgentgetter隐藏在navigator

var __originalNavigator = navigator;
navigator = new Object();
navigator.__proto__ = __originalNavigator;
navigator.__defineGetter__('userAgent', function () { return 'Custom'; });

我无法覆盖window.navigator对象,因此您的解决方案对我不起作用。一个由“新月新鲜”做。
cburgmer

6
这也适用于phantomjs ...需要新对象。我猜这是一个webkit的东西。
皮克勒

在Safari版本12.1.2(14607.3.9)中不起作用。上的Javascript错误navigator.__proto__ = __originalNavigator;,它说:cyclic __proto__ value或诸如此类。删除该行使用户代理“在本地工作”,这意味着navigator.userAgent将取回您设置的内容,但是对于XMLHttpRequest调用,它将仅使用默认的UA。
强尼

24

以下解决方案适用于Chrome,Firefox,Safari,IE9 +以及iframe:

function setUserAgent(window, userAgent) {
    if (window.navigator.userAgent != userAgent) {
        var userAgentProp = { get: function () { return userAgent; } };
        try {
            Object.defineProperty(window.navigator, 'userAgent', userAgentProp);
        } catch (e) {
            window.navigator = Object.create(navigator, {
                userAgent: userAgentProp
            });
        }
    }
}

例子:

setUserAgent(window, 'new user agent');
setUserAgent(document.querySelector('iframe').contentWindow, 'new user agent');

我在Safari 8上进行了测试,但似乎无法正常工作。我使用console.log(navigator.userAgent),控制台记录默认的用户代理字符串。
王王航空

我认为可能是在脚本更改用户代理之前我有console.log。
王王航空

@AeroWindwalker我刚刚再次在Safari 8中对其进行了测试,并且可以正常工作。
乔尔·理查德

是的,我意识到该脚本有效。只是在脚本将新的用户代理应用到服务器之前,iframe已将默认用户代理发送到服务器。
王王航空

1
@AeroWang您如何解决这个问题?the iframe sent the default user agent to the server before the script applies a new user agent to it.
LIGHT 2015年

9

使用Object.defineProperty应该向混合添加更多浏览器:

if (navigator.__defineGetter__) {
    navigator.__defineGetter__("userAgent", function () { 
        return "ua"; 
    });
} else if (Object.defineProperty) { 
    Object.defineProperty(navigator, "userAgent", { 
        get: function () { 
            return "ua";
        }
    });
}

此代码应该工作(和测试)的火狐1.5 +Chrome浏览器6+歌剧10.5+IE9 +。不幸的是,任何平台上的Safari都不允许更改userAgent。

编辑:Safari不允许更改userAgent,但是可以替换整个导航器对象,如上面另一种解决方案所指出的那样。


嘿,Bundyo,在IE8中有什么方法可以做同样的事情吗?
Pratik Pattanayak 2015年

不幸的是我什么都不知道。
邦代

8

对于这里的用户,因为他们需要在单元测试中更改userAgent值,Tyler Long的解决方案有效,但是如果您要还原初始userAgent或对其进行多次更改,则可能需要将属性设置为可配置的:

function setUserAgent(userAgent) {
    Object.defineProperty(navigator, "userAgent", { 
        get: function () { 
            return userAgent; // customized user agent
        },
        configurable: true
    });
}

// Now in your setup phase:
// Keep the initial value
var initialUserAgent = navigator.userAgent;
setUserAgent('foo');

// In your tearDown:
// Restore the initial value
setUserAgent(initialUserAgent);

否则,您可能会遇到TypeError: Cannot redefine property错误。在Chrome Headless上为我工作。


1
经过反复试验,我得出了相同的结论,希望我先阅读此答案。
MDahlke

目前在Windows Google Chrome版本78.0.3904.108(正式版本)(64位)中对我不起作用
Ryan

谢谢,像魅力一样工作const setUserAgent = (userAgent: 'Chrome' | 'Android') => { Object.defineProperty(navigator, 'userAgent', { get: () => userAgent, configurable: true, }); };
Aamir Afridi

4

新月新鲜的答案是正确的。但是有一个问题:__defineGetter__已弃用:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/ defineGetter

不推荐使用 此功能已从Web标准中删除。尽管某些浏览器可能仍支持它,但是它正在被删除。不要在新旧项目中使用它。使用它的页面或Web应用程序可能随时中断。

您应该defineProperty改用:

Object.defineProperty(navigator, "userAgent", { 
    get: function () { 
        return "foo"; // customized user agent
    }
});

navigator.userAgent; // 'foo'

1
TypeError:尝试更改无法配置的属性的吸气剂。
uchuugaka

@uchuugaka,我根本没有测试Safari。抱歉
泰勒·

别担心。是的 最重要的是锁定安全性。这些都不起作用。
uchuugaka

我认为该解决方案在Firefox中不再可用,因为navigator.useragent属性是只读的。
Olivier de Broqueville,

4

要更新此线程,defineGetter在Jasmine中已不再使用,因为它已弃用。但是我发现这允许我修改茉莉中navigator.userAgent的吸气剂:

navigator = {
  get userAgent() {
    return 'agent';
  }
}

console.log(navigator.userAgent); // returns 'agent'

只要记住在茉莉花中完成测试后就重置导航器对象即可


使用__defineGetter__上6.1的NodeJS(使用茉莉花2)似乎工作很适合我。
Stephan Bijzitter '16

__defineGetter__ 在Jasmine 2.4.1和node 6.8.1上对我不起作用,但是使用它可以很好地工作。
karl 2016年

如何重置?
joacoleza

3

对于那些试图在TypeScript中做相同事情的人,这里是解决方案:

(<any>navigator)['__defineGetter__']('userAgent', function(){
    return 'foo';
});

navigator.userAgent; // 'foo'

还是语言相同:

(<any>navigator)['__defineGetter__']('language', function(){
    return 'de-DE';
});

2

我想我会采用依赖注入方法。代替:

function myFunction() {
    var userAgent = navigator.userAgent;
    // do stuff with userAgent
}

也许做类似的事情:

function myFunction(userAgent) {
    // do stuff with userAgent
}

function getUserAgent() {
    window.userAgentReal = +window.userAgentReal || 0;
    return [ navigator.userAgent ][window.userAgentReal++];
}

function getUserAgentMock() {
    window.nextUserAgentMock = +window.nextUserAgentMock || 0;
    return [
        'test user agent1',
        'test user agent2',
        'test user agent3'
    ][window.nextUserAgentMock++];
}

var userAgent;
while (userAgent = getUserAgent()) {
    myFunction(userAgent);
}

然后,您可以通过以下方式“模拟” getUserAgent()

function getUserAgentReal() { // formerly not 'Real'
    // ...
}

function getUserAgent() { // formerly 'Mock'
    // ...
}

这种设计仍不是完全自动化的(您必须手动重命名getter才能执行测试),并且给诸如操作上的简单操作增加了很多复杂性navigator.userAgent,而且我不确定您将如何实际识别任何内容。中的错误myFunction,但我只是想将它扔在那里,以便为您提供一些可能如何处理的想法。

也许这里提出的“依赖注入”的想法可以与FireUnit集成在一起。


当然,可以将navigator.userAgent设置为getUserAgent,然后在运行中重新定义getUserAgent。只要我的定义是最后的,模拟就成为事实。但是,它会使实际的javascript更大,更笨拙,更脏。因此,我尝试避免这种情况。
Stefan Kendall

1

上述答案不适用于PhantomJS + TypeScript。下面的代码为我工作:

var __originalNavigator = navigator;
(window as any).navigator = new Object();
navigator["__proto__"] = __originalNavigator["__proto__"];
navigator["__defineGetter__"]('userAgent', function () { return 'Custom'; });

帮助很大。最佳解决方案。谢谢您
-Dumbo

1

本主题较晚,但对于Karma + Jasmin和Typescript而言,想要设置userAgent属性,可以这样做:

describe('should validate YYYY-MM-dd format only on IE browser', () => {
    // this validator has a specific condition to work only in IE11 and down
    (window as any).navigator.__defineGetter__('userAgent', function () {
      return 'MSIE';
    });

...
// rest of the test

});

本文帮助:https : //www.codeproject.com/Tips/1036762/Mocking-userAgent-with-JavaScript


1

试试这个对我没有毛刺问题的工作

Object.defineProperty(global.navigator, 'userAgent', { get: () => 'iPhone' });

0

navigator.userAgent 是只读字符串属性,因此无法对其进行编辑


navigator.userAgent是一个吸气剂,而不是“只读字符串属性”。
Eli Gray

6
@Elijah Grey,从您的角度来看,仅getter和只读属性有什么区别?
塞德里克·博伊文

-1

通过defineGetter在Firefox和Opera上更改navigator.userAgent

navigator.__defineGetter__('userAgent', function(){
    return( "iPhone 5" );
});

alert( navigator.userAgent ); //iPhone 5

通过对象实例在IE和Opera上更改navigator.userAgent

var navigator = new Object; 
navigator.userAgent = 'iPhone 5';

alert( navigator.userAgent ); //iPhone5

好东西是,如果您使用IE Web浏览器控件,则可以通过execScript双重欺骗HTTP请求和JavaScript navigator.userAgent。

WebBrowser1.Navigate "http://example.com", , , , "User-Agent: iPhone 5" & vbCrLf

WebBrowser1.Document.parentWindow.execScript ("var navigator=new Object;navigator.userAgent='iPhone 5';")
WebBrowser1.Document.parentWindow.execScript ("alert(navigator.userAgent);") 'iPhone 5

我认为您通过使导航器成为新对象提到的IE方法不起作用。请进行验证
Pratik Pattanayak 2015年

-2

不,我怀疑您可以在JavaScript中完成此操作。但是,使用Firefox的用户代理切换器,您可以测试所需的任何用户代理,那么为什么不使用它呢?


1
您没有看到我说“数百个用户代理字符串”的部分吗?
Stefan Kendall,2009年

1
我认为您不太了解单元测试的目的。“我只有30个测试用例,那么为什么每次我做最小的更改时都不要手动运行呢?”
Stefan Kendall,2009年

使用用户代理测试器并修改代码以使用所有“数百个用户代理字符串”进行自动测试。如果您知道JavaScript,那应该非常简单
Marius,2009年

与JS的用户知识似乎无关。您的答案不包含有关张贴者将如何实际达到此结果的任何有效信息。这更多是评论而不是答案。
佩里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.