最近在做Discuz!NT论坛与网站整合的东西,于是便用到了Discuz提供的Discuz! Toolkit

看了看源码,应该说这是个不错的工具库,提供了关于注册、登录、 文章、积分等论坛操作的一篮子功能,而且配备了对应的Wiki

只可惜,Discuz!NT终归是异构的系统,响应速度和突发异常并非如自己的代码一样可控,使用同步方式调用API就显得有那么些不智了关于异步调用Discuz!NT接口

好在Toolkit是开源的,可以DIY,看看它提供的同步访问Web Service方法:

string postData)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(apiUrl);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = postData.Length;
            request.Timeout = 20000;

            HttpWebResponse response = null;

            try
            {
                StreamWriter swRequestWriter = new StreamWriter(request.GetRequestStream());
                swRequestWriter.Write(postData);
                if (swRequestWriter != null)
                    swRequestWriter.Close();

                response = (HttpWebResponse)request.GetResponse();
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    return Encoding.UTF8.GetBytes(reader.ReadToEnd());
                }
            }
            finally
            {
                if (response != null)
                    response.Close();
            }
        }

 

要进行异步的调用,原本的同步流程会被切成两部分:

1、调用开始等待响应

2、响应触发调用完成。

演化成语言:一个方法以委托的方式,在这两部分之间传递,第1部分会将方法指针(委托)塞入请求里,在流程执行到第2部分时,方法被取出回调。

 

先设计委托原型:

public delegate void GetResponseGeneric<T>(T objects);
异步请求数据结构:
class RequestState
     { 
         public HttpWebRequest request;
         public HttpWebResponse response;       
      }

扩展一下Util工具类,加上异步调用接口方法:

/// <summary>
        /// 异步获取响应开始
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fun">泛型回调方法</param>
        /// <param name="method_name"></param>
        /// <param name="parameters"></param>
        #region void GetResponseBegin<T>(GetResponseGeneric<T> fun,string method_name, params DiscuzParam[] parameters)
        public void GetResponseBegin<T>(GetResponseGeneric<T> fun,string method_name, params DiscuzParam[] parameters)
        {
            DiscuzParam[] signed = Sign(method_name, parameters);

            StringBuilder builder = new StringBuilder();

            for (int i = 0; i < signed.Length; i++)
            {
                if (i > 0)
                    builder.Append("&");

                builder.Append(signed[i].ToEncodedString());
            }
            //异步获取字节流
            GetResponseBytesBegin<T>(fun,Url, method_name, builder.ToString());           
        }
        #endregion

        /// <summary>
        /// 异步获取响应完成
        /// </summary>
        #region void GetReponseEnd<T>(GetResponseGeneric<T> fun,byte[] response_bytes)
        public void GetReponseEnd<T>(GetResponseGeneric<T> fun, byte[] response_bytes)
        {
            XmlSerializer response_serializer = GetSerializer(typeof(T));
            try
            {
                T response = (T)response_serializer.Deserialize(new MemoryStream(response_bytes));
                //回调 调用方 接收方法
                fun(response);
            }
            catch
            {
                Error error = (Error)ErrorSerializer.Deserialize(new MemoryStream(response_bytes));
                throw new DiscuzException(error.ErrorCode, error.ErrorMsg);
            }
        }
        #endregion

        /// <summary>
        /// 异步获取字节流开始
        /// </summary>
        /// <param name="callback">回调方法</param>
        /// <param name="apiUrl"></param>
        /// <param name="method_name"></param>
        /// <param name="postData"></param>
        #region void GetResponseBytesBegin<T>(GetResponseGeneric<T> fun,string apiUrl, string method_name, string postData)
        private void GetResponseBytesBegin<T>(GetResponseGeneric<T> fun, string apiUrl, string method_name, string postData)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(apiUrl);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = postData.Length;
            request.Timeout = 20000;

            StreamWriter swRequestWriter = new StreamWriter(request.GetRequestStream());
            swRequestWriter.Write(postData);
            if (swRequestWriter != null)
                swRequestWriter.Close();

            RequestState myRequestState = new RequestState();
            myRequestState.request = request;

            //异步调用
            request.BeginGetResponse(ar => GetResponseBytesEnd<T>(fun, ar), myRequestState);                   
        }
        #endregion

        /// <summary>
        /// 异步获取字节流完成
        /// </summary>
        /// <param name="asynchronousResult"></param>
        #region void GetResponseBytesEnd<T>(GetResponseGeneric<T> fun,IAsyncResult asynchronousResult)
        private void GetResponseBytesEnd<T>(GetResponseGeneric<T> fun, IAsyncResult asynchronousResult)
        {
            RequestState myRequestState = (RequestState)asynchronousResult.AsyncState;
            HttpWebRequest myHttpWebRequest = myRequestState.request;
            try
            {
                //接收完成
                myRequestState.response = (HttpWebResponse)myHttpWebRequest.EndGetResponse(asynchronousResult);

                using (StreamReader reader = new StreamReader(myRequestState.response.GetResponseStream(), Encoding.UTF8))
                {
                    byte[] response_bytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
                    //回调 异步获取响应完成 方法
                    GetReponseEnd<T>(fun, response_bytes);
                }
            }
            catch (WebException ex)
            {
                DiscuzLogger.WriteLog(typeof(Util), ex);
            }
            finally
            {
                if (myRequestState.response != null)
                    myRequestState.response.Close();
            }
        }
        #endregion
至此,与Discuz!NT的异步交互功能提供完成,怎么使用呢?关于异步调用Discuz!NT接口
假设需要异步获取某一版块指定数量的贴子,可以在DiscuzSession添加一个这样的方法:
/// <summary>
        /// 异步 获取主题列表 开始
        /// </summary>
        /// <param name="fid"></param>
        /// <param name="page_size"></param>
        /// <param name="page_index"></param>
        /// <returns></returns>
        public void GetTopicListBegin(GetResponseGeneric<TopicGetListResponse> fun,
            int fid, int page_size, int page_index, string typeIdList)
        {
            List<DiscuzParam> param_list = new List<DiscuzParam>();
            if (session_info != null && !string.IsNullOrEmpty(session_info.SessionKey))
            {
                param_list.Add(DiscuzParam.Create("session_key", session_info.SessionKey));
            }

            param_list.Add(DiscuzParam.Create("type_id_list", typeIdList));
            param_list.Add(DiscuzParam.Create("fid", fid));
            param_list.Add(DiscuzParam.Create("page_size", page_size));
            param_list.Add(DiscuzParam.Create("page_index", page_index));

            //异步开始
            util.GetResponseBegin<TopicGetListResponse>(fun, "topics.getList", param_list.ToArray());           
        }

调用方使用起来只需要定义好接收方法,剩下就是收数据(CacheObjectCollection.HomepageTopicList是一个缓存项的getter/setter):

/// <summary>
        /// 异步获取论坛指定板块的帖子列表开始
        /// </summary>
        /// <param name="fid">板块ID</param>
        /// <param name="pageSize">数量</param>
        /// <param name="pageIndex">索引</param>
        /// <param name="typeIdList">主题ID 以 , 为分隔符</param>
        #region static void HomepageTopicGetBegin(int fid, int pageSize, int pageIndex, string typeIdList)
        public static void HomepageTopicGetBegin(int fid, int pageSize, int pageIndex, string typeIdList)
        {
            if (CacheObjectConllection.HomepageTopicList == null)
            {
                try
                {
                    DiscuzSession session = GetSession();
                    session.GetTopicListBegin(HomepageTopicGetEnd, fid, pageSize, pageIndex, typeIdList);
                }
                catch (DiscuzException ex)
                {
                    Logger.WriteLog(typeof(DiscuzHelper),
                        string.Format("DISCUZ!NT异步获取论坛指定板块的帖子列表错误:\r\n{0}"
                        , ex.ToString()));
                }
            } 
        }
        #endregion

        /// <summary>
        /// 异步获取论坛指定板块的帖子列表完成
        /// </summary>
        #region static void HomepageTopicGetEnd(TopicGetListResponse result)
        public static void HomepageTopicGetEnd(TopicGetListResponse result)
        {
            CacheObjectConllection.HomepageTopicList = result;
        }
        #endregion

 

到此,异步调用框架完成,个人感觉很好。只是,需求变化成了生成静态页面,它便成了鸡肋……关于异步调用Discuz!NT接口

相关文章: