1. 运行官网提供的案例
网络接口请求必不可少,是前端对接后端的必经途径。在华为官网HarmonyOS第一课从网络获取数据中列出的案例之《新闻加载数据》中,下载源码解压后,可看到如下所示的目录:
其中HttpServerOfNews 明显不是属于HarmonyOS开发包中的内容。将这个文件夹用其他编辑器打开(VScode、webstorm、Cursor等都可以),看下这个文件夹内的目录结构。
这个打开后,结构很了然。按照顺手的习惯,打开终端,按顺序执行以下步骤:
npm install
npm run start
执行成功后,在回到DevEco Studio关于新闻加载数据的项目上,将请求的service改为本地启动服务连接。
在src/main/ets/common/constant/CommonConstant.ets中找到
static readonly SERVER: string = 'http://**.**.**.**:9588';
将其改为
static readonly SERVER: string = 'http://localhost:9588';
或者改为本机的局域网Ip地址。上述写服务端的漏掉了在log中打印出本地的IP地址,我们可以打个log看看。在服务端项目中,打开根目录下app.js文件。将
app.listen(9588, () => {
const os = require('os');
const networkInterfaces = os.networkInterfaces();
let localIpAddress = '';
Object.keys(networkInterfaces).forEach((interfaceName) => {
const interfaces = networkInterfaces[interfaceName];
for (let i = 0; i < interfaces.length; i++) {
const iface = interfaces[i];
if (iface.family === 'IPv4' && !iface.internal) {
localIpAddress = iface.address;
break;
}
}
if (localIpAddress) return;
});
console.log(`本机局域网访问地址: http://${localIpAddress}:9588`);
console.log('服务器启动成功!');
});
保存后重新启动。
得到这个地址,可以替换上述的localhost地址。
然后打开entry/src/main/ets/pages/index.ets,点击右侧边栏Preview就能看到数据请求的结果啦。
如果想要在服务端更新数据,然后想使用自动服务重启服务器,就要先安装nodemon。
cnpm install nodemon -g
然后运行
npm run dev
2. 如何使用HTTP访问网络
2.1 在moudle.json5文件中配置网络访问权限
// module.json5
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:dependency_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
]
2.2 封装http请求方法
封装下网络请求方法对前端开发人员来说是非常熟悉的,封装http请求对统一的接口逻辑进行管理,设置统一的错误处理机制,有利于维护和修改,而且简化了调用的方式。所以这个步骤是必不可少的。
- 引入http模块,使用creteHttp返回一个HttpRequest对象,里面包括request、requestInStream、destroy、on和off方法。
import { http } from '@kit.NetworkKit';
let httpRequest = http.createHttp();
- 请求格式
httpRequest.request('EXAMPLE_URL',
{//可选参数},
(err: BusinessError, data: http.HttpResponse)=>{
}
)
填写HTTP请求的URL地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在可选参数中extraData中指定。
- 可选参数
-
method: http.RequestMethod.POST, // 可选,默认为http.RequestMethod.GET;当使用POST请求时此字段用于传递请求体内容,具体格式与服务端协商确定
-
extraData: 'data to send',//接口参数放置其中
-
expectDataType: http.HttpDataType.STRING, // 可选,指定返回数据的类型
-
usingCache: true, // 可选,默认为true
-
priority: 1, // 可选,默认为1
-
header: { 'Accept' : 'application/json' },// 开发者根据自身业务需要添加header字段
-
readTimeout: 60000, // 可选,默认为60000ms
-
connectTimeout: 60000, // 可选,默认为60000ms
-
usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定
-
usingProxy: false, //可选,默认不使用网络代理,自API 10开始支持该属性
-
caPath: '/path/to/cacert.pem', // 可选,默认使用系统预设CA证书,自API 10开始支持该属性
-
clientCert: { // 可选,默认不使用客户端证书,自API 11开始支持该属性
- certPath: '/path/to/client.pem', // 默认不使用客户端证书,自API 11开始支持该属性
- keyPath: '/path/to/client.key', // 若证书包含Key信息,传入空字符串,自API 11开始支持该属性
- certType: http.CertType.PEM, // 可选,默认使用PEM,自API 11开始支持该属性
- keyPassword: "passwordToKey" // 可选,输入key文件的密码,自API 11开始支持该属性
-
certificatePinning:[ ]//可选,支持证书锁定配置信息的动态设置,自API 12开始支持该属性
-
multiFormDataList:[ ] //可选,仅当Header中,'content-Type'为'multipart/form-data'时生效,自API 11开始支持该属性(常见表单文件上传、或者复杂嵌套数据场景等)
2.4 错误处理
做错误处理的时候,需要先引入对应的模块
import { BusinessError } from '@kit.BasicServicesKit'
httpRequest.request('EXAMPLE_URL',
{//可选参数},
(err: BusinessError, data: http.HttpResponse)=>{
if (!err) {
// data.result为HTTP响应内容,可根据业务需要进行解析
console.info('Result:' + JSON.stringify(data.result));
console.info('code:' + JSON.stringify(data.responseCode));
console.info('type:' + JSON.stringify(data.resultType));
// data.header为HTTP响应头,可根据业务需要进行解析
console.info('header:' + JSON.stringify(data.header));
console.info('cookies:' + JSON.stringify(data.cookies)); // 自API version 8开始支持cookie
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,开发者务必调用destroy方法主动销毁该JavaScript Object。
httpRequest.destroy();
} else {
console.info('error:' + JSON.stringify(err));
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,开发者务必调用destroy方法主动销毁该JavaScript Object。
httpRequest.destroy();
}
}
)
2.5 案例中的封装
// HttpUtil.ets
import { http } from '@kit.NetworkKit';
...
export function httpRequestGet(url: string): Promise<ResponseResult> {
let httpRequest = http.createHttp();
let responseResult = httpRequest.request(url, {
method: http.RequestMethod.GET,
readTimeout: 10000,
header: {
'Content-Type': ContentType.JSON
},
connectTimeout: 10000,
extraData: {}
});
let serverData: ResponseResult = new ResponseResult();
// Processes the data and returns.
return responseResult.then((value: http.HttpResponse) => {
if (value.responseCode === 200) {
// 获取返回数据
let result = `${value.result}`;
let resultJson: ResponseResult = JSON.parse(result);
if (resultJson.code === 'success') {
serverData.data = resultJson.data;
}
serverData.code = resultJson.code;
serverData.msg = resultJson.msg;
} else {
serverData.msg = `'网络请求失败,请稍后尝试!'&${value.responseCode}`;
}
return serverData;
}).catch(() => {
serverData.msg = '网络请求失败,请稍后尝试!';
return serverData;
})
}
2.6 使用封装好的请求
封装了http的请求后,就是在model里面使用,下面按照get请求得到返回数据的方法得到新闻类型列表的结果。
import { httpRequestGet } from '../common/utils/HttpUtil';
class NewsViewModel {
getNewsTypeList(): Promise<NewsTypeModel[]> {
return new Promise((resolve: Function, reject: Function) => {
let url = `${Const.SERVER}/${Const.GET_NEWS_TYPE}`;
httpRequestGet(url).then((data: ResponseResult) => {
if (data.code === Const.SERVER_CODE_SUCCESS) {
resolve(data.data);
} else {
reject(Const.TabBars_DEFAULT_NEWS_TYPES);
}
}).catch(() => {
reject(Const.TabBars_DEFAULT_NEWS_TYPES);
});
});
}
}
3. 总结
通过查阅官方API文档,在@ohos.net.http (数据请求)的开头就写了说明如下
本模块首批接口从API version 6开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 建议使用Remote Communication Kit进行HTTP请求,Remote Communication Kit将持续演进。
emmm……官网课程当中提供的案例,不一定是最新的,更不一定的目前推荐的,牢记这条,毕竟鸿蒙还在不断发展的阶段。跟上鸿蒙的变化,以万变迎接万变,自是最稳定的招数。
接下来看看Remote Communication Kit进行HTTP请求是什么样子的。
- 先导入模块
import { rcp } from '@kit.RemoteCommunicationKit';
import { BusinessError } from '@kit.BasicServicesKit';
- 创建会话 区分不同方式的请求
const session = rcp.createSession();
// 1. get请求
session.get("EXAMPLE_URL1").then((response) => {
console.info(`Response succeeded: ${response}`);
}).catch((err: BusinessError) => {
console.error(`Response err: Code is ${err.code}, message is ${JSON.stringify(err)}`);
});
//2. post请求
session.post("EXAMPLE_URL2", "data to send").then((response) => {
console.info(`Response succeeded: ${response}`);
}).catch((err: BusinessError) => {
console.error(`Response err: Code is ${err.code}, message is ${JSON.stringify(err)}`);
});
//3. put请求
session.put("EXAMPLE_URL3", "data to send").then((response) => {
console.info(`Response succeeded: ${response}`);
}).catch((err: BusinessError) => {
console.error(`Response err: Code is ${err.code}, message is ${JSON.stringify(err)}`);
});
//4. delete请求
session.delete("EXAMPLE_URL4").then((response) => {
console.info(`Response succeeded: ${response}`);
}).catch((err: BusinessError) => {
console.error(`Response err: Code is ${err.code}, message is ${JSON.stringify(err)}`);
});
无论是选择哪种方案,目前都是支持的。封装好http请求,更改的时候也只需要更改这一块的文件就可以了,估摸这个后续变更也不会太大吧。