Flex通信篇——构建企业级HTTP通信层

彭博 发布于 2012/03/09 12:24
阅读 201
收藏 0

概述

RIA和SOA是一对绝配。SOA强调把业务以接口方式向外界提供不关注前端的呈现,而RIA则强调用户体现,结合两者优势能够设计出用户体现良好、灵活的、易扩展、易集成的系统。要处理好RIA前端和SOA后端,需要搭建一个健壮的企业级通信层,该层职责:

  • 负责处理RIA前端和SOA后端的数据交互。
  • 封装SOA业务接口,便于开发调用。
  • 采用异步通信方式,SOA业务接口请求返回时进行回调。
  • SOA业务接口调用错误处理机制。

SOA业务接口的设计

  • 不使用webservice,采用自定义数据格式,数据简单些。
  • 针对Flex,返回格式采用xml数据格式方便写。
  • 安全策略:用户调用登录接口后返回登录信息,其中还回信息中包含令牌(session),每次调用登录以外的业务接口都必须传入令牌参数作验证。同一用户,每次登录令牌值都会刷新,即:最后登录的获得使用权。
  • 大部分业务接口采用GET方式调用,少数采用POST方式,后台应该统一处理GET,POST参数。

SOA通信格式的设计

业务接口调用成功返回格式(XML)

<?xml version="1.0" encoding="utf-8">

<rsp>

{业务所需的XML节点信息}

</rsp>

业务接口调用失败返回格式(XML)

<?xml version="1.0" encoding="utf-8">

<error_rsp>

<code>{错误码}</code>

<msg>{错误描述}</msg>

</error_rsp>

业务接口常用参数

  • v:version,业务接口版本号,用于版本控制,同一接口根据不同版本号可以有不同的实现逻辑。
  • f:format,返回格式,指定返回信息的格式,常用的有xml,json,Flex使用xml方便些。
  • api:业务接口名称,用于区分接口类型。
  • t:时间戳,用于解决客户端浏览器缓存问题,服务端不处理该参数。
  • session:令牌/会话码,除了登录接口,其他接口一般都需要这个参数。

调用例子

  • 登录成功
    • Request: http://server1:2009/LCMS2/api.jhtm?api=lcms.user.login&v=2.0&f=xml&uname=Hunk&pwd=123
    • Reponse:

      <?xml version="1.0" encoding="UTF-8" ?>

      <rsp>

      <uid>51</uid>

      <urole>4</urole>

      <session>cbff8aca-8b56-48d7-a740-534f3a84e575</session>

      </rsp>

  • 登录失败
    • Request: http://server1:2009/LCMS2/api.jhtm?api=lcms.user.login&v=2.0&f=xml&uname=User1&pwd=123
    • Reponse:

      <?xml version="1.0" encoding="UTF-8" ?>

      <error_rsp>

      <code>500</code>

      <msg>用户不存在</msg>

      </error_rsp>

  • 获取用户列表
    • Request: http://server1:2009/LCMS2/api.jhtm?api=lcms.user.get&v=2.0&f=xml&session=cbff8aca-8b56-48d7-a740-534f3a84e575&page_no=1&page_size=10
    • Reponse:totalResults是不分页时的查找总数量,返回第一页的item(用户),每页10行

      <?xml version="1.0" encoding="UTF-8" ?>

      <rsp>

      <totalResults>37</totalResults>

      <item><uid>51</uid><uname>hunk</uname><urole>4</urole></item>

      <item><uid>48</uid><uname>hunk4</uname><urole>3</urole></item>

      </rsp>

  • 删除用户
    • Request: http://server1:2009/LCMS2/api.jhtm?api=lcms.user.delete&v=2.0&f=xml&session=cbff8aca-8b56-48d7-a740-534f3a84e575&del_uid=36
    • Reponse:

      <?xml version="1.0" encoding="UTF-8" ?>

      <rsp>

      <true />

      </rsp>

HttpRequest类

该类用于封装请求参数,方便调用,声明为internal,只在包内使用,代码如下:

package cwn.lcms

{

    import mx.collections.ArrayCollection;

    internal class HttpRequest

    {

        //用于存放Get参数

        private var _GetParams:ArrayCollection = new ArrayCollection();

        //用于存放Post参数

        private var _PostParams:ArrayCollection = new ArrayCollection();

        //根路径

        public var RootUrl:String;

        private var _Disposed:Boolean = false;

        public function HttpRequest(root:String = "")

        {

            RootUrl = root;

        }

        //需要对参数进行url加密

        private function Encode(value:String):String

        {

            return encodeURIComponent(value);

        }

        public function AddGetParam(name:String, value:String):void

        {

            _GetParams.addItem(name + "=" + Encode(value));

        }

        public function AddPostParam(name:String, value:String):void

        {

            _PostParams.addItem(name + "=" + Encode(value));

        }

        public function GetUrl():String

        {

            var url:String = RootUrl;

            if (url.lastIndexOf("?") < 0)

                url += "?";

            for (var i:int = 0; i < _GetParams.length; i++)

            {

                url += _GetParams[i] + "&";

            }

            if (_GetParams.length > 0)

                url = url.substr(0, url.length - 1);

            return url;

        }

        public function get HasPostData():Boolean

        {

            return _PostParams.length > 0;

        }

        public function GetPostData():String

        {

            if (!HasPostData)

                return null;

            var data:String = "";

            for (var i:int = 0; i < _PostParams.length; i++)

            {

                data += _PostParams[i] + "&";

            }

            data = data.substr(0, data.length - 1);

            return data;

        }

        public function Dispose():void

        {

            if (_Disposed)

                return;

            _Disposed = true;

            _GetParams.removeAll();

            _PostParams.removeAll();

            _GetParams = null;

            _PostParams = null;

        }

    }

}

各种数据类

定义各种业务接口相关的数据类,把XML节点转成Object,利用IDE智能感知,方便编码,以下是UserData类的定义。

package cwn.lcms.data

{

    public class UserData

    {

        public var Id:String = "";

        public var Name:String = "";

        public var Password:String = "";

        public var Session:String = "";

        public var Role:int = 0;

        public function UserData(xml:XML = null)

        {

            FromXml(xml);

        }

        public function FromXml(xml:XML):void

        {

            if (xml == null)

                return;

            Id = xml.uid;

            Name = xml.uname;

            Role = xml.urole;

            Session = xml.session;

        }

        public function Clone():UserData

        {

            var data:UserData = new UserData();

            data.Id = Id;

            data.Name = Name;

            data.Password = Password;

            data.Session = Session;

            data.Role = Role;

            return data;

        }

    }

}

WebAPI类

该类是通信层最核心的类,以静态属性、方法为主,先定义一些主要的属性和工具方法,如下:

package cwn.lcms

{

    import cwn.lcms.data.UserData;

    import flash.events.Event;

    import flash.events.IOErrorEvent;

    import flash.events.SecurityErrorEvent;

    import flash.external.ExternalInterface;

    import flash.net.URLLoader;

    import flash.net.URLRequest;

    import flash.net.URLVariables;

    import mx.collections.ArrayCollection;

    public final class WebAPI

    {

        private static var _Debug:Boolean = false; //调试控制

        private static var _HttpCache:ArrayCollection = new ArrayCollection(); //存放http请求        

        public static var Url:String = null; //根路径    

        public static var Session:String = null; //会话码

        public static var SessionErrorFunction:Function; //会话错误回调方法

        public static var CurrentUser:UserData = null; //当前登录用户数据

        private static function RemoveCache(value:Object):void

        {

            var i:int = _HttpCache.getItemIndex(value);

            if (i >= 0)

            {

                var loader:URLLoader = _HttpCache.removeItemAt(i) as URLLoader;

                if (loader != null)

                {

                    loader.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorEventHandler);

                    loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, SecurityErrorHandler);

                }

            }

        }

        //执行HTTP请求并缓存请求,callbackXml:Function(result:XML):void

        private static function Invoke(http:HttpRequest, callbackXml:Function = null):void

        {

            if (_Debug)

            {

                trace(http.GetUrl());

            }

            var loader:URLLoader = new URLLoader();

            _HttpCache.addItem(loader);

            loader.addEventListener(IOErrorEvent.IO_ERROR, IOErrorEventHandler);

            loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, SecurityErrorHandler);

            loader.addEventListener(Event.COMPLETE, function(event:Event):void

                {

                    OnInvokeCallback(loader.data, callbackXml);

                    RemoveCache(loader);

                });

            var request:URLRequest = new URLRequest(http.GetUrl());

            request.method = "GET";

            if (http.HasPostData) //当有Post参数时,改用POST方式请求

            {

                request.method = "POST";

                request.data = new URLVariables(http.GetPostData());

            }

            loader.load(request);

        }

        //HTTP请求成功回调,callbackXml:Function(result:XML):void

        private static function OnInvokeCallback(response:Object, callbackXml:Function = null):void

        {

            try

            {

                if (_Debug)

                {

                    trace("rsp:" + response);

                }

                var result:XML = XML(response);

                if (result.localName() == "rsp") //调用业务接口成功

                {

                    if (callbackXml != null)

                        callbackXml(result);

                }

                else if (result.localName() == "error_rsp") //调用业务接口错误

                {

                    if (int(result.code) == 8 && SessionErrorFunction != null) //session错误处理

                    {

                        SessionErrorFunction();

                        LcmsError.ShowInfo("该用户已在其他机器上登录,请重新登录。");

                    }

                    else

                        LcmsError.ShowError(result); //输出错误信息

                    if (callbackXml != null)

                        callbackXml(null);

                }

                else

                    LcmsError.ShowInfo("[数据格式错误]/r/n" + response); //输出错误信息

            }

            catch (e:Error)

            {

                LcmsError.ShowInfo("[未知异常]/r/n" + e.message + "/r/n" + e.getStackTrace()); //输出错误信息

            }

        }

        //HTTP请求错误处理

        private static function SecurityErrorHandler(event:SecurityErrorEvent):void

        {

            LcmsError.ShowInfo("[无访问权限]/r/n" + event.text); //输出错误信息

            RemoveCache(event.currentTarget);

        }

        //HTTP请求错误处理

        private static function IOErrorEventHandler(event:IOErrorEvent):void

        {

            LcmsError.ShowInfo("[服务器连接失败]/r/n" + event.text); //输出错误信息

            RemoveCache(event.currentTarget);

        }

        //创建HTTP请求,并设置必须的参数

        private static function CreateRequest(api:String, version:String = "2.0", format:String = "xml"):HttpRequest

        {

            var http:HttpRequest = new HttpRequest();

            http.RootUrl = Url;

            http.AddGetParam("api", api);

            http.AddGetParam("v", version);

            http.AddGetParam("f", format);

            var date:Date = new Date();

            http.AddGetParam("t", date.time.toString());

            return http;

        }

    }

}

然后,就可以根据业务需要封装所需的接口,下面简单介绍几个接口。

  • 登录

        public static function Login(name:String, password:String, callbackXml:Function = null):void

        {

            var http:HttpRequest = CreateRequest("lcms.user.login");

            http.AddGetParam("uname", name);

            http.AddGetParam("pwd", password);

            Invoke(http, callbackXml);

            http.Dispose();

        }

  • 获取用户列表

        public static function GetUser(name:String = null, role:String = null, pageNO:String = "1", pageSize:String = "25", callbackXml:Function = null):void

        {

            var http:HttpRequest = CreateRequest("lcms.user.get");

            http.AddGetParam("session", Session);

            if (name != null)

                http.AddGetParam("uname", name);

            if (role != null)

                http.AddGetParam("urole", role);

            if (pageNO != null)

                http.AddGetParam("page_no", pageNO);

            if (pageSize != null)

                http.AddGetParam("page_size", pageSize);

            Invoke(http, callbackXml);

            http.Dispose();

        }

  • 删除用户

        public static function DeleteUser(id:String, callbackXml:Function = null):void

        {

            var http:HttpRequest = CreateRequest("lcms.user.delete");

            http.AddGetParam("session", Session);

            http.AddGetParam("del_uid", id);

            Invoke(http, callbackXml);

            http.Dispose();

        }

使用WebAPI

  • WebAPI初始化设置

        private function SessionError():void

        {

            //跳转到登陆

        }

        cwn.lcms.WebAPI.Url = "http://server1:2009/LCMS2/api.jhtm";

        cwn.lcms.WebAPI.SessionErrorFunction = SessionError;

        cwn.lcms.WebAPI.Session = null;

        cwn.lcms.WebAPI.CurrentUser = null;

  • 登录

        private function LoginCallback(xml:Object):void

        {

            //…

            cwn.lcms.WebAPI.Session = null;

            cwn.lcms.WebAPI.CurrentUser = null;

            if (xml == null)

                return;

            var user:UserData = new UserData(XML(xml));

            //…

            cwn.lcms.WebAPI.CurrentUser = user;

            if (user.Session != "")

                cwn.lcms.WebAPI.Session = user.Session;

            //登陆成功跳转

        }

        cwn.lcms.WebAPI.Login("Hunk", "123", LoginCallback);

  • 获取用户列表

        function GetUserCallback(xml:Object):void

        {

            if (xml == null)

                return;

            _Data.removeAll();//ArrayCollection

            for each (var item:XML in xml.item)

            {

                var user:UserData = new UserData(item);

                _Data.addItem(user);

            }

            //数据绑定

        }

        cwn.lcms.WebAPI.GetUser(null, null, "1", "25", GetUserCallback);

 

注:设计不唯一,上述是已实践的个案参考。

系列索引

Flex通信篇——Flex和外部应用程序进行通信

Flex通信篇——Flex和外部进行异步通信

Flex通信篇——Flex键盘组合键

Flex通信篇——构建企业级HTTP通信层

后记

写Blog有一段时间,终于能完成一个完整系列,即使略带稚气,也要好好表彰下自己:-)。


原文链接:http://blog.csdn.net/hunkcai/article/details/5403851
加载中
返回顶部
顶部