一、Flex端HTTPService代码
功能说明
Flex中,很多类都可以通过两种方式进行使用和配置,一种是MXML标签方式,另一种ActionScript编程方式,这两种方式稍后都将进行介绍。
本例只对HTTPService的使用进行简单介绍,程序主要完成从服务器端请求xml数据并加载绑定到Flex端的DataGrid上。
以下是xml文档(category.xml)内容:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<category>
<name>Dairy</name>
<categoryID>4</categoryID>
</category>
<category>
<name>Deli</name>
<categoryID>5</categoryID>
</category>
<category>
<name>Fruit</name>
<categoryID>3</categoryID>
</category>
<category>
<name>Meat</name>
<categoryID>1</categoryID>
</category>
<category>
<name>Seafood</name>
<categoryID>6</categoryID>
</category>
<category>
<name>Vegetables</name>
<categoryID>2</categoryID>
</category>
</catalog>
主要属性
名称 | 类型 | 说明 |
---|---|---|
url | Property | HTTP请求的地址 |
fault | Event | 当请求失败时触发 |
result | Event | 当请求成功是触发 |
send | Method | 发送请求 |
headers | Property | 用于自定义HTTP请求头 |
lastResult | Property | 最后一次请求成功时返回的数据 |
method | Property | HTTP请求方式(get、post、delete、put) |
requestTimeout | Property | 请求超时时间 |
resultFormat | Property | 对服务器响应的数据的解析方式,默认为object,可以设置为object、array、xml、flashvars、text、e4x,详见官方API |
contentType | Property | 请求发送的数据内容类型,默认为application/x-www-form-urlencoded,可设置为application/xml |
关于Flex的API完全文档可以查阅官方文档①:http://flex.apache.org/asdoc/index.html
Flex客户端代码
①MXML方式
既然是使用MXML方式,肯定会使用标签,这里使用的标签是<mx:HTTPService/>因为是标签,所以除了上面介绍的属性,还必需一个id属性来使AS方便调用:
<mx:HTTPService id="httpService" url="http://localhost:5025" fault="onHttpServiceFault(event)" result="onHttpServiceSuccess(event)"/>
上面代码设置了当请求失败的fault事件和当请求成功的result事件,下面是在请求失败时做的简单处理和在请求成功时,将响应的XML数据绑定到DataGrid上。
protected function onHttpServiceFault(event:FaultEvent):void { Alert.show(event.message.toString()); } protected function onHttpServiceSuccess(event:ResultEvent):void { try { var dataSource:ArrayCollection = event.result.catalog.category as ArrayCollection; dgData.dataProvider = dataSource; } catch(e:Error) { Alert.show(event.result.toString()); dgData.dataProvider = null; } } // 下面是对应的DataGrid的MXML代码 <mx:DataGrid id="dgData" width="300"> <mx:columns> <mx:DataGridColumn headerText="类型ID" dataField="categoryID"/> <mx:DataGridColumn headerText="类别名称" dataField="name"/> </mx:columns> </mx:DataGrid>
这里采用的是在接收到数据是,使用AS代码将数据源绑定到DataGrid上,同样可以使用MXML的方式进行绑定,只需将dataProvider属性移到<mx:DataGrid/>标签中即可:
<mx:DataGrid id="dgData" width="300" dataProvider="{httpService.lastResult.catalog.category}">
最后就是需要用一个按钮,在点击按钮的时候发送请求,为了演示携带参数的请求,我在Flex客户端添加了一个TextInupt用于填入请求的xml文件名,为了避免恶意输入带路径的字符串,在Flex端做了简单的验证。以下是点击按钮的AS代码和文本框及按钮的MXML代码:
protected function loadData(event:MouseEvent):void { var fileName:String = txtDataFile.text; // 只允许加载URL同目录的文件,避免恶意加载 var namePassed:Boolean = fileName.indexOf('/') === -1 && fileName.indexOf('\\') === -1; if (namePassed) { httpService.send({ filename : fileName }); } else { Alert.show("禁止输入包含目录的路径"); } } // MXML代码 <mx:TextInput id="txtDataFile" text="category.xml" x="22" y="10" width="150" height="25"/> <mx:Button id="btnLoadData" label="加载数据" x="192" y="10" width="130" height="25" click="loadData(event)"/>
下面是Flex端MXML方式完整代码:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:layout="org.osmf.layout.*"> <fx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; protected function onHttpServiceFault(event:FaultEvent):void { Alert.show(event.message.toString()); } protected function onHttpServiceSuccess(event:ResultEvent):void { try { var dataSource:ArrayCollection = event.result.catalog.category as ArrayCollection; dgData.dataProvider = dataSource; } catch(e:Error) { Alert.show(event.result.toString()); dgData.dataProvider = null; } } protected function loadData(event:MouseEvent):void { var fileName:String = txtDataFile.text; // 只允许加载URL同目录的文件,避免恶意加载 var namePassed:Boolean = fileName.indexOf('/') === -1 && fileName.indexOf('\\') === -1; if (namePassed) { httpService.send({ filename : fileName }); } else { Alert.show("禁止输入包含目录的路径"); } } ]]> </fx:Script> <fx:Declarations> <!-- 将非可视元素(例如服务、值对象)放在此处 --> <mx:HTTPService id="httpService" url="http://localhost:5025" useProxy="false" fault="onHttpServiceFault(event)" result="onHttpServiceSuccess(event)"/> </fx:Declarations> <mx:TextInput id="txtDataFile" text="category.xml" x="22" y="10" width="150" height="25"/> <mx:Button id="btnLoadData" label="加载数据" x="192" y="10" width="130" height="25" click="loadData(event)"/> <mx:Panel x="22" y="52" fontSize="12"> <mx:DataGrid id="dgData" width="300"> <mx:columns> <mx:DataGridColumn headerText="类型ID" dataField="categoryID"/> <mx:DataGridColumn headerText="类别名称" dataField="name"/> </mx:columns> </mx:DataGrid> </mx:Panel> </s:Application>
②ActionScript方式
MXML方式和AS方式的区别在于,将<mx:HTTPService/>换成使用AS来new HTTPService对象,替换的操作就是,在AS代码中声明一个HTTPService对象,并且在主Application创建完成时,对HTTPService进行初始化(creationComplete事件,类似HTML中的onload事件):
// 首先在Application标签中添加creationComplete时间 <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="creationComplete(event)"> // 然后在AS代码中声明一个HTTPService对象,并在creationComplete方法中对其进行初始化 internal var httpService:HTTPService; protected function creationComplete(event:FlexEvent):void { httpService = new HTTPService(); httpService.url = "http://localhost:5025"; // 添加事件监听,下面这种方式和最下面被注释的两行代码的方式,功能相同 httpService.addEventListener("fault", onHttpServiceFault); httpService.addEventListener("result", onHttpServiceSuccess); // httpService.addEventListener(FaultEvent.FAULT, onHttpServiceFault); // httpService.addEventListener(ResultEvent.RESULT, onHttpServiceSuccess); }
下面是Flex端MXML方式完整代码:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="creationComplete(event)"> <fx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.events.FlexEvent; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.http.HTTPService; internal var httpService:HTTPService; protected function creationComplete(event:FlexEvent):void { httpService = new HTTPService(); httpService.url = "http://localhost:5025"; httpService.useProxy = false; httpService.addEventListener("fault", onHttpServiceFault); httpService.addEventListener("result", onHttpServiceSuccess); // httpService.addEventListener(FaultEvent.FAULT, onHttpServiceFault); // httpService.addEventListener(ResultEvent.RESULT, onHttpServiceSuccess); } protected function onHttpServiceFault(event:FaultEvent):void { Alert.show(event.message.toString()); } protected function onHttpServiceSuccess(event:ResultEvent):void { try { var dataSource:ArrayCollection = event.result.catalog.category as ArrayCollection; dgData.dataProvider = dataSource; } catch(e:Error) { Alert.show(event.result.toString()); dgData.dataProvider = null; } } protected function loadData(event:MouseEvent):void { var fileName:String = txtDataFile.text; // 只允许加载URL同目录的文件,避免恶意加载 var namePassed:Boolean = fileName.indexOf('/') === -1 && fileName.indexOf('\\') === -1; if (namePassed) { httpService.send({ filename : fileName }); } else { Alert.show("禁止输入包含目录的路径"); } } ]]> </fx:Script> <fx:Declarations> <!-- 将非可视元素(例如服务、值对象)放在此处 --> </fx:Declarations> <mx:TextInput id="txtDataFile" text="category.xml" x="22" y="10" width="150" height="25"/> <mx:Button id="btnLoadData" label="加载数据" x="192" y="10" width="130" height="25" click="loadData(event)"/> <mx:Panel x="22" y="52" fontSize="12"> <mx:DataGrid id="dgData" width="300"> <mx:columns> <mx:DataGridColumn headerText="类型ID" dataField="categoryID"/> <mx:DataGridColumn headerText="类别名称" dataField="name"/> </mx:columns> </mx:DataGrid> </mx:Panel> </s:Application>
二、服务器端监听处理
IIS直接部署
直接将XML文件部署到IIS上,这种方式就用不上传过去的filename了,因为IIS在接收到请求后,直接就将xml文件返回了。
.NET自定义监听
原理比较简单:使用Socket -> 监听指定IP的指定端口 -> 解析请求头中filename参数的值 -> 读取指定文件的内 -> 将文件数据作为响应内容进行传输 -> 关闭请求连接
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
namespace NetRequestListener
{
class Program
{
private static Socket _serverSocket;
static void Main(string[] args)
{
CreateSocket();
Console.WriteLine("Server port 5025 is start listening..");
ReceiveRequest();
}
/// <summary>
/// 初始化Socket
/// </summary>
private static void CreateSocket()
{
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.102"), 5025));
_serverSocket.Listen(52);
}
/// <summary>
/// 循环接收请求并处理
/// </summary>
private static void ReceiveRequest()
{
while (true)
{
Socket client = _serverSocket.Accept();
Console.WriteLine("An client has connecting");
Thread th = new Thread(new ThreadStart(() =>
{
byte[] fileNameBytes = new byte[1024];
int len = client.Receive(fileNameBytes);
byte[] responseData;
try
{
responseData = ReadFileContent(Encoding.UTF8.GetString(fileNameBytes, 0, len));
}
catch (FileNotFoundException)
{
responseData = Encoding.UTF8.GetBytes("File not exist");
}
catch (Exception)
{
responseData = Encoding.UTF8.GetBytes("An error occurred when reading file");
}
client.Send(responseData);
client.Shutdown(SocketShutdown.Both);
client.Close();
}));
th.IsBackground = true;
th.Start();
}
}
/// <summary>
/// 根据请求中的数据读取指定文件
/// </summary>
/// <param name="requestData">HTTP的请求头信息</param>
/// <returns></returns>
private static byte[] ReadFileContent(string requestData)
{
// 使用正则筛选出filename参数的值
Regex rg = new Regex("filename=(\\S+)");
string filePath = rg.Match(requestData).Groups[1].Value;
filePath = System.Web.HttpUtility.UrlDecode(filePath);
if (File.Exists(filePath))
{
return File.ReadAllBytes(filePath);
}
else
{
throw new FileNotFoundException();
}
}
}
}
Node.js自定义监听
node的代码相对要简单很多,代码很简单,就不做说明了:
// 引入module
var http = require('http');
var fs = require('fs');
var url = require('url');
http.createServer(function (req, res) {
console.log('An client has connecting');
// 将请求解析为对象,取出参数部分
var param = url.parse(req.url, true).query;
var message;
try {
// 取到参数中的filename,根据filename读取指定文件,这里使用阻塞读取方式
message = fs.readFileSync(param.filename, 'utf-8');
} catch(e) {
if(e.code === 'ENOENT') {
message = 'File is not exist';
} else {
message = 'An error occurred when reading file';
console.log(e.message);
}
}
// 自定义响应头
res.writeHead(200, { 'content-type' : 'text/xml'});
// 使用end将message响应给客户端,end后会关闭连接,另有write方法,这种方式不会关闭连接
res.end(message);
}).listen(5025, '192.168.1.102');
console.log('Server port 5025 is start listening..');
如果不想使用阻塞读取,当让也可以使用异步事件驱动:
// 引入module
var http = require('http');
var fs = require('fs');
var url = require('url');
http.createServer(function (req, res) {
console.log('An client has connecting');
var param = url.parse(req.url, true).query;
var message;
fs.readFile(param.filename, {encoding : 'utf-8'}, function (err, data) {
if (err) {
if (err.code === 'ENOENT') {
message = 'File is not exist';
} else {
message = 'An error occurred when reading file';
console.log(e.message);
}
} else {
message = data;
}
res.writeHead(200, { 'content-type' : 'text/xml'});
res.end(message);
});
}).listen(5025, '192.168.1.102');
console.log('Server port 5025 is start listening..');
①:Adobe在2011年11月份已将Flex框架捐赠给Apache基金会