博主在进行Cocos小游戏的开始时,想要实现http请求。简单的用node.js在本地搭了一个服务器后,查询Cocos的官方文档以及网上搜索,都没有发现官方封装的API。
博主之前从事原生微信小程序开发以及uniapp开发,使用惯了方便快捷的requestAPI,以为这个是很简单的一个东西。结果Cocos官方并没有封装过http请求!也是走了很多弯路,手动封装了一下http请求(每次都使用原生Js写法真的很让人抓狂),这里写出代码和具体思路,供诸位参考。
首先,Js原生http请求是使用XMLHttpRequest实现的。虽然这个东东以XML为前缀,但是其实也可以进行JSON格式数据的传输。
const xhr = new XMLHttpRequest();//创建XMLHttpRequest示例对象
xhr.setRequestHeader('Content-Type','application/json');//设置请求头,用来适配json格式数据传输
xhr.open('POST',url,true);//初始化请求,设置请求方式,请求地址,是否异步调用
xhr.send(JSON.stringfy(params));//发送请求参数(请求参数是跟在url后面的一串字符串,如?name=张三&age=18)
值得注意的是,参数其实是不支持中文字符的,但是为什么我们传入name=张三也是合法的呢?原因是绝大多数浏览器会自动将中文字符进行转码,弄成%开头的一堆数字。原本我们也是需要在脚本里面使用encodeURI这个函数进行转码的,但是这活被浏览器干了,倒也省心。
这四行代码就可以实现简单的请求了。但是我们还没有拿到请求数据。
xhr内置四个监听函数
xhr.onerror = ()=>{//处理错误逻辑}
xhr.onprogress = ()=>{//处理加载逻辑}
xhr.onloadend = ()=>{//处理请求结束逻辑}
xhr.ontimeout = ()=>{//处理超时逻辑}
除此之外,还有一个可以监听到所有事件的函数
xhr.onreadystatechange = ()=>{//在这里处理所有事件逻辑}
一般来说,我们使用xhr.onreadystatechange就够了。
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){
console.log(xhr.responseText)
}
}
readyState有几个可取值,分别代表不同状态,4就是加载完成的状态。status是请求的状态码,通常200为请求成功。之后在responseText中获得返回的数据,如果是JSON格式数据,还可以再调用JSON.parse来转成js对象。
首先我们应该清楚为什么要实现封装。因为现有的请求太过于繁琐,需要在不同的地方设置很多参数。而事实上我们需要的是什么,我们只需要上传信息,然后拿到信息就可以了。而且,如果想要区别请求的状态,就需要利用到xhr的四个监听函数用来细分。与此同时,我们还需要建立错误反馈机制,用来方便之后调试。所以我们的思路是,进一步的封装应该要在利用xhr的多个监听函数的基础上,并且能够实现一次传参直接获得消息。
这方面也不能忽略的是,网络请求是需要较长的响应时间的。如果在网络条件比较差或者传输数据量比较大的情况下,使用同步请求会阻塞主线程,导致之后的程序无法运行。所以应该设计成异步函数。
export class Http {
public static get(url, callback, thisObj) {
var xhr = new XMLHttpRequest();
xhr.onerror = function () {
callback.call(thisObj, "ERROR", xhr)
}
xhr.onprogress = function () {
callback.call(thisObj, "PROGRESS", xhr)
}
xhr.onloadend = function () {
callback.call(thisObj, "COMPLETE", xhr)
}
xhr.ontimeout = function () {
callback.call(thisObj, "TIMEOUT", xhr)
}
xhr.open("GET", url, true);//这里的true意思是允许异步请求
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send();//这里不写参数的原因是,参数也可以直接放在url里,我们在进一步封装之后,直接写到url中
}
}
首先进行简单封装,设置请求方式和数据格式。这个模块只能实现最简单的请求功能。
httpSend(subUrl, params, callback) {
var param = this.obj_contact(params);
var url = this.baseUrl + '/' + subUrl + param;
Http.get(url, function (eventName: string, xhr: XMLHttpRequest) {
if (eventName == 'COMPLETE') {
if (xhr.readyState == 4 && (xhr.status >= 200 && xhr.status < 400)) {
var response = JSON.parse(xhr.responseText)
callback && callback(response);
} else {
console.log('服务器维护中');
}
} else if (eventName == 'TIMEOUT') {
console.log("连接网关超时")
} else if (eventName == 'ERROR') {
console.log("连接网关发生错误")
}
}, this);
}
接着进行第二步封装,这步封装中我们利用了回调函数。第一步封装我们传入回调函数两个参数,分别为请求状态字符串和xhr实例对象。返回的数据储存在xhr实例对象内,所以必须传入,而请求状态字符串可以提示当前的请求状态,也需要传入。这里说明一下baseUrl就是所请求网站的域名,subUrl就是该网站域名下的文件路径。然后通过自己写的一个函数obj_contact来将js对象转化为?key1=value1&key2=value2...的格式,加到完整域名的后面。
这里写出obj_contact的具体实现
public obj_contact(obj) {
var s = "";
for (var k in obj) {
let v = obj[k];
if (s.length == 0) {
s += "?" + k + "=" + v;
} else {
s += "&" + k + "=" + v;
}
}
return s;
}
一个很简单的函数。
写到这里,我们发现还没有拿到服务器返回的消息。其实可以两步解决的,在第一步封装的回调函数里我们就可以把xhr.responseText当做参数返回的。但是我们的函数体会显得很臃肿,因为第二步封装里的函数还需要执行请求状态的判断,所以我们进行第三步封装。
var params = {"name":"张三","id":"01","age":"18"};
var subUrl = "test/demo";
httpGet(subUrl, params, (res) => {
if (res.code == 200) {
if (res.data) {
this.data = res.data
}
})
现在我们的函数看上去就很简洁了,只需要传入两个信息就能得到数据了,同样,Post请求也可以这样封装。