这个判断是在服务端完成的, PHP代码将首先查看网关是否接收text/vnd.wap.vml MIME类型。如果不是,将检测前面的字符,查看是否为WML浏览器。如果不符合,那么就假设为HTML浏览器。如果有新的WML浏览器,那么ID字符串也要增加。
这个代码基于Robert Whitinger(robert@wapsight.com)的代码,使用了Don Amaro(donamaro.concepcion@nl.unisys.com)提供的列表。
注意:由于只需要四个字符串就可以辨别,因此例如:"WapIDE-SDK/2.0;(R320s(Arial))" 可以使用“WapI”来代替是可行的做法,也是足够的。
同样的功能也可以通过ASP来解决。先判断请求的是“/index.wml” 或者 “/index.html” 和所需要的MIME类型。另外以下的脚本辨别的方式和上面不一样。另外还需要网关告诉服务器它能接收 的text/vnd.wap.wml MIME类型。该例子如下所示:
<%
Response.Buffer = TRUE
Dim IsWap
httpAccept = LCase(Request.ServerVariables("HTTP_ACCEPT"))
if Instr(httpAccept,"wap") then
IsWap=1
Else Response.Redirect "/index.html" : Response.Flush : Response.End
End if
%>
<%Response.ContentType = "text/vnd.wap.wml"%><?xml version="1.0"?>
<%Response.Flush%>
<!DOCTYPE wml PUBLIC "//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="redirect">
<onevent type="onenterforward">
<go href="/index.wml"/>
</onevent>
<p>
<a href="/index.wml">enter</a>
</p>
</card>
</wml>
<%Response.Flush:Response.End%>
30. 如何判断访问者是来自哪个浏览器或者移动电话?
可以通过检查HTTP_USER_AGENT标签来判断。例如试着使用Microsoft Internet Explorer访问一个站点的时候,HTTP_USER_AGENT将返回:Mozilla/4.0 (compatible;MSIE 5.0; Windows 98);在同样的情况下使用Nokia 7110访问这个站点,HTTP_USER_AGENT就会是:Nokia7110/1.0(04.73)。据此可以判断用户代理是什么类型的。
31. 可以得到用户代理的电话号码吗?
不可以,除非网关支持这个特点,WAP没有办法知道用户的电话号码。
32. 可以通过WML使得可以用WAP设备进行拨号吗?
WAP的电话功能可以使用Wireless Telephony Application Interface(WTAI)。
例如:
WMLScript: WTAPublic.MakeCall("9287787");
但是第一代的WAP设备不支持这个功能。
33. 能够从WAP设备中读取数据吗,例如:电话号码?
这里有一些通过HTTP的信息,但是十分有限。既然只有网关发送过来少量的信息,像WAP设备的号码可能无法读取。同时,在某些国家这还涉及到个人隐私的问题。
基本上丢弃的内容就是WAP网关传送给HTTP服务器的内容。这不同于WAP网关到网关。Phone.com的UP.Link网关是一个最好的例子。因为它在HTTP头中返回一个字符串叫做 UP_X_SUBNO,里面包含了电话号码。Ericsson网关将传送一个辨别设备用的字符串,但是在明文中没有电话号码。
每次WAP设备向HTTP服务器请求一个URL,WAP网关就会将信息传送给HTTP服务器。
以下的PHP脚本显示了从网关过来的所有HTTP头的信息。可以使用WML浏览器进行测试。(http://wap.colorline.no/clientinfo.html)。其他的例子也可以在下面的UTL中找到:http://wap.colorline.no/demos.html
第一个部分是取得所有的标准HTTP头信息。第二个部分是提取一个内容。
<?
header("Content-type: text/vnd.wap.wml");
echo("<?xml version="1.0"?>n");
echo("<!DOCTYPE wml PUBLIC "//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">nn");
echo("<!—Code written in Microsoft NOTEPAD.EXE à n");
?>
<wml>
<card id="init" title="Client Info">
<p>
<?
// First part – standard HTTP stuff
$headers = getallheaders();
while (list($header, $value) = each($headers)) {
echo strtoupper($header). ": ". $value. "<br/>n";
}
// Second part
// IP address of the client side
echo("REMOTE_ADDR: ".$REMOTE_ADDR. "<br/>n");
// Port at the client side
echo("REMOTE_PORT: ".$REMOTE_PORT. "<br/>n");
// Name of authenticated user
echo("REMOTE_USER: ".$REMOTE_USER. "<br/>n");
// Gateway Interface type
echo("GATEWAY_INTERFACE: ".$GATEWAY_INTERFACE. "<br/>n");
// Protocol used by the server
echo("SERVER_PROTOCOL: ".$SERVER_PROTOCOL. "<br/>n");
// Request Method
echo("REQUEST_METHOD: ".$REQUEST_METHOD. "<br/>n");
// Connection type
echo("HTTP_CONNECTION: ".$HTTP_CONNECTION. "<br/>n");
// Host it connected via (proxy)
echo("HTTP_VIA: ".$HTTP_VIA. "<br/>n");
?>
</p>
</card>
</wml>
Henrik Gemal (gemal@dk.net)也有一个在线的基于WML的工具BrowserSpy,来显示更多关于HTTP头的信息、服务器环境和用户的浏览器等等。有关这个工具的详细情况可以浏览http://wap.gemal.dk/
Werner Forkel 提交了一个Perl的脚本,可以显示电话号码(如果有)。可以在以下位置测试:http://wap.colorline.no/wap-faq/apps/subnotest.wml,同样也收集在:http://wap.colorline.no/demos.html.
这些程序只适合某个网关。如果要测试其他的网关,可能就显示不出电话号码。因此电话号码不能作为ID号来处理。至少因为不是一个全球的标准。
34. 有没有办法连接到电话号码?
在某些情况下,当在显示了一连串的号码之后,需要中断功能连接到一个电话号码上并拨号。例如:执行一个 dial:12345678 就非常像 mailto: 标签。
越来越多的浏览器都支持这个功能,但还不是所有。Phone.com, Mitsubishi 和 Ericsson 已经在浏览器中集成了这个基于Wireless Telephony Interface specifications (WTAI)的功能。 WTAI将允许以下的URL将关闭连接并且拨号:
<go href="wtai://wp/mc;+4712345678">Make acall to +47-12345678</go>
Nokia 7110 已经有个功能叫做“Use Number”。它可以通过WML卡片查找一个类似于电话号码的列表,然后用户可以选择进行呼叫。注意用户必须分离这些数字以便它能正常工作。
35. 使用GET或者POST方式能传送多少字符?
使用GET或者POST方式所能传送的字符数目,不同的设备有不同的限制。一个GET通过UTL传送变量,能传送的数据总量比使用POST方式所能传送的数据要小。例如,Nokia 7110似乎对每个GET 限制在512个字节左右,但是POST最大可以达到一个编译后卡片的大小(约1300字节)。UP.SDK 4.0将GET请求限制在970左右,最大可以达到一个编译后卡片的大小。
显然,卡片有时候保存了要发送给服务器的参数的内容,既然编译后的卡片大小有限制,那么肯定要影响到整个所能传输的数据。
在POST和GET之间没有太多的区别。比如这个没有很好地使用GET的例子。
<input type="text" name="var1" format="*N"/>
<p>
<anchor>Send it
<go href="somescript.cgi?variable=$(var1)" method="get"/>
</anchor>
</p>
下面仍然是一个使用GET的请求,但是使用了<postfield>来传送参数。这个代码就漂亮多了。既然可以定义为GET,同样也很容易转成POST。
<input type="text" name="var1" format="*N"/>
<p>
<anchor>Send it
<go href="somescript.cgi" method="get">
<postfield name="variable" value="$(var1)"/>
</go>
</anchor>
</p>
直接改为POST:
<input type="text" name="var1" format="*N"/>
<p>
<anchor>Send it
<go href="somescript.cgi" method="post">
<postfield name="variable" value="$(var1)"/>
</go>
</anchor>
</p>
最好是做测试找到到底能传输多少数据。这里有个测试程序:
http://wap.colorline.no/wap-faq/apps/putsize.php3。
这个程序也可以在下面的URL中找到:http://wap.colorline.no/demos.html。
该程序将产生一个卡片包含一个变量,里面包含了一定数量的字符X。用户可以选择传输是使用GET还是POST。在传输之后,脚本将要显示接收到的字符个数。
脚本生成一个页面来测试使用GET或者POST方式到底能发送多少个字符:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<head>
<meta forua="true" http-equiv="Cache-Control" content="max-age=0"/>
<meta forua="true" http-equiv="Cache-Control" content="must-revalidate"/>
</head>
<card>
<do type="prev" label="Back">
<go href="putsize.php3"/>
</do>
<p>
<anchor>GET data
<go method="get" href="putsize.php3">
<postfield name="a"
value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"/>
</go>
</anchor>
</p>
</card>
</wml>
36. 如何同HTML站点一样POST/CGI,返回表单数据到服务器?
如果使用:
<go method="post" href="mycgi.cgi">
并且使用:
<postfield name="fieldname" value="$NameOfInputField"/>
就可以POST数据给CGI程序了。
37. POST无法工作是怎么回事?
有很多说POST参数将会丢失的流言,特别是在Nokia 7110。就笔者所知,还没有哪个Nokia 7110有这样的问题。这个问题主要是存在于网关或接收方。
测试显示Nokia SDK 2.18,当使用内建网关的时候,使用POST会出现问题。甚至当method 设置成“POST”的时候,服务器那边还是将POST请求作为 GET。
当使用POST的URL时 ,Nokia SDK 将会崩溃。在某些情况下URL的最后的字符将被删除。
POST Test页面将简单的POST的两个变量叫做“var1”和“var2”来显示整个变量的内容和HTTP头的内容。如果不能看到正确的变量内容,肯定有问题。检查HTTP头中的application/x-www-form-urlencoded(注意!需要在变量中输入一些内容)。
这个方法解决了Nokia SDK 2.18的问题,可以把它配置到任意的公共网关来测试。笔者推荐使用 wapHQ 网关。
在其他的情况下,POST确实不工作,问题可能是HTTP头在服务器端解释的时候有问题。脚本语言,例如:ASP、Java或是CGI等等都是通过查看在HTTP头中的 application/x-www-form-urlencoded 完全匹配的字符串。在某些情况下字符串可能有附加的数据,例如:charset="utf8" 。既然服务器端不是精确的匹配,它就不会查看HTTP头,因此POST就变量丢失了。
注意这不是浏览器的问题,在HTTP头加入字符集描述,将造成脚本语言方面的错误。
为了检测有关网关或浏览器的问题,仍使用上面的POST Test页面来测试。同样查看application/x-www-form-urlencoded 的输出,检查有没有附加的字符在结尾部分,如果有,那么这就是服务器端的问题。
解决这个问题的方案很复杂,它随用户使用的脚本描述语言不同而不同,而且需要操作原代码。简单地说,解决方案就是需要人工读取HTTP头,不要使用脚本语言已经写好的读取方式。
这里有一个用ASP编写的解决方法。它显示了如何在POST中抓取数据。用户需要从二进制数据中发现需要的变量。
Dim lngToalByteCount
Dim vntRequestData
lngTotalByteCount = Request.TotalBytes
vntRequestData = Request.BinaryRead(lngTotalByteCount)
全部的代码,就应该像下面的代码:
<%@ Language=VBScript %>
<%
Dim Temp, i, sPost, sWMLDeck
''Converts the binary data to a string.
For i = 1 To Request.TotalBytes
Temp = Request.BinaryRead(1)
sPost = sPost & Chr(AscB(Temp))
Next
''Parses out the values of the POSTED variables (in this
''example myvar1 and myvar2).
Dim sVar1, sVar2
sVar1 = getVar("myvar1", sPost)
sVar2 = getVar("myvar2", sPost)
''Writes the WML Deck displaying the POSTED Variables
sWMLDeck = "<?xml version=""1.0""?>" & vbCrLf
sWMLDeck = sWMLDeck & "<!DOCTYPE wml PUBLIC ""//WAPFORUM//DTD WML 1.1//EN"" "
sWMLDeck = sWMLDeck & """http://www.wapforum.org/DTD/wml_1.1.xml"">" & vbCrLf
sWMLDeck = sWMLDeck & vbCrLf & "<wml>" & vbCrLf & vbTab
sWMLDeck = sWMLDeck & "<card id=""main"" title=""POST TEST"">" & vbCrLf
sWMLDeck = sWMLDeck & vbTab & vbTab & "<p>" & vbCrLf
sWMLDeck = sWMLDeck & vbTab & vbTab & vbTab & "myVar1: " & sVar1 & "<br/>" & vbCrLf
sWMLDeck = sWMLDeck & vbTab & vbTab & vbTab & "myVar2: " & sVar2 & vbCrLf
sWMLDeck = sWMLDeck & vbTab & vbTab & "</p>" & vbCrLf & vbTab
sWMLDeck = sWMLDeck & "</card>" & vbCrLf & ">/wml>"
Response.ContentType = "text/vnd.wap.wml"
Response.Write(sWMLDeck)
''Quick function for picking out the values of the POSTed variables.
''sKey is the variable name, sRaw is the POST string.
Private Function getVar(sKey, sRaw)
Dim sRetVal
If InStr(sRaw, sKey) Then
sRetVal = Mid(sRaw, InStr(sRaw, sKey) + Len(sKey) + 1)
If InStr(sRetVal, "&") Then
sRetVal = Mid(sRetVal, 1, InStr(sRetVal, "&") - 1)
End If
End If
getVar = sRetVal
End Function
%>
38. 为什么META标签不工作?
浏览器不支持默认的meta标签,例如:
<meta http-equiv="refresh" content="1;http://somewhere.com/">
虽然有少量网关支持非常有限的META标记。但是测试显示,如果使用了它们,网关就会出问题。例如某网关不支持普通的HTTP Cache控制,如果要实现Cache控制只好使用特殊的META标记。显然从其他网关来的用户就可能不支持这个META。注意:不要使用META tags。肯定有其他的方式来完成你的想法。
最常使用的META是:
<meta http-equiv="refresh" content="1;http://somewhere.com/">
这个告诉浏览器重新装入指定的WML页面。WML中已经包含了一个<ontimer>。
39. 为什么服务器接收不到用户发送的参数?
用户输入的参数或者其他参数可以像在HTML中一样通过提交方式发送到服务器。在HTML中这个是<FORM>,POST或者GET。
首先知道要知道POST和GET的区别。对于POST浏览器将生成一个数据包将变量名和它们的内容捆绑在一起,并发送到服务器。对于GET,它其实是一个URL请求,变量名和内容都包含在URL中。
对于WAP环境,要求是非常严格的,必须要根据协议来操作。虽然以下的URL
"/cgi-bin/somescript?username=john&telephone=123-123-1234&occupation=banana+bender"
可以在HTML环境中工作,但是在WAP环境中无法工作。以上的部分编码将使得保护的变量内容被误解。特殊的空格(在 banana 和 bender )被转成 “+”。 URL就根本没有空格。
以上的URL在WAP中无法工作的主要原因是用来分割每个变量和变量内容的 & (与号)没有转义。正确的格式应该是:
"/cgi-bin/somescript?username=john&telephone=123-123-1234&occupation=banana+bender"
在这里 & 被名字实体所替换。为了解释更清楚些,请看下面的代码:
<card id="input" title="Gimme some data">
<p><input type="text" name="username" format="M*m"/></p>
<p><input type="text" name="occupation" format="M*m"/></p>
<p>
<anchor>Send this
<go href="/cgi-bin/somescript?username=$(username)
&occupation=$(occupation)"/>
</anchor>
</p>
注意这不是真正的WAP协议,专门的字符应该转义,否则将得到不可预料的结果。
40. 为什么在HTTP中的Referer看不见?
当HTML浏览器从一个URL到另外一个URL的时候,它默认地会发送一个叫做 Referer的 HTTP头给服务器,告诉它在浏览这个页面之前的那个页面。这是一个非常有用的特点,在WAP环境中同样也有。但是既然这个信息来自用户代理(浏览器)、WAP设备,通常为了节约带宽和时间,就被省略了。
为了使用 Referer ,应该使用新的URL标签例如: <a>,<go>等等,并且加入参数:sendreferer。
<go href="/somedir/somedeck.wml" sendreferer="true"/>
这样就会把参考的URL发送到服务器。
41. 如果没有找到URL,有可能重新将用户引导到另外一个WML卡片或者页面吗?
是的。但这是服务器端的特点,与客户端没有关系。
42. 为什么普通的HTTP 302重新导向不好使?
这的确是一个事实。核心的问题是在服务端的脚本语言,而不是在服务端语言和服务器之间。
所谓的302 Found HTTP反应是指服务器告诉用户代理,它所需要的资源在另外的地方可以找到。302反应可能包括一个人们可理解的信息,如果在这种情况下“ Content-type: ”就被设置了。笔者所测试过的服务器,即使没有内容也都加了“Content-type:”。默认的 “Content-type:” 是text/html。当然有些网关不喜欢这个类型。
以下的例子已经经过测试,在Apache和Microsoft Internet Information Server都可以工作。如果使用其他的Web Server,或者其他的脚本语言,需要能转换这些简单的脚本。关键的工作是十分简单的,除非需要,不用告诉服务器整个HTTP头。大多数Web Server将自动完成这个HTTP头,使得用户代理可以理解。
所有的代码例子可以在线测试。如果它们能够工作,用户将被重新引导到http://wap.colorline.no/clientinfo.html ,在那儿将产生一个WML页面来显示所有的HTTP头。
PHP 代码测试可以在 http://wap.colorline.no/wap-faq/apps/302test.php3中找到。
<?
header("Location: http://wap.colorline.no/clientinfo.html");
header("Content-type: text/vnd.wap.wml";
?>
Perl测试代码可以在http://wap.colorline.no/cgi-bin/302test.pl中找到。
print "Location: http://wap.colorline.no/clientinfo.htmln";
print "Content-type: text/vnd.wap.wmln";
ASP测试代码可以在 http://www.colorline.no/302test.asp中找到。 (注意不同的URL):
<%
Response.Redirect = "http://wap.colorline.no/clientinfo.html";
Response.ContentType = "text/vnd.wap.wml";
Response.Flush
Response.End
%>
43. 可能在WML中实现ASP Session吗?
不可能。可以把信息存储在临时变量中模拟Session。Session是保存在用户PC上的“cookies”中。目前的WAP设备不支持“cookies”。不过下一代的设备和WML可能支持“cookies”。
44. WAP支持Session吗?
在HTML中,一个十分普遍的“处理”用户的方法就是为每个用户分配一个“session”。这个有时候是通过指定一个独一无二的cookies来实现的。然而WAP的资源非常有限,因此session的处理必须以不同的方式来处理。
Alex Kriegel 提供了一个安装在 WAPlinks的Custom Session Object包。这个zip文件中包含了VB类的文件和编译过的Dll文件,还有相关的文档。这些可以在http://www.waplinks.com/customsessionobject.zip下载。
另外一种方法是使用 PHPlib ,它是使用 PHP 编写的。
Tarique (tarique@nagpur.dot.net.in) 提供了如何使用PHPlib来验证和处理每个WAP用户。有相关的文件和注释可以到下面地址下载:
http://wap.colorline.no/wap-faq/archive/phplib_wml.zip
45. 可以在WAP中使用Cookies吗?
在理论上是可以的,但不是所有的WAP设备都支持。另一个方法来管理会话是使用隐藏的fields(包含会话标识,无论是POST或者GET方式)。
46. WAP支持Cookies吗?
普通的HTTP Cookies是作为WAP的扩展来实现的。无论你以前听到什么,Cookies的支持将越来越好。实际上Phone.com的 UP.Link网关已经支持这个功能有一段时间了。
可以使用以下的脚本语言检测Cookie-support,:
http://wap.colorline.no/wap-faq/apps/cookietest.php3
脚本在http://wap.colorline.no/demos.html也可以得到。
当第一次看见卡片的时候,记数器应该为0。所有的Cache都被关闭。卡片通过在URL中随机地加入变量来强制自己加载(笔者不推荐这种强制加载办法)。当点击增加计数连接,页面将重新加载,卡片就再次出现,并且记数器变成1。
在脚本中,Cookie的名字被称做 TestCookie,它有很长的生命期,因此可以隔好几天再来查看记数器,它将是上一次的数值。这要求你能使用与上一次访问所使用的WAP环境一样,否则将把你的数值清0。
如果记数装置一直都是0,那么cookie 就没有能传送到你的Web Server。这个脚本也能表示Cookie是否被传送。
另外,这个脚本同样还显示HTTP头中的HTTP_VIA 和 HTTP_USER_AGENT 。这些能够得到所使用的网关和模式。一些网关使用HTTP_VIS标识自己,而另外一些使用HTTP_USER_AGENT,还有一些则让程序无法知道。
下面是它的PHP代码。一个标准的 PHP setcookie() 函数只有在这种脚本语言中才会出现。函数只是简单地设置cookie,并且PHP变量 $HTTP_COOKIE_VARS 用来读取cookie。
<?
if(isset($HTTP_COOKIE_VARS["TestCookie"]))
{// Check if TestCookie is set
$cookieset = "set";
// Read the Cookie
$cookieid = $HTTP_COOKIE_VARS["TestCookie"];
// and increase its value
$cookieid++;
}
else {
// cookie was not set
$cookieset = "not set";
// start counter at zero
$cookieid = 0;
}
// apply the Cookie to the HTTP header
setcookie("TestCookie",$cookieid);
// set the content type for WML
header("Content-type: text/vnd.wap.wml");
// disable ALL caching
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
echo("<?xml version="1.0"?>n");
echo("<!DOCTYPE wml PUBLIC "//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">nn");
echo("<!-- This application attempts to test the capabilities of a WAP gateway to support; cookies -->n");
echo("<!-- App by Espen.Lyngaas@colorline.no (c) 2000 -->n");
// Generate random value for reload forcing
$random = mt_rand(100000,999999);
?>
<wml>
<head>