[NET] 如何從 Winform 移植到 Webform [自己搞定HTTP協議]


Winform 如何移植到 WebForm。

這個問題其實也就是要找一個好的方案,最大程度的復用現有的資源。

同時,WebForm的話,不一定要走ASPNET這條路,可以自己搞定HTTP協議的。

    下圖是Winform版

    

    下圖是Web版本

    

 

 

1。如何自己搞定HTTP協議

     WebForm,可以重頭老老實實的用ASPNET來做,不過,現在Oracle,MongoDB這樣的數據庫都支持本地的瀏覽器方式管理了,這樣的應用,不用安裝ASP,PHP這樣的服務器,完全自己實現一個簡單的HTTP服務器。

      OK,我們也實現一個吧,偵聽13000(這里隨便什么都可以的,不過推薦使用10000以上的端口號)端口,開啟多線程,做HTTP服務吧。

 

         public  void Start()
        {
            TcpListener server =  null;
             try
            {
                 //  Set the TcpListener on port 13000.
                Int32 port =  13000;
                IPAddress localAddr = IPAddress.Parse( " 127.0.0.1 ");

                 //  TcpListener server = new TcpListener(port);
                server =  new TcpListener(localAddr, port);

                 //  Start listening for client requests.
                server.Start();

                 //  Enter the listening loop.
                 while ( true)
                {
                     /// 對於每個請求創建一個線程,線程的參數是TcpClient對象
                    TcpClient client = server.AcceptTcpClient();
                    OutputLog( " [Init] " + DateTime.Now +  " Connected! "0);
                    ParameterizedThreadStart ParStart =  new ParameterizedThreadStart(ProcessFun);
                    Thread t =  new Thread(ParStart);
                    t.Start(client);
                }
            }
             catch (SocketException e)
            {
                OutputLog( " SocketException:  " + e,  0);
            }
             finally
            {
                 //  Stop listening for new clients.
                server.Stop();
                server =  null;
            }

        }

 

 

這里就是不停的啟動ProcessFun來響應客戶端(寫客戶端),同時用一個無限循環來偵聽請求。

  private  void ProcessFun( object clientObj)
        {
            TcpClient client = clientObj  as TcpClient;
             //  Buffer for reading data
            Byte[] bytes =  new Byte[ 512];

            OutputLog( " [Init]Waiting for a connection...  "0);

             //  Get a stream object for reading and writing
            NetworkStream stream = client.GetStream();
             int i;
             //  Loop to receive all the data sent by the client.
             while ((client.Available !=  0) && (i = stream.Read(bytes,  0, bytes.Length)) !=  0)
            {
                 //  Translate data bytes to a ASCII string.
                String data = System.Text.Encoding.ASCII.GetString(bytes,  0, i);
                 // OutputLog("Received:" + data);

                 string[] OrgRequest = data.Split(Environment.NewLine.ToCharArray());
                 if (OrgRequest[ 0].StartsWith( " GET "))
                {
                    String[] Request = OrgRequest[ 0].Split( "   ".ToCharArray());
                    String RequestItem = HttpUtility.UrlDecode(Request[ 1], System.Text.Encoding.UTF8);
                    OutputLog( " [RequestItem]Received :  " + RequestItem,  0);
                    String[] RequestPath = RequestItem.Split( " ? ".ToCharArray());
                     switch (RequestPath[ 0])
                    {
                         case  " / ":
                             // 根節點 
                            GETPage(stream, GetPage.ConnectionList());
                             break;
                         case  " /Connection ":
                            GETPage(stream, GetPage.Connection(RequestPath[ 1]));
                             break;
                         default:
                            GETFile(stream, RequestItem.Replace( " / "" \\ "));
                             break;
                    }
                }
                 else
                {
                     if (OrgRequest[ 0].StartsWith( " POST "))
                    {

                    }
                }
            }

             //  Shutdown and end connection
            client.Close();
        }

 這里響應請求,我只寫完了GET,POST還沒有開始動手呢。這里我們通過Stream.Read來獲取請求,然后根據請求內容來尋找資源,將資源返還給客戶端。

         ///   <summary>
        
///  
        
///   </summary>
        
///   <param name="stream"></param>
        
