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

相关文章
相关标签/搜索