月度存档: 十月 2008

服务端XMLHTTP(ServerXMLHTTP in ASP)进阶应用-User Agent伪装

服务端XMLHTTP(ServerXMLHTTP in ASP)进阶应用-User Agent伪装

这篇开始讲讲ServerXMLHTTP的进阶应用。说是进阶应用,但也就是讲一些在基本应用里没有讲到的属性或者方法之类:)

使用setRequestHeader伪装User-Agent

User-Agent一般是服务端程序用来判断客户端浏览器、操作系统等信息的标志,它的说明可以参考Wiki,譬如在我的电脑 IE7 的UA就是:

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ;

SLCC1; .NET CLR 2.0.50727; .NET CLR 3.5.21022; .NET CLR 3.5.30729; .NET CLR 3.0.30618)

可以看到,这个UA提供了不少信息,IE版本、Windows版本、.NET版本都有。再看看 Firefox 的:

Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3 (.NET CLR 3.5.30729)

UA的格式不是本文的重点,因此,如果需要了解UA的具体格式,可以去Google上找找,另外,在http://www.user-agents.org/这里可以找到目前大多数浏览器、搜索引擎Spider等的UA。

在我们使用 ServerXMLHTTP 发送请求时,它所用的 User-Agent 是以下内容:

Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)

但是,某些网站会限制这个UA的访问,比如Google,如果我们使用以下代码来请求Google的页面,它使用的是默认UA“Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)”:

<%@LANGUAGE=”JScript” CODEPAGE=”65001″%>
<%
// code from xujiwei
// http://www.xujiwei.cn
var url = “http://news.google.cn/?output=rss”;
var xmlhttp = new ActiveXObject(“MSXML2.ServerXMLHTTP.5.0”);
xmlhttp.open(“GET”, url, false);
//xmlhttp.setRequestHeader(“User-Agent”, “Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3 (.NET CLR 3.5.30729)”);
xmlhttp.send(“”);
Response.BinaryWrite(xmlhttp.responseBody);
xmlhttp = null;
%>

你会发现,我们会收到一个 403 Forbidden 的错误:

为了使得Google的RSS输出程序能把我们的识别成正常的RSS阅读或者一般浏览器,就需要在请求发出前设置 Request Header。要设置 Request Header,只需要简单的在调用 open 方法之后,send 方法之前使用 setRequestHeader 来设置就行了,它的语法是 xmlhttp.setRequestHeader(key, value)。下面我们就让Google的RSS输出程序把我们的请求识别为Firefox的请求:

<%@LANGUAGE=”JScript” CODEPAGE=”65001″%>
<%
// code from xujiwei
// http://www.xujiwei.cn
var url = “http://news.google.cn/?output=rss”;
var xmlhttp = new ActiveXObject(“MSXML2.ServerXMLHTTP.5.0”);
xmlhttp.open(“GET”, url, false);
// 设置 User Agent 为 Firefox 的UA
xmlhttp.setRequestHeader(“User-Agent”, “Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3 (.NET CLR 3.5.30729)”);
xmlhttp.send(“”);
Response.BinaryWrite(xmlhttp.responseBody);
xmlhttp = null;
%>

刷新浏览器,显示出了Firefox的RSS预览界面,获取Google资讯RSS成功!

ok,我们能正确得到Google资讯的RSS了,再通过XMLDOM来操作返回的RSS文档,就可以采集Google资讯那海量的新闻了。

to be continued.

系列目录:

1. 服务器XMLHTTP(Server XMLHTTP in ASP)基础

2. 服务端XMLHTTP(ServerXMLHTTP in ASP)基本应用(上)

3. 服务端XMLHTTP(ServerXMLHTTP in ASP)基本应用(下)

by xujiwei

http://www.xujiwei.com/

[JavaScript] 慎用 somefunction.prototype

在写 JavaScript 脚本的时候,为了创建一个类,如果不使用框架,一般情况我们都会使用 prototype 来给要创建的类增加公有方法,例如:

  • // code from xujiwei.cn
  • function Person(name) {
  • this.Name = name;
  • }
  • Person.prototype.SayHello = function() {
  • alert(‘Hello, ‘ + this.Name);
  • }
  • Person.prototype.SayBye = function() {
  • alert(‘Goodbye, ‘ + this.Name);
  • }

不过,有的时候,为了书写以及维护的方便,我们会把公有方法的声明写到一个对象里,然后赋值给 Person.prototype,例如:

  • // code from xujiwei.cn
  • function Person(name) {
  • this.Name = name;
  • }
  • Person.prototype = {
  • SayHello: function() {
  • alert(‘Hello, ‘ + this.Name);
  • },
  • SayBye: function() {
  • alert(‘Goodbye, ‘ + this.Name);
  • }
  • }

使用这种方式,在这个类具有大量公有方法的时候,就不需要维护许多的 Person 标识符,如果某一天这个类的名字需要改变,那么要改的地方只有两个,一个是 function 的声明,一个是 prototype 前面的标识符,如果是使用前一种方式的话,那么有多少个公有方法,就需要维护 N+1 个标识符了,虽然可以使用查找替换,但是从稳定上来说,查找替换可能会引起一些错误,这增加了维护的成本。

这种方式虽然给我们的维护增加了便利,但也引发了另外一个隐藏的问题,就是类的 constructor 属性丢失的问题

  • // code from xujiwei.cn
  • function Person1(name) {
  • this.Name = name;
  • }
  • Person1.prototype.SayHello = function() {
  • alert(‘Hello, ‘ + this.Name);
  • }
  • Person1.prototype.SayBye = function() {
  • alert(‘Goodbye, ‘ + this.Name);
  • }
  • // code from xujiwei.cn
  • function Person2(name) {
  • this.Name = name;
  • }
  • Person2.prototype = {
  • SayHello: function() {
  • alert(‘Hello, ‘ + this.Name);
  • },
  • SayBye: function() {
  • alert(‘Goodbye, ‘ + this.Name);
  • }
  • }
  • alert(new Person1(‘Bill’).constructor);
  • alert(new Person2(‘Steve’).constructor);

运行上面的测试代码我们可以发现,Person1 的 constructor 属性为 Person1 类的构造函数,但是 Person2 的 constructor 属性却是 Object,那么在需要使用 constructor 属性来判断对象类型的时候,就会出现问题。

因此,在写 JavaScript 类的时候,如果不需要使用 constructor 属性来获取对象的类型,那么个人比较倾向于使用第二种写法,但是如果需要使用 constructor 属性以实现自己的反射机制或 GetType 函数等等,那么就要使用第一种写法。

当然,如果在实现自己反射机制或 GetType 函数时并不依赖 constructor 属性,那么两种写法都是可以的了,例如额外维护一个成员变量,用于标识自身的类型等。也可以使用一些现成的JS框架,有一些框架已经实现了JS中类的实现等,例如 js.class,这就看个人需要进行取舍了。

服务端XMLHTTP(ServerXMLHTTP in ASP)基本应用(下)

服务端XMLHTTP(ServerXMLHTTP in ASP)基本应用(下)

接着上一篇《服务端XMLHTTP(ServerXMLHTTP in ASP)基本应用(上)》继续讲讲ServerXMLHTTP的基本应用。

3. 请求使用HTTP认证的页面

呃,虽然说目前使用HTTP基本认证的已经少之又少,但是,总该知道ServerXMLHTTP有这么一个功能,可以直接实现HTTP基本认证。

在ServerXMLHTTP对象的open中,我们通常用到的只是它的前3个参数,即method、uri、async,但事实上,它还有另外两个可选参数,即用于HTTP基本认证的username及password。

那么,如果某天,我们要使用ServerXMLHTTP访问某个使用HTTP基本认证的网站,并且我们已经有了认证所需要的用户名及密码,那么可以使用以下密码来访问需要认证的内容:

  • <%@LANGUAGE=”JScript” CODEPAGE=”65001″%>
  • <%
  • // code from xujiwei
  • // http://www.xujiwei.cn
  • // 访问www.google.cn并不需要HTTP认证,这里只是作为一个演示
  • var url = “http://www.google.cn”;
  • var xmlhttp = new ActiveXObject(“MSXML2.ServerXMLHTTP.5.0”);
  • // 用户名和密码分别为username和password
  • xmlhttp.open(“GET”, url, false, “username”, “password”);
  • xmlhttp.send(“”);
  • Response.BinaryWrite(xmlhttp.responseBody);
  • xmlhttp = null;
  • %>

4. 使用responseXML属性

有时候,我们所需要的结果并不是文本的,而是一个XML文档,譬如目前最常用的RSS。这个时候,responseXML属性就是我们的不二选择了。

使用responseXML属性所得到的对象,就是一个DOMDocument对象,这个对象可以使用诸如selectNodes、selectSingleNode这样的方法来操作XML文档对象。