///   <param name="FileName"></param>
         private  void GETFile(NetworkStream stream, String FileName)
        {
             byte[] msg =  null;
             byte[] bFile =  new  byte[ 0];
            String data = String.Empty;
            Boolean IsFound =  false;
             if (File.Exists(ServerPath + FileName))
            {
                IsFound =  true;
                bFile = ReadFile(ServerPath + FileName);
            }
             else
            {
                 // 資源文件里面獲得
                 if (FileName.StartsWith( " \\MainTreeImage "))
                {
                     // MainTreeImage00.png -- 從MainTreeImage 里面獲得
                     int MainTreeImageIndex = Convert.ToInt32(FileName.Substring( " \\MainTreeImage ".Length,  2));
                    Image img = GetSystemIcon.MainTreeImage.Images[MainTreeImageIndex];
                    bFile = GetSystemIcon.imageToByteArray(img,ImageFormat.Png);
                    IsFound =  true;
                }
            }

             if (IsFound)
            {
                 //  Process the data sent by the client.
                data =  " HTTP/1.1 200 OK " + Environment.NewLine;
                 // if content-type is wrong,FF can;t render it,but IE can
                 string filetype = String.Empty;
                 switch ( new FileInfo(FileName).Extension)
                {
                     case  " .css ":
                        filetype =  " text/css ";
                         break;
                     case  " .js ":
                        filetype =  " text/javascript ";
                         break;
                     case  " .png ":
                        filetype =  " image ";
                         break;
                     default:
                         break;
                }
                data +=  " Content-Type: @filetype; charset=utf-8 ".Replace( " @filetype ", filetype) + Environment.NewLine;
                data +=  " Content-Length:  ";
                data += (bFile.Length).ToString();
                data += Environment.NewLine + Environment.NewLine;
                msg = System.Text.Encoding.ASCII.GetBytes(data);
                 //  Send back a response.
                stream.Write(msg,  0, msg.Length);
                stream.Write(bFile,  0, bFile.Length);
                OutputLog( " [System]Sent HTML OK "0);

            }
             else
            {
                data =  " HTTP/1.1 404 Not Found " + Environment.NewLine;
                msg = System.Text.Encoding.ASCII.GetBytes(data);
                 //  Send back a response.
                stream.Write(msg,  0, msg.Length);
                OutputLog( " [System]FileName Not Found: " + FileName,  0);
            }
            stream.Flush();

        }
    }

 將資源(文件)給客戶端,就是將資源轉換為Bytes字節流,然后寫入NetStream里面去。

 當然,為了“欺騙”瀏覽器,我們還要在成功找到資源的時候,給個200 OK的標記,如果沒有資源的話,給個404的標記。

 以后,還要考慮 緩存的問題,已經請求過的資源,就給個3XX的,不用再重復取得資源了。

 對於有些資源內容,這里干脆不從實體文件里面取了,直接從資源里面取了。客戶才不知道這個文件到底怎么來的。  

 

2。最大程度的復用代碼

    為了展示這個樹形結構,原來的Windows代碼非常的冗長(不是冗余)。

    如何將這個樹形Winform轉為WebForm?如果最大程度的復用代碼,這是必須要考慮的。樹形展示,我使用了zTree這個Jquery插件。只要能夠給他一個JSON的節點信息,就可以幫你自動完成樹形了。

    原來的代碼的目標是生成一個Treeview,現在的目標是做個JSON。當然,可以將原來的代碼里面的TreeNode的構成邏輯化為BsonDocument(MongoDB的概念,類似JSON)。不過,最最正確的做法是,直接將Treeview的結果轉換為JSON。不用修改任何原來的代碼,最大限度的使用原來的代碼。

 

#region"展示數據庫結構 WebForm"
         ///   <summary>
        
///  
        
///   </summary>
        
///   <param name="ConnectionName"></param>
        
///   <returns></returns>
         public  static String FillConnectionToJSON(String ConnectionName)
        {
            String strJSON = String.Empty;
            TreeView tree =  new TreeView();
            FillConnectionToTreeView(tree);
             // Transform Treeview To JSON
            
// 必須這樣做,防止二重管理的問題。如果這里的邏輯有兩套的話,維護起來比較麻煩。
            
// 一套邏輯,來控制樹的內容。然后將TreeView的內容轉換為JSON。
            
// 遞歸GetSubTreeNode
            strJSON = GetSubTreeNode(tree.Nodes[ 0]).ToJson(SystemManager.JsonWriterSettings);
             return strJSON;
        }
         ///   <summary>
        
///  
        
///   </summary>
        
///   <param name="SubNode"></param>
        
///   <returns></returns>
         private  static BsonDocument GetSubTreeNode(TreeNode SubNode)
        {
             if (SubNode.Nodes.Count ==  0)
            {
                BsonDocument SingleNode =  new BsonDocument();
                SingleNode.Add( " name ", SubNode.Text);
                SingleNode.Add( " icon "" MainTreeImage " + String.Format( " {0:00} ",SubNode.ImageIndex) +  " .png ");
                 return SingleNode;
            }
             else
            {
                BsonDocument MultiNode =  new BsonDocument();
                MultiNode.Add( " name ", SubNode.Text);
                BsonArray ChildrenList =  new BsonArray();
                 foreach (TreeNode item  in SubNode.Nodes)
                {
                    ChildrenList.Add(GetSubTreeNode(item));
                }
                MultiNode.Add( " children ", ChildrenList);
                MultiNode.Add( " icon "" MainTreeImage " + String.Format( " {0:00} ", SubNode.ImageIndex) +  " .png ");
                 return MultiNode;
            }
        }
         #endregion

     原來的FillConnectionToTreeView(tree);是一個很龐大的方法,用來構建一個Treeview,里面存放着數據庫的完整結構。

     這里我們將這個Treeview ,通過GetSubTreeNode 這個方法轉換為了BsonDocument,然后直接使用內置方法轉為JSON,一切搞定了。10來行代碼,非常優雅。

    通過這個例子,我想說,作為程序員,一定要時刻注意,不要出現同樣的代碼,不要雙重管理代碼,不要改了一個地方,另一個地方也必須改動。

 

    明天去 埃森哲 入職,上海埃森哲的朋友,多多關照阿。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM