在HTTP協議下使用JSON在flash和C# web程式之間通訊

wl1121發表於2009-05-19

先把文章中要用到一些擴充套件程式集貼出來。

C#用到了System.Web.Extensions程式集,包括3個檔案:
System.Web.Extensions.dll
System.Web.Extensions.Design.dll
AJAXExtensionsToolbox.dll

Flash用到了adobe的corelib-.90程式集:
corelib.swc

網上現在有很多將flash與C# web 服務程式通訊的資料,不過很多都是用remoting或者web service的方式做的,而且很多高手還做了效率比較,結果是基於TCP協議的remoting效率最好,但是這裡對remoting就不多說了,並且基於TCP的remoting因為協議不同效率差別肯定明顯。我還是介紹在HTTP協議下的通訊方法吧。

這裡使用iis作為伺服器,服務程式只要能處理客戶端提交的資料,能向客戶端返回資料就行(當然push做不到,http協議嘛)。所以處理程式大多直接實現IHttpHandle介面,當然從Page繼承是一點也沒有問題的,因為Page類也實現了IHttpHandle介面,如何繼承Page,如何實現IHttpHandle的介面方法就給個例子吧,這個資料挺多的。

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.SessionState;

namespace com.Flight.Script
{
    /// <summary>
    /// 指令碼控制請求,IRequiresSessionState介面沒有啥方法,純粹為了session支援
    /// </summary>
    public class ScriptHandler : IHttpHandler, IRequiresSessionState
    {
        /// <summary>
        /// 處理指令碼請求
        /// </summary>
        /// <param name="context">指令碼名稱</param>
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            context.Response.Write("輸出內容");
        }

        /// <summary>
        /// 是否允許後續請求處理
        /// </summary>
        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }
}

這裡提一下web.config配置,下面是一個例子,在這裡例子裡面我修改了副檔名“.do”,這樣的配置要求修改iis伺服器設定的,如果沒有自己的伺服器最好別這樣做,比較麻煩,可以使用“ashx”或者“axd”都可以。

<httpHandlers>
  <add verb="*" path="chat.do" type="com.gs.chat.Chat, Game"/>
</httpHandlers>

下面是伺服器上將物件進行JSON序列化的程式,其實JSON序列化就是將一個物件轉換成有規律的字串,能夠被別人解讀出來,但不是所有的物件都能被序列化,一定要實現了ISerializable介面的類才行,可以參考Hashtable類,這個類就實現了ISerializable介面中的GetObjectData方法,因此這個型別的物件進行反序列化是沒有問題的。

using ...
using System.Web.Script.Serialization;
using ...

...
/// <summary>
/// 向客戶端返回的資料
/// 預設採用json編碼格式
/// </summary>
/// <param name="_result">伺服器方法的方會物件</param>
/// <returns>返回字串</returns>
public virtual string JsonResult(object _result)
{
    //返回的字串
    string resultString = "";
    //採用了微軟的序列化方式
    JavaScriptSerializer jss = new JavaScriptSerializer();
    resultString = jss.Serialize(_result);
    return resultString;
}

/// <summary>
/// 反序列化,從JSON字串轉換到物件
/// </summary>
/// <param name="_jsonstring">JSON串</param>
/// <returns>反序列化得到的物件</returns>
public virtual object JsonObject(string _jsonstring)
{
    JavaScriptSerializer jss = new JavaScriptSerializer();
    return jss.DeserializeObject(_jsonstring);
}
...

輸出都是一樣的,直接Write:

context.Response.Write(_text);

這裡對於伺服器的開發方法可以很多,比如在同一個IHttpHandle中可以寫多個處理程式,分別處理不同的請求,然後有個專門的類負責分析要執行哪個方法。下面是傳送和接收聊天訊息的方法,供參考:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using com.Flight.Web.Ajax;
using System.Text.RegularExpressions;

namespace com.gs.chat
{
    public class Chat : AjaxController
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            AjaxJsBuilder.RegistType(typeof(Chat));
            StartProcess();
        }

        [SafeMethod("A")]
        [AjaxMethod()]
        public Hashtable SendMessage(string _msg)
        {
            string patternForCmd = @"-\|cmd:[a-zA-z]+\|-";
            Regex rg = new Regex(patternForCmd, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            Match m = rg.Match(_msg);

            if (m.Success)
            {
                string cmd = m.Value;
                if (cmd.ToLower().Equals("-|cmd:clear|-"))
                {
                    MessageContainer.MsgContainer.Clear();
                }
            }
            else
            {
                Message msg = new Message("me", "all", _msg, ToType.ALL);
                MessageContainer.MsgContainer.Add(msg);
            }
            Hashtable rht = new Hashtable();
            rht.Add("stat", "OK");
            rht.Add("msg", "傳送成功。");
            return rht;
        }

        [SafeMethod("A")]
        [AjaxMethod()]
        public MessageContainer ReceiveMessage(string _u)
        {
            MessageContainer mc = new MessageContainer();
            foreach (Message msg in MessageContainer.MsgContainer)
            {
                if (msg.From.Equals(_u) || msg.To.Equals(_u))
                {
                    mc.Add(msg);
                }
            }

            return mc;
        }
    }
}

AjaxController這個類是自己寫的,從page繼承,用來做一些基本處理,比如JSON序列化啊,反序列化啊,安全檢查啊,還有分析客戶端要呼叫的方法之類,後面也會提到一些裡面的具體設計。

下面看看客戶端的開發,首先是向伺服器傳送請求,這個比較簡單,直接貼一個簡單例子,應該都明白

import flash.net.URLLoader;
import flash.net.URLRequest;

var request:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
var request_data:String = ajax_flag + "&" + method_flag + method + "&" + parameters;
request.data = request_data;
request.method = URLRequestMethod.POST;
loader.load(request);

主要是在loader的complete事件中處理的時候要注意把伺服器返回的字串進行JSON反序列化,轉換成物件,程式碼如下:

import com.adobe.serialization.json.JSON; //匯入使用的到類

在load_complete事件裡面寫如下程式碼:

//從事件物件想讀取伺服器返回的資料,反序列化為物件
var receiveMsg:Object = JSON.decode(event.target.data);

獲取receiveMsg物件之後就可以通過點操作符訪問屬性資料了。以一個HashTable為例,其中的Key就是反序列化後物件的屬性,Value就是屬性值。

在C#中能夠被直接序列化的容器物件很多,可以更具需要選擇,如果都不能滿足你的要求,也可以自己寫一個物件,實現ISerializable介面的GetObjectData方法進行資料傳輸。

按照上面的實現思路,如果一個IHttpHandle只能處理一種請求那麼我們就要寫出很多很多的IHttpHandle這樣就無法和webservice相提並論了,因為我們知道在webservice中一個service可以提供多個方法響應客戶端的請求,這個其實只要我們稍作處理就可以了,比如上面的例子裡面我們看到請求字串中包含了一些特殊的引數:

ajax_flag + "&" + method_flag + method

可以很容易看出來,這裡就是用來告訴伺服器我要呼叫哪些方法的引數。

注意:伺服器方法在響應請求之前還要做安全判斷,這個判斷很重要,否則隨便就能呼叫伺服器程式那豈不太危險?這裡給出一個簡單的解決方法,可以採用一個HttpModule做前端判斷,如果已經登入,請求引數中沒有特殊的符號(防止注入),並且有訪問這個HttpHandle的許可權那麼繼續,然後在HttpHandle中給指定的方法加個安全特性程式碼如下:

[SafeMethod("A")]
[AjaxMethod()]
public MessageContainer ReceiveMessage(string _u)
{
    MessageContainer mc = new MessageContainer();
    foreach (Message msg in MessageContainer.MsgContainer)
    {
        if (msg.From.Equals(_u) || msg.To.Equals(_u))
        {
            mc.Add(msg);
        }
    }

    return mc;
}

[AjaxMethod()]和[SafeMethod("A")]這兩個特性說明ReceiveMessage方法是允許客戶端呼叫的,並且使用者具有特性A的時候才能執行這個方法,否則報錯。

原文連結:http://bbs.actionscript3.cn/thread-23446-1-1.html

相關文章