例如,我们可以利用ServerXMLHTTP抓取新浪新闻的RSS并显示出来:

  • <%@LANGUAGE=”JScript” CODEPAGE=”65001″%>
  • <%
  • // code from xujiwei
  • // http://www.xujiwei.cn
  • // 新浪新闻的RSS地址
  • var url = “http://rss.sina.com.cn/news/marquee/ddt.xml”;
  • var xmlhttp = new ActiveXObject(“MSXML2.ServerXMLHTTP.5.0”);
  • xmlhttp.open(“GET”, url, false);
  • xmlhttp.send(“”);
  • var xml = xmlhttp.responseXML;
  • Response.Write(“<h1>” + xml.selectSingleNode(“/rss/channel/title”).text + “</h1>”);
  • var items = xml.selectNodes(“/rss/channel/item”);
  • for(var i = 0; i < items.length; i++) {
  • Response.Write(“<h3>” + items[i].selectSingleNode(“title”).text + “</h3>”);
  • Response.Write(“<small>” + items[i].selectSingleNode(“pubDate”).text + “</small>”);
  • Response.Write(“<div>” + items[i].selectSingleNode(“description”).text + “</div><hr />”);
  • }
  • items = null;
  • xmlhttp = null;
  • %>

这些如果弄明白了,写一个RSS新闻聚合器就不是难事了。当然XMLDOM操作就不在本系列的范围之类了。

5. to be continued

呃,这篇其实有点废话了。隔得有点久,都快忘了该写什么了。

接下来会写写稍微进阶点的操作。

系列目录:

1. 服务器XMLHTTP(Server XMLHTTP in ASP)基础

2. 服务端XMLHTTP(ServerXMLHTTP in ASP)基本应用(上)

by xujiwei

http://www.xujiwei.com

服务端XMLHTTP(ServerXMLHTTP in ASP)基本应用(上)

服务端XMLHTTP(ServerXMLHTTP in ASP)基本应用(上)

在上一篇《服务器XMLHTTP(Server XMLHTTP in ASP)基础》中我介绍了一下ServerXMLHTTP对象的一些基本属性和方法,接下来我讲讲ServerXMLHTTP的基本应用。

在ServerXMLHTTP中,异步请求不再是主要用途,往往是同步的请求用得更多,因为在服务端编程中,程序的执行是需要迅速结束并返回结果的,不像在桌面程序中,有一个消息循环。这样就导致了在服务端编程中,同步编程用得更多。当然,这并不是说异步请求没有用处,在一定的情况下,异步请求会有很大的作用。

1. 简单的使用ServerXMLHTTP请求并显示指定Url

首先来看一下很简单的例子,使用ServerXMLHTTP请求Google的首页并显示出来:

  • <%@LANGUAGE=”JScript” CODEPAGE=”65001″%>
  • <%
  • // code from xujiwei
  • // http://www.xujiwei.cn
  • var url = “http://www.google.cn”;
  • var xmlhttp = new ActiveXObject(“MSXML2.ServerXMLHTTP.5.0”);
  • xmlhttp.open(“GET”, url, false);
  • xmlhttp.send(“”);
  • Response.BinaryWrite(xmlhttp.responseBody);
  • xmlhttp = null;
  • %>

在浏览器查看这个页面,你就可以看到Google的首页了:

图片附件

但是,我们可以看到,这里的Logo图片是没有显示的,因为这个logo在网页源代码里是以相对路径的方式来指定的:

<img src=/intl/zh-CN/images/logo_cn.gif width=286 height=110 border=0 alt=”Google” title=”Google”>

但是,我们的测试服务器里并没有这个图片文件,因此浏览器就会显示此图片的替代文字“Google”。

这里我使用了xmlhttp的responseBody属性,这是因为,在不知道所请求的网页是使用什么编码的情况下,可以让浏览器来处理这个问题,而不用在服务器处理编码。如果要在服务器处理编码,你必需知道你所请求的URL所返回的内容是使用什么编码的,并且正确的将返回内容进行转码以使得客户端浏览器能正常的显示。

例如,我们请求Baidu的首页,就会因为编码问题而导致页面完全错乱:

图片附件

所以,使用responseText或者responseBody,完全取决于我们的需要,并不是一成不变的,或者,在某些时候,我们要使用的并不是这两个中的一个,而是responseXML:)

2. 设置超时

在使用ServerXMLHTTP发送同步请求时,整个ASP程序的执行是被阻塞了的,也就意味着在开始发送请求到请求完成响应这段时间里,我们是做不了任何事情的。那么这里就有几个问题,如果所请求的域名解析很慢怎么办?如果程序运行的服务器与请求的服务器之间的网络环境比较差导致连接很慢怎么办?如果要发送的数据量很大但是带宽不够怎么办?同样如果响应的数据量很大但是带宽不够怎么办?

服务器所在环境及网络条件我们是无法改善的,因为,面对这些问题,我们只能采取回避的策略,即如果碰到这些问题,我们就直接丢掉这个请求。这时,ServerXMLHTTP的超时机制就有很大的用处了。

在前一篇中,我介绍了ServerXMLHTTP的常用方法,其中有一个setTimeouts方法,就是用来设置ServerXMLHTTP对象的四个超时时间,分别是:域名解析超时时间(resolveTimeout)、连接超时时间(connectTimeout)、数据发送超时时间(sendTimeout)、数据接收超时时间(receiveTimeout)。这四个超时时间所代表的意义可以从它们的字面来理解,它们分别对应了这一节开头所提出一的四个问题。

在不使用setTimeouts方法进行设置的情况下,域名解析超时时间(resolveTimeout)是无限的,即不会在域名解析时产生超时,连接超时时间(connectTimeout)的默认值为60秒,数据发送超时时间(sendTimeout)的默认值为30秒,数据接收超时时间(receiveTimeout)的默认值也是30秒。

通常情况下,我们不需要默认值中所指定的那么长的超时时间,因为碰到了最坏的情况下,在一个页面显示时,访客将要面对2分钟左右的无响应时间,这时访客往往认为这个页面是无效的并且会离开这个页面。

所以我们要做的就是给ServerXMLHTTP设置一个较短的超时时间,一般情况下,域名解析和连接远程服务器都可以在2秒内完成,发送数据时间视数据量而定,如果只是使用GET请求,这个数据量是很小的,也可以在2秒内完成,而响应,则可以稍微长一点,定在10秒左右,超过10秒时可以认为远程服务器没有响应。

需要注意的是,setTimeouts方法所使用的参数单位是以毫秒为单位的,也就是说,如果要指定2秒的超时时间,所用的参数为2000。另外,setTimeouts的参数顺序也是固定的,按顺序为:域名解析超时时间(resolveTimeout)、连接超时时间(connectTimeout)、数据发送超时时间(sendTimeout)、数据接收超时时间(receiveTimeout)。

那么,可以使用下面的代码来完成超时设置:

  • <%@LANGUAGE=”JScript” CODEPAGE=”65001″%>
  • <%
  • // code from xujiwei
  • // http://www.xujiwei.cn
  • var url = “http://www.google.com”;
  • var xmlhttp = new ActiveXObject(“MSXML2.ServerXMLHTTP.5.0”);
  • // 设置超时时间,注意参数顺序
  • xmlhttp.setTimeouts(2000, 2000, 2000, 10000);
  • xmlhttp.open(“GET”, url, false);
  • xmlhttp.send(“”);
  • Response.BinaryWrite(xmlhttp.responseBody);
  • xmlhttp = null;
  • %>

如果在某个阶段超时了,程序会抛出异常,在JScript里可以使用try…catch来捕获,并根据ServerXMLHTTP对象的readyState属性来获知是在哪个阶段产生了超时异常。注意,同步请求时,超时异常会发生在调用send方法所在的行,例如上例中的xmlhttp.send(“”)。

  • <%@LANGUAGE=”JScript” CODEPAGE=”65001″%>
  • <%
  • // code from xujiwei
  • // http://www.xujiwei.cn
  • var url = “http://www.youtube.com/”;
  • var xmlhttp = new ActiveXObject(“MSXML2.ServerXMLHTTP.5.0”);
  • // 设置超时时间,注意参数顺序
  • xmlhttp.setTimeouts(2000, 2000, 2000, 10000);
  • xmlhttp.open(“GET”, url, false);
  • try {
  • xmlhttp.send(“”);
  • }
  • catch(e) {
  • Response.Write(“发生异常:” + e.message + “<br/>”);
  • // 判断是否为超时错误
  • if(e.number == -2147012894) {
  • var step = “”;
  • // 判断超时错误发生所在的阶段
  • switch(xmlhttp.readyState) {
  • case 1:
  • step = “解析域名或连接远程服务器”
  • break;
  • case 2:
  • step = “发送请求”;
  • break;
  • case 3:
  • step = “接收数据”;
  • break;
  • default:
  • step = “未知阶段”;
  • }
  • Response.Write(“在 ” + step + ” 时发生超时错误”);
  • }
  • Response.End();
  • }
  • Response.BinaryWrite(xmlhttp.responseBody);
  • xmlhttp = null;
  • %>

3. to be continued

这篇有点长了,拆到下篇再继续写。

系列目录:

1. 服务器XMLHTTP(Server XMLHTTP in ASP)基础

by xujiwei

http://www.xujiwei.com