Unity - HttpHelper框架類庫

編者注

這幾天一直在寫Unity當中的C#的網絡傳輸,可是發現因爲Unity採用的mono框架,支持的僅僅是.Net Framework 3.5的API,並不支持更高級別的HttpClient,則不少功能須要手動開發。最先簡單測試過GET數據,而且JSON文件反序列化爲對象成功。以後實現了POST的JSON文件,獲取返回的JSON,因此麻痹大意,致使在uploadfile這個環節花費了大量的時間。apache

設計方法

筆者認爲apache commons的庫設計很是好,因此仿照了httpclient進行了設計。json

└─Utils
    ├─GlobalConfig
    │      UnityTemplate.cs - 用來存儲Unity項目規範結構
    │
    ├─HttpHelper
    │  │  HttpHelper.cs - 針對訪問http程序的主要方法
    │  │
    │  ├─http
    │  │      HttpEntity.cs - HTTP Body中的內容
    │  │      HttpHeader.cs - HTTP Headers
    │  │
    │  └─methods
    │          HttpGet.cs - Get方法
    │          HttpPost.cs - Post方法
    │          HttpRequestBase.cs - 公共繼承方法
    │
    └─JsonHelper - 另外的庫,用來處理Json序列化與反序列化

HttpHelper

根據HttpClient方法,因爲已經可以確認.net core開源版本已經集成HttpClient,若是將來Unity何時集成core,則不須要考慮如今的方法,則把如今的方法命名爲HttpHelper
HttpHelper主要方法爲execute,模仿httpclient,參數是存入的get或者post內容。因爲返回內容不肯定是鍵值對仍是json,則直接返回HttpWebResponse瀏覽器

public static HttpWebResponse execute(HttpRequestBase methods)

HttpHeader

羅列了簡單的http header信息,並設置默認值,方便使用。網絡

public class HttpHeader
{
    // 默認值通過http編碼
    public string ContentType = "application/x- www-form-urlencoded";

    public string UserAgent = "Unity/HttpHelper";

    public long ContentLength;

    public System.Net.IWebProxy Proxy = null;

    public bool KeepAlive = true;
}

HttpEntity

因爲C#只可以經過ArrayList或者詞典,對不定類型的對象進行存儲,因此這裏使用了ArrayList存儲有可能發生的多種類型。
因爲即使是多種類型,依舊是採用鍵值對的方式進行訪問(json不經過鍵值對,不過會以鍵值對的方式聲明。)app

using System.Collections;
using System.Collections.Generic;
using System.IO;

public class HttpEntity
{
    private ArrayList entitys;

    public HttpEntity()
    {
        this.entitys = new ArrayList();
    }

    public void setKeyValuePair(KeyValuePair<string, long> keyValuePair)
    {
        this.entitys.Add(keyValuePair);
    }

    public void setKeyValuePair(KeyValuePair<string,int> keyValuePair)
    {
        this.entitys.Add(keyValuePair);
    }

    public void setKeyValuePair(KeyValuePair<string,string> keyValuePair)
    {
        this.entitys.Add(keyValuePair);
    }

    public void setKeyValuePair(KeyValuePair<string,FileInfo> keyValuePair)
    {
        this.entitys.Add(keyValuePair);
    }

    public ArrayList getHttpElements()
    {
        return this.entitys;
    }

    public long getContentLength()
    {
        return -1;
    }

    public bool hasFile()
    {
        foreach(object item in this.entitys)
        {
            if (item is KeyValuePair<string, FileInfo>)
            {
                return true;
            }
        }

        return false;
    }
}

分隔符的生成

根據http的需求,須要生成分隔符,可是根據瀏覽器的建議,不會真正生成一個和文件內容毫無關係的字符串做爲分隔符。這樣作的緣由是要花費大量的時間去解決分隔符衝突的問題。因此通用作法是隨機找一個儘量複雜的分隔符下降重複的機率。
做者以guid的方式生成分隔符,使分隔符更復雜。框架

// 生成分隔符
        string boundary = Guid.NewGuid().ToString("N");

文件傳送的規則

KeyValuePair<string, FileInfo> kv = (KeyValuePair<string, FileInfo>)item;

                // HTTP規定,起始的分割線
                string fileboundary = "--" + boundary + "\r\n";
                httpbody_length += getLength(fileboundary);

                // HTTP規定,Content-Disposition
                string ContentDisposition = "Content-Disposition: form-data; name=\"" + kv.Key + "\"; filename=\"" + kv.Value.Name + "\"\r\n";
                httpbody_length += getLength(ContentDisposition);

                // HTTP規定,ContentType
                string ContentType = "Content-Type: application/octet-stream" + "\r\n";
                httpbody_length += getLength(ContentType);

                // HTTP規定,ContentTransferEncoding
                string ContentTransferEncoding = "ContentTransferEncoding:binary" + "\r\n";
                httpbody_length += getLength(ContentTransferEncoding);

                // 區分文件頭與文件內容
                string separator = "\r\n";
                httpbody_length += getLength(separator);

                // 獲取文件長度
                FileStream fileStream = new FileStream(kv.Value.FullName, FileMode.Open, FileAccess.Read);
                httpbody_length += fileStream.Length;
                fileStream.Close();

                // 再次添加separator
                httpbody_length += getLength(separator);

碰到的坑

HttpWebRequest.ContentLength

HttpWebRequest必須先設置ContentLength這個參數,而後纔可以經過GetRequestStream獲取requestStream。
閱讀API,描述爲必須這樣,猜想實現是根據ContentLength對Stream進行初始化致使的。這種方式直接致使,沒法經過FileStream的大小來判斷最終的ContentLength大小。
解決方法:post

// 先計算body的長度,在寫入stream中
        request.ContentLength = httpbody_length;

        // get http body bytes
        Stream requestStream = request.GetRequestStream();

400 request bad

在Unity端報400 request bad,因爲客戶端的限制,實質是看不到Unity的實際報錯內容。則須要到服務端去查看錯誤內容。測試

Required CommonsMultipartFile parameter 'file' is not present

因爲須要手動編寫http的body規則,手誤致使編寫錯誤,而且沒有看到,致使調試好久。ui

相關文章
相關標籤/搜索