Unity - HttpHelper框架类库

编者注

这几天一直在写Unity当中的C#的网络传输,但是发现由于Unity采用的mono框架,支持的仅仅是.Net Framework 3.5的API,并不支持更高级别的HttpClient,则很多功能需要手动开发。最早简单测试过GET数据,并且JSON文件反序列化为对象成功。之后实现了POST的JSON文件,获取返回的JSON,所以麻痹大意,导致在uploadfile这个环节花费了大量的时间。

设计方法

笔者认为apache commons的库设计非常好,所以仿照了httpclient进行了设计。

└─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不通过键值对,不过会以键值对的方式声明。)

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大小。
解决方法:

        // 先计算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规则,手误导致编写错误,并且没有看到,导致调试很久。

转载于:https://my.oschina.net/hava/blog/1558615

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值