Flex HTTPService使用与.NET服务端监听(node.js 友情客串)

一、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基金会

原文地址:https://www.cnblogs.com/hourglasser/p/3541644.html