OpenHarmony三方库组件:httpclient

OpenHarmony三方库组件:httpclient简介
HTTP是现代应用程序通过网络交换数据和媒体的的主要方式。httpclient是OpenHarmony 里一个高效执行的HTTP客户端,使用它可使您的内容加载更快&

简介

HTTP 是现代应用程序通过Internet 交换数据和媒体的主要方式。 httpclient 是OpenHarmony 的高效HTTP 客户端。这使您的内容加载速度更快并节省流量。

httpclient 基于著名的OKHTTP,并集成了android-async-http、AutobahnAndroid、OkGo 等库的功能,在OpenHarmony 中创建一个高效、易于使用且功能全面的网络请求库。

当前版本的httpclient依赖、扩展和发展了系统提供的网络请求功能和上传下载功能,实现了以下功能:

1.支持调试开关、超时、公共请求头和请求参数等全局设置,并支持链式调用。

2. 自定义任务调度程序维护一个任务队列,用于处理同步/异步请求。

3.支持标签取消请求。

4.支持配置自定义拦截器。

5.支持重定向。

6.支持客户端提取。

7.支持文件上传和下载。

8.支持cookie管理。

9.支持请求内容的加密和解密。

10.支持自定义请求。

11.支持身份认证。

12.支持证书验证。

13.支持响应缓存。

14.支持请求配置中的responseData属性。

15.支持请求优先级设置。

16.支持证书锁定。

下载安装

ohpm 安装@ohos/httpclient

有关更多信息,包括OpenHarmony ohpm 环境配置,请参阅如何安装OpenHarmony ohpm 软件包。

使用说明

API的使用方式发生了变化(旧的httpclient已经停止使用),新的使用方式请参考以下步骤。

入口module.json5添加网络请求权限

\’请求权限\’: [

{

\’名称\’: \’ohos.permission.INTERNET\’

},

{

\’名称\’: \’ohos.permission.GET_NETWORK_INFO\’

}

]

从\’@ohos/httpclient\’ 导入{ HttpClient,TimeUnit };

获取HttpClient对象并配置超时时间

this.client=new HttpClient.Builder()

.setConnectTimeout(10, TimeUnit.SECONDS)

.setReadTimeout(10, TimeUnit.SECONDS)

.setWriteTimeout(10, TimeUnit.SECONDS)

。建造();

let status :string=\’\’ //响应代码

let content:string=\’\’ //响应内容

GET请求方法示例

从\’@ohos/httpclient\’ 导入{ HttpClient, Request,Logger };

//设置请求参数

让request=new Request.Builder()

.get(\’https://postman-echo.com/get?foo1=bar1foo2=bar2\’)

.addHeader(\’内容类型\’, \’application/json\’)

.params(\’testKey1\’, \’testValue1\’)

.params(\’testKey2\’, \’testValue2\’)

。建造();

//开始请求

this.client.newCall(请求).enqueue((结果)={

如果(结果){

this.status=result.responseCode.toString();

}

if (结果.结果) {

this.内容=结果.结果;

} 除此之外{

this.content=JSON.stringify(结果);

}

Logger.info(\’onComplete – 状态: \’ + this.status);

Logger.info(\’onComplete – 内容: \’ + JSON.stringify(this.content));

},(错误)={

this.status=error.code.toString();

this.content=error.data;

Logger.info(\’onError – 错误: \’ + this.content);

});

})

POST请求方法示例

从\’@ohos/httpclient\’ 导入{ HttpClient, Request,RequestBody,Logger };

让request: request=new Request.Builder()

.url(\’https://1.94.37.200:8080/user/requestBodyPost\’)

.post(请求正文.create(

{

\’邮箱\’: \’zhang_san@gmail.com\’,

\’姓名\’: \’zhang_san\’

}

,new Mime.Builder().contentType(\’application/json\’).build().getMime()))

.ca([这个.certData])

。建造();

this.client.newCall(请求).execute().then((结果)={

如果(结果){

this.status=result.responseCode.toString();

}

if (结果.结果) {

this.内容=结果.结果;

} 除此之外{

this.content=JSON.stringify(结果);

}

Logger.info(\’onComplete – 状态: \’ + this.status);

Logger.info(\’onComplete – 内容: \’ + JSON.stringify(this.content));

}).catch((错误)={

this.status=error.code.toString();

this.content=error.data;

Logger.error(\’onError – 错误: \’ + this.content);

});

})

POST请求方法带两个参数示例

从\’@ohos/httpclient\’ 导入{ HttpClient, Request,RequestBody,Mime,Logger };

让request=new Request.Builder()

.url(\’https://postman-echo.com/post\’)

.post(请求正文.create({

a: \’a1\’, b: \’b1\’

}, new Mime.Builder()

.contentType(\’application/json\’, \’charset\’, \’utf8\’).build().getMime()))

。建造();

//启动同步请求

this.client.newCall(请求).execute().then((结果)={

如果(结果){

this.status=result.responseCode.toString();

}

if (结果.结果) {

this.内容=结果.结果;

} 除此之外{

this.content=JSON.stringify(结果);

}

Logger.info(\’onComplete – 状态: \’ + this.status);

Logger.info(\’onComplete – 内容: \’ + JSON.stringify(this.content));

}).catch((错误)={

this.status=error.code.toString();

this.content=error.data;

Logger.error(\’onError – 错误: \’ + this.content);

});

POST请求方法使用FormEncoder示例

从\’@ohos/httpclient\’ 导入{ HttpClient, Request,FormEncoder,Logger };

让formEncoder=new FormEncoder.Builder()

.add(\’电子邮件\’,\’zhang_san@gmail.com\’)

.add(\’名字\’,\’zhang_san\’)

。建造();

让feBody=formEncoder.createRequestBody();

让request: request=new Request.Builder()

.url(\’https://1.94.37.200:8080/user/requestParamPost\’)

//提交表单请求时,将header 中的Content-Type 值设置为application/x-www-form-urlencoded

.addHeader(\’内容类型\’,\’application/x-www-form-urlencoded\’)

.post(feBody)

.ca([这个.certData])

。建造();

this.client.newCall(请求).execute().then((结果)={

如果(结果){

this.status=result.responseCode.toString();

}

if (结果.结果) {

this.内容=结果.结果;

} 除此之外{

this.content=JSON.stringify(结果);

}

Logger.info(\’onComplete – 状态: \’ + this.status);

Logger.info(\’onComplete – 内容: \’ + JSON.stringify(this.content));

}).catch((错误)={

this.status=error.code.toString();

this.content=error.data;

Logger.error(\’onError – 错误: \’ + this.content);

});

PUT请求示例

从\’@ohos/httpclient\’ 导入{ HttpClient, Request, RequestBody,Logger };

让request: request=new Request.Builder()

.url(\’https://1.94.37.200:8080/user/createUser\’)

.put(请求体.create(

{

‘年龄’: 0,

\’创建时间\’: \’2024-03-08T06:12:53.876Z\’,

\’邮件\’: \’字符串\’,

‘性别’: 0,

\’移动\’: \’字符串\’,

\’名称\’: \’字符串\’,

\’更新时间\’: \’2024-03-08T06:12:53.876Z\’,

\’userUuid\’: \’字符串\’

}, new Mime.Builder().contentType(\’application/json\’).build()))

.ca([这个.certData])

。建造();

this.client.newCall(请求).execute().then((结果)={

如果(结果){

this.status=result.responseCode.toString();

}

if (结果.结果) {

this.内容=结果.结果;

} 除此之外{

this.content=JSON.stringify(结果);

}

Logger.info(\’onComplete – 状态: \’ + this.status);

Logger.info(\’onComplete – 内容: \’ + JSON.stringify(this.content));

}).catch((错误)={

this.status=error.code.toString();

this.content=error.data;

Logger.error(\’onError – 错误: \’ + this.content);

});

DELETE请求示例

从\’@ohos/httpclient\’ 导入{ HttpClient, Request, RequestBody,Logger };

让request=new Request.Builder()

.url(\’https://reqres.in/api/users/2\’)

。擦除()

。建造();

this.client.newCall(请求).execute().then((结果)={

如果(结果){

this.status=result.responseCode.toString();

}

if (结果.结果) {

this.内容=结果.结果;

} 除此之外{

this.content=JSON.stringify(结果);

}

Logger.info(\’onComplete – 状态: \’ + this.status);

Logger.info(\’onComplete – 内容: \’ + JSON.stringify(this.content));

}).catch((错误)={

this.status=error.code.toString();

this.content=error.data;

Logger.error(\’onError – 错误: \’ + this.content);

});

tag取消请求示例

从\’@ohos/httpclient\’ 导入{ HttpClient, Request, RequestBody,Logger };

让request=new Request.Builder()

。获得()

.url(这个.echo服务器)

.tag(\’tag123\’) //设置请求的标签

.addHeader(\’内容类型\’, \’application/json\’)

。建造();

this.client.newCall(请求).enqueue((结果)={

如果(结果){

this.status=result.responseCode.toString();

}

if (结果.结果)

this.内容=结果.结果;

除此之外

this.content=JSON.stringify(结果);

},(错误)={

this.content=JSON.stringify(错误);

});

this.client.cancelRequestByTag(\’tag123\’); //通过标签取消请求。

文件上传示例

获取上传文件的路径,生成上传文件(此步骤可以省略,也可以使用命令将上传文件导入到设备中),同时生成上传文件的进程路径

从\’@ohos/httpclient\’ 导入{ HttpClient, Request, FileUpload,Logger };

让这里AbilityContext: Context=getContext();

让hereCacheDir:字符串=hereAbilityContext.cacheDir;

让hereFilesDir:字符串=hereAbilityContext.filesDir;

常量ctx=这个

Logger.info(\’缓存目录\’ + 这里CacheDir)

让filePath=这里CacheDir + 文件名。

Logger.info(\’文件路径\’ + 文件路径)

让fd=fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)

fs.truncateSync(fd.fd)

fs.writeSync(fd.fd, \’测试httpclient\’)

fs.fsyncSync(fd.fd)

fs.closeSync(fd)

Logger.info(\’writeSync\’);

Logger.info(\’文件创建成功\’)

//文件上传目前仅支持两种协议类型:“Data possible”和“Internal”。

//但是,“internal”仅支持临时目录。示例:external://cache/path/to/file.txt

//所以我们需要将获取到的文件路径转换为内部格式的路径

filePath=filePath.replace(hereCacheDir, \’internal://cache\’);

开始上传

从\’@ohos/httpclient\’ 导入{ HttpClient, Request, FileUpload,Logger };

让这里AbilityContext: Context=getContext();

让hereCacheDir:字符串=hereAbilityContext.cacheDir;

让hereFilesDir:字符串=hereAbilityContext.filesDir;

//创建文件上传对象并包装参数

let fileUploadBuilder = new FileUpload.Builder()
.addFile(filePath)
.addData(\”name2\”, \”value2\”)
.build();
Log.showInfo(\’about to set : abilityContext – cacheDir = \’ + hereCacheDir);
Log.showInfo(\’about to Set : abilityContext – filesDir = \’ + hereFilesDir);
Log.showInfo(\”type of :\” + typeof hereAbilityContext)
// 生成上传参数
let request = new Request.Builder()
.url(this.fileServer)
.body(fileUploadBuilder)
.setAbilityContext(hereAbilityContext)
.build();
this.client.newCall(request).execute().then((data) => {
// 上传进度回调监听
data.uploadTask.on(\’progress\’, (uploadedSize, totalSize) => {
Logger.info(\’progress—>uploadedSize: \’ + uploadedSize
+ \’ ,totalSize—>\’ + totalSize);
if (uploadedSize == totalSize){
Logger.info(\”upload success\”)
}
})
// 上传完毕回调监听
data.uploadTask.on(\’headerReceive\’, (headers) => {
Logger.info( \’progress—>uploadSize: \’ + JSON.stringify(headers));
})
}).catch((error)=> {
this.status = \”\”;
this.content = error.data;
Logger.error(\”onError -> Error : \” + this.content);
});

文件下载请求示例

import { HttpClient, Request,Logger } from \’@ohos/httpclient\’;
let hereAbilityContext: Context = getContext();
let hereFilesDir: string = hereAbilityContext.filesDir;
try {
this.status = \”\”;
let fPath = hereFilesDir + \”/sampleEnqueue.jpg\”;

// request可以不设置下载路径fPath,如果不设置下载路径fPath,下载的文件默认缓存在cache文件夹
let request = new Request.Builder()
.download(\”https://imgkub.com/images/2022/03/09/pexels-francesco-ungaro-15250411.jpg\”, fPath)
.setAbilityContext(hereAbilityContext)
.build();
// 发起请求
this.client.newCall(request).enqueue((data) => {
// 设置下载完成监听
data.downloadTask.on(\’complete\’, () => {
Logger.info(\” download complete\”);
this.content = \”Download Task Completed\”;
});
// 设置下载进度监听
data.downloadTask.on(\”progress\”, ( receivedSize, totalSize)=>{
Logger.info(\” downloadSize : \”+receivedSize+\” totalSize : \”+totalSize);
this.content = \”\”+(receivedSize/totalSize)*100;
});
}, (error)=> {
this.status = \”\”;
this.content = error.data;
Logger.error(\”onError -> Error : \” + this.content);
});
} catch (err) {
Logger.error(\” execution failed – errorMsg : \”+err);
}

二进制文件分片上传示例

导入上传文件至应用cache目录 ,并使用chown命令修改用户权限

1. 查询应用cache沙箱路径对应的物理路径
2. 进入此物理路径,修改cache下的文件如uoload.rar的权限为同一个user_id
2.1 查询user_id 使用: ps -ef | grep cn.openharmony.httpclient 注:替换自己的包名即可,查询结果的第一列即为user_id
3.使用chown {user_id}:{user_id} uploar.rar 本例: chown 20010042:20010042 upload.rar

开始上传

import { HttpClient, Request,BinaryFileChunkUpload,Logger } from \’@ohos/httpclient\’;
let hereAbilityContext: Context = getContext();
let hereCacheDir: string = hereAbilityContext.cacheDir;

// 待上传文件路径
let filePath: string = this.hereCacheDir + \’/\’ + this.fileName
let fileUploadBuilder = new BinaryFileChunkUpload.Builder()
.addBinaryFile(hereAbilityContext, {
filePath: filePath,
fileName: this.fileName,
chunkSize: 1024 * 1024 * 4,
name: \’chunk\’
})
.addData(\’filename\’, this.fileName)
.addUploadProgress(this.uploadCallback.bind(this))
.addUploadCallback(this.callStat.bind(this))
.build();
let request = new Request.Builder()
.url(this.baseUrl + \’/upload\’)
.setAbilityContext(hereAbilityContext)
.body(fileUploadBuilder)
.build();
this.client.newCall(request).execute();

拦截器使用示例

import { Chain, Dns, HttpClient, Interceptor, Request, Response, TimeUnit, Utils,Logger } from \’@ohos/httpclient\’;
// 通过addInterceptor添加拦截器
// addInterceptor允许调用多次,添加多个拦截器,拦截器的调用顺序根据添加顺序来决定
let request = new Request.Builder()
.url(\’https://postman-echo.com/post\’)
.post()
.body(RequestBody.create(\’test123\’))
.setDefaultConfig(defaultConfigJSON)
.addInterceptor(new CustomInterceptor())
.build();

this.client.newCall(request).execute().then((result) => {
if (result) {
this.status = result.responseCode.toString();
}
if (result.result) {
this.content = result.result;
} else {
this.content = JSON.stringify(result);
}
Logger.info(\”onComplete -> Status : \” + this.status);
Logger.info(\”onComplete -> Content : \” + JSON.stringify(this.content));
}).catch((error) => {
this.status = error.code.toString();
this.content = error.data;
Logger.error(\”onError -> Error : \” + this.content);
});
export class CustomInterceptor implements Interceptor {
intercept(chain: Chain): Promise<Response> {
return new Promise<Response>(function (resolve, reject) {
let request = chain.requestI();
Logger.info(\”request = \” + request)
let response = chain.proceedI(request)
Logger.info(\”response = \” + response)
response.then((data) => {
resolve(data)
}).catch((err) => {
reject(err)
})
})
}
}

gzip解压缩示例

客户端编解码文本

import { FileUpload, gZipUtil, HttpClient, Mime, Request, RequestBody } from \’@ohos/httpclient\’
//编码文本
const test = \”hello, GZIP! this is a gzip word\”;
let compressed = gZipUtil.gZipString(test);
//解码文本
let restored = gZipUtil.ungZipString(JSON.parse(JSON.stringify(compressed)));
let result = \”解码后数据:\” + restored
客户端编码文件

// 编码文件
let appInternalDir: string = this.getContext().cacheDir;
let encodeStr = \”hello, GZIP! this is a gzip word\”
let resourcePath = appInternalDir + \”/hello.txt\”;
let gzipPath = appInternalDir + \”/test.txt.gz\”;
let fd = fs.openSync(resourcePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
fs.truncateSync(fd.fd);
fs.writeSync(fd.fd, encodeStr);
fs.fsyncSync(fd.fd)
fs.closeSync(fd);
gZipUtil.gZipFile(resourcePath, gzipPath);
客户端解码文件

// 解压缩字符串
let appInternalDir = getContext().cacheDir;
let gzipPath = appInternalDir + \”/test.txt.gz\”;
let dest = appInternalDir + \”/hello2.txt\”;
await gZipUtil.ungZipFile(gzipPath, dest);
let fileID = fs.openSync(dest, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
// 获取文件信息
let stat = fs.statSync(fileID.fd);
let size = stat.size // 文件的大小,以字节为单位
let buf = new ArrayBuffer(size);
fs.readSync(fileID.fd, buf)
let textDecoder = new util.TextDecoder(\”utf-8\”, { ignoreBOM: true });
let decodedString = textDecoder.decode(new Uint8Array(buf), { stream: false });
let result=\’解压成功\’
result = \’\\n原编码文件路径:\’ + gzipPath + \’\\n\’
result += \’\\n解码后路径:\’ + dest + \’\\n\’
result += \’\\n文件大小:\’ + size + \’ byte\’ + \’\\n\’
result += \’\\n解码结果:\’ + decodedString + \’\\n\’
let test = \”hello, GZIP! this is a gzip word\”;
let requestBody: RequestBody = RequestBody.create(test);
let request: Request = new Request.Builder()
.url(\’http://www.yourserverfortest.com\’)
.post(requestBody)
// 添加Accept-Encoding请求头,进行gzip压缩
.addHeader(\’Accept-Encoding\’, \’gzip\’)
// 添加压缩后传送数据类型
.addHeader(\’Content-Type\’, \’application/octet-stream\’)
// 将压缩数据转成buffer对象
.setGzipBuffer(true)
.build();
this.client.newCall(request).execute().then((result) => {
if (result.result) {
Logger.info(\’返回结果: \’ + result.result);
} else {
Logger.info(\’返回结果: \’ + JSON.stringify(result));
}
}).catch((err) => {
Logger.error(\’请求状态: \’ + error.code.toString());
})
http支持自动解压缩

let requestBody1 = RequestBody.create(\’your data\’, new Mime.Builder().build().getMime())
let request = new Request.Builder()
.url(\’http://www.yourserverfortest.com\’)
.post(requestBody1)
.addHeader(\”Content-Type\”, \”text/plain\”)
.addHeader(\”Accept-Encoding\”, \”gzip\”)
.build();
this.client.newCall(request).enqueue((result) => {
this.status = \’\\n返回状态:\’ + result.responseCode + \’\\n\’;
if (result.result) {
this.content += \’\\n返回结果:\’ + result.result + \’\\n\’;
this.content += \’\\n返回header:\’ + JSON.stringify(result.header) + \’\\n\’;
} else {
this.content += \’\\n返回结果:\’ + result.result + \’\\n\’;
}

}, (error) => {
this.status = \’请求状态:\’ + error.code.toString();
this.content = error.data;

});
http上传gzip文件

let hereCacheDir: string = getContext().cacheDir;
let appInternalDir = hereCacheDir;
let destpath = appInternalDir + \”/test2.txt.gz\”;
destpath = destpath.replace(hereCacheDir, \”internal://cache\”);
let fileUploadBuilder = new FileUpload.Builder()
.addFile(destpath)
.addData(\”filename\”, \”test2.txt\”)
.build();
let fileObject = fileUploadBuilder.getFile();
let dataObject = fileUploadBuilder.getData();
let request = new httpclient.Request.Builder()
.url(\’http://www.yourserverfortest.com\’)
.addFileParams(fileObject, dataObject)
.setAbilityContext(this.hereAbilityContext)
.build();
this.client.newCall(request).execute().then((data) => {
data.uploadTask.on(\’progress\’, (uploadedSize, totalSize) => {
Logger.info(\’Upload progress—>uploadedSize: \’ + uploadedSize + \’ ,totalSize—>\’ + totalSize);
this.content = \”当前上传大小:\” + uploadedSize + \’byte\\n\’
if (uploadedSize >= totalSize) {
Logger.info(\’Upload finished\’);
this.content += \”\\n上传总文件大小:\” + totalSize + \’byte\\n\’
this.content += \”\\n上传文件路径:\” + appInternalDir + \”/test2.txt.gz\\n\”
}
})
data.uploadTask.on(\’headerReceive\’, (headers) => {
Logger.info(\’Upload—>headerReceive: \’ + JSON.stringify(headers));
})
data.uploadTask.on(\’complete\’, (data) => {
this.status = \”上传完成\”
this.status += \”\\n上传结果:\” + data[0].message
Logger.info(\’Upload—>complete,data: \’ + JSON.stringify(data));
})
}).catch((error) => {
this.status = \”\”;
this.content = error;
Logger.error(\”onError -> Error : \” + this.content);
});
http下载gzip文件

let hereAbilityContext: Context = getContext();
let hereFilesDir: string = this.hereAbilityContext.filesDir;
this.downloadfile = this.hereFilesDir + \’/yourserverUrlFileName\’;
let request = new Request.Builder()
.download(\’http://www.yourserverfortest.com/yourserverUrlFileName\’)
.setAbilityContext(this.hereAbilityContext)
.build();
this.client.newCall(request).execute().then(async (data) => {
data.downloadTask.on(\’progress\’, (receivedSize, totalSize) => {
this.content = \’\\n下载文件大小:\’ + receivedSize + \’ byte\\n\’
this.content += \’\\n下载文件总大小:\’ + totalSize + \’ byte\\n\’
this.content += \”\\n下载文件路径:\” + this.downloadfile + \’\\n\’
})
data.downloadTask.on(\’complete\’, async () => {
let appInternalDir = this.hereFilesDir;
let dest = appInternalDir + \”/helloServer.txt\”;
await gZipUtil.ungZipFile(this.downloadfile, dest);
let fileID = fs.openSync(dest, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
// 获取文件信息
let stat = fs.statSync(fileID.fd);
let size = stat.size // 文件的大小,以字节为单位
let buf = new ArrayBuffer(size);
fs.readSync(fileID.fd, buf)
let textDecoder = new util.TextDecoder(\”utf-8\”, { ignoreBOM: true });
let decodedString = textDecoder.decode(new Uint8Array(buf), { stream: false });
this.status = \’下载成功\’
this.content += \’\\n下载文件内容:\’ + decodedString + \’\\n\’
})
}).catch((error) => {
this.status = \’请求状态:\’ + error.code.toString();
this.content = error.data;
Logger.error(\”onError -> Error : \” + JSON.stringify(error));
});

cookie管理示例

初始化

import {CookieJar,CookieManager,CookiePolicy,CookieStore,HttpClient,Request,RequestBody,
TimeUnit,Logger} from \’@ohos/httpclient\’;
let hereCacheDir: string = getContext().cacheDir;
client: any = new HttpClient
.Builder()
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10, TimeUnit.SECONDS)
.build();
cookieJar = new CookieJar();
cookieManager = new CookieManager();
store = new CookieStore(hereCacheDir);

给httpclient设置cookie管理的参数

Logger.info(\”http cookiejarRequest request sending \”);
this.cookiemanager.setCookiePolicy(httpclient.CookiePolicy.ACCEPT_ALL);// 设置缓存策略
this.cookiejar.setCookieStore(this.store); // 设置cookie存取处理对象
//first request to get the cookie
let request1 = new Request.Builder()
.get(this.commonServer) //Modify URL
.tag(\”tag_cookie1\”)
.cookieJar(this.cookiejar) // 给httpclient设置缓存处理对象
.cookieManager(this.cookiemanager) // 给httpclient设置缓存策略管理对象
.addHeader(\”Content-Type\”, \”application/json\”)
.build();
this.client.newCall(request1).enqueue(this.onComplete, this.onError);
// 设置httpclient请求回调

onComplete: function (result) {
if (result.response) {
this.status = result.response.responseCode;
}
if (result.response.result)
this.content = result.response.result;
else
this.content = JSON.stringify(result.response);
Logger.info(\”onComplete -> Content : \” + JSON.stringify(this.content));
},
onError: function (error) {
Logger.error(\”onError -> Error : \” + error);
this.content = JSON.stringify(error);
Logger.error(\”onError -> Content : \” + JSON.stringify(this.content));
},

请求内容加解密示例

导入加密库crypto-js

\”dependencies\”: {
\”@ohos/crypto-js\”: \”^1.0.2\”
}
引入加密模块

import { CryptoJS } from \’@ohos/crypto-js\’
const secretKey: string = \’abcd1234\’
使用AES加密请求内容,解密响应结果

import {HttpClient,Request,RequestBody,TimeUnit,Logger} from \’@ohos/httpclient\’;
let request = new Request.Builder()
.post()
.body(RequestBody.create(\”test123\”))
.url(this.echoServer)
.addInterceptor(new CustomInterceptor())
.build();
// 发起请求
this.client.newCall(request).execute().then((result) => {
if (result) {
this.status = result.responseCode.toString();
}
if (result.result)
this.content = result.result;
else
this.content = JSON.stringify(result.response);
}).catch((error) => {
this.content = JSON.stringify(error);
});
import {Interceptor,Chain,Response,Logger} from \’@ohos/httpclient\’;
class CustomInterceptor implements Interceptor {
intercept(chain: Chain): Promise<Response> {
return new Promise<Response>(function (resolve, reject) {
let request = chain.requestI();
Logger.info(\”request = \” + request)
Logger.info(\”inside AES interceptor request\” + JSON.stringify(request.body.content))
let encrypted = CryptoJS.AES.encrypt(request.body.content, CryptoJS.enc.Utf8.parse(secretKey), {
iv: CryptoJS.enc.Utf8.parse(\’0000000000\’),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
format: CryptoJS.format.Hex
}).toString()
request.body.content = encrypted;
let response = chain.proceedI(request)
Logger.info(\”response = \” + response)
response.then((data) => {
resolve(data)
Logger.info(\”inside AES interceptor response\”)
let decrypted = CryptoJS.AES.decrypt(data.result, CryptoJS.enc.Utf8.parse(secretKey), {
iv: CryptoJS.enc.Utf8.parse(\’0000000000\’),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
format: CryptoJS.format.Hex
}).toString()
Logger.log(\”AES decrypt = \” + decrypted);
data.result = decrypted;
}).catch((err) => {
reject(err)
})
})
}
}

自定义请求示例

同步自定义请求

import {HttpClient,Request,RequestBody,TimeUnit,Logger} from \’@ohos/httpclient\’;
let request = new Request.Builder()
.url(\”https://postman-echo.com/post\”)// 配置对应url
.post(RequestBody.create(\”test123\”))
.addHeader(\”Content-Type\”, \”text/plain\”)
.setEntryObj(new Weather()) //设置自定义请求的实体对象
.build();
this.client.newCall(request)
.executed() // 发起同步请求
.then(result => {
// 得到的是一个自定义请求类型的对象
Logger.info(\’Custom Request Result\’ + JSON.stringify(result));
})
.catch(err => {
Logger.error(\’Custom Request Error\’ + JSON.stringify(err));
});
异步自定义请求

let request = new Request.Builder()
.url(\”https://postman-echo.com/post\”) // 配置对应url
.post(RequestBody.create(\”test123\”))
.addHeader(\”Content-Type\”, \”text/plain\”)
.setEntryObj(new Weather(), true) //设置自定义请求的实体对象,异步需要传入true,否则执行的是常规请求
.build();
this.client.newCall(request)
// 发起异步请求
.enqueue((result) => {
// 得到的是一个自定义请求类型的对象
Logger.info(\’Custom Request Result == \’ + JSON.stringify(result));
}, (error) => {
Logger.error(\’Custom Request error == \’ + JSON.stringify(error));
})

Multipart/form-data示例

创建RequestBody并使用创建的RequestBoy初始化MultiPart生成器

import {HttpClient,Request,RequestBody,MultiPart,TimeUnit,Mime,Logger} from \’@ohos/httpclient\’;
let requestBody1 = RequestBody.create({Title: \’Multipart\’, Color: \’Brown\’},new Mime.Builder().contentDisposition(\’form-data; name=\”myfile\”\’).contentType(\’text/plain\’, \’charset\’, \’utf8\’).build().getMime())
let requestBody2 = RequestBody.create(\”HttpClient\”,new Mime.Builder().contentDisposition(\’form-data; name=\”http\”\’).contentType(\’text/plain\’, \’charset\’, \’utf8\’).build().getMime())
let requestBody3 = RequestBody.create(data,new Mime.Builder().contentDisposition(\’form-data; name=\”file\”;filename=\”httpclient.txt\”\’).contentType(\’text/plain\’, \’charset\’, \’utf8\’).build().getMime())
let boundary = \”webKitFFormBoundarysioud821\”;
let multiPartObj = new MultiPart.Builder()
.type(httpclient.MultiPart.FORMDATA)
.addPart(requestBody1)
.addPart(requestBody2)
.addPart(requestBody3)
.build();
let body = multiPartObj.createRequestBody();
在请求/响应中使用multipart

let request = new Request.Builder()
.url(this.echoServer)
.post(body)
.addHeader(\”Content-Type\”, \”multipart/form-data\”)
.params(\”LibName\”, \”HttpClient-ohos\”)
.params(\”Request\”, \”MultiData\”)
.build()

身份认证

创建client、request对象,使用NetAuthenticator对象对用户名和密码加密进行身份认证

import {HttpClient,Request,NetAuthenticator,TimeUnit,Mime,Logger} from \’@ohos/httpclient\’;
let client = new HttpClient.Builder().setConnectTimeout(10,TimeUnit.SECONDS)
.authenticator(new NetAuthenticator(\’jesse\’, \’password1\’))
.build();
let request = new Request.Builder()
.get(\”https://publicobject.com/secrets/hellosecret.txt\”)
.addHeader(\”Content-Type\”, \”application/json\”)
.build();
client.newCall(request).execute().then((result) => {
Logger.info(\’authenticator:\’ + result.responseCode.toString())
if (result) {
Logger.info(\’authenticator:\’ + result.responseCode.toString())
}
})

证书校验

将证书文件放入resources的rawfile文件件下,例如将client_rsa_private.pem.unsecure私钥文件放入rawfile文件夹下

import { HttpClient, RealTLSSocket, Request, StringUtil, TLSSocketListener, Utils } from \’@ohos/httpclient\’;
let currentALPNProtocols = [\”spdy/1\”, \”http/1.1\”]
let currentPasswd = \”123456\”
let currentSignatureAlgorithms = \”rsa_pss_rsae_sha256:ECDSA+SHA256\”
let currentCipherSuites = \”AES256-SHA256\”
let ifUseRemoteCipherPrefer = true
let protocols = [socket.Protocol.TLSv12]
let keyRes = \’client_rsa_private.pem.unsecure\’
let certRes = \’client.crt\’
let caRes = [\’ca.crt\’]
let url = \”https://106.15.92.248:5555\”
let client = new HttpClient.Builder().build();
let realTlsSocet = new RealTLSSocket();
let hereResourceManager: resmgr.ResourceManager = getContext().resourceManager;
realTlsSocet.setLisenter(new TLSSocketListenerImpl(this.content))
realTlsSocet.setKeyDataByRes(hereResourceManager, keyRes, (errKey, resultKey) => {
})
.setCertDataByRes(hereResourceManager, certRes, (errCert, resulterrKey) => {
})
.setCaDataByRes(hereResourceManager, caRes, (errCa, resultCa) => {
})
.setUseRemoteCipherPrefer(ifUseRemoteCipherPrefer)
.setSignatureAlgorithms(currentSignatureAlgorithms)
.setCipherSuites(currentCipherSuites)
.setPasswd(currentPasswd)
.setProtocols(protocols)
.setALPNProtocols(currentALPNProtocols)
let request: Request = new Request.Builder().setTlsRequst(realTlsSocet).url(url).build();
client.newCall(request).execute()
class TLSSocketListenerImpl extends TLSSocketListener {
constructor(content: string) {
super(content);
}
onBind(err: string, data: string): void {
if (!!!err) {
this.content += \’\\ntlsSoket:onBind:data:绑定成功\’
this.content += \’\\ntlsSoket:onBind:data:正在链接..\’
} else {
this.content += \’\\ntlsSoket:onBind:err:\’ + JSON.stringify(err)
}
}
onMessage(err: string, data: object): void {
if (!!!err) {
let bufferContent = buffer.from(data[\’message\’])
let unitString: ArrayBuffer = JSON.parse(JSON.stringify(bufferContent)).data;
let resultData: ESObject = Utils.Utf8ArrayToStr(unitString);
this.content += \’\\ntlsSoket:onMessage:接收服务器消息:\’ + JSON.stringify(resultData)
this.content += \’\\ntlsSoket:onMessage:服务器路由:\’ + JSON.stringify(data[\’remoteInfo\’])
} else {
this.content += \’\\ntlsSoket:onMessage:接收服务器消息:err:\’ + JSON.stringify(err)
}
}
onConnect(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:onConnect:data:\’ + ((!!data && data != undefined) ? data : \’连接成功\’)
} else {
this.content += \’\\ntlsSoket:onConnect:err:\’ + JSON.stringify(err)
if (err[\’code\’] == 0) {
this.content += \’\\ntlsSoket:onConnect:err:连接不上服务器,请确认服务器是否可用,确认客户端是否联网\’
}
}
}
onSend(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:onSend:data:发送成功\’
} else {
this.content += \’\\ntlsSoket:onSend:err:\’ + JSON.stringify(err)
}
}
onClose(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:onClose:data:\’ + data
} else {
this.content += \’\\ntlsSoket:onClose:err:\’ + JSON.stringify(err)
}
}
onError(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:onError:data:\’ + data
} else {
this.content += \’\\ntlsSoket:onError:err:\’ + JSON.stringify(err)
if (err[\’errorNumber\’] = -1 || JSON.stringify(err).includes(\’951\’)) {
this.content += \’\\ntlsSoket:onError:err:连接不上服务器,请确认服务器是否可用,确认客户端是否联网\’
}
}
}
onVerify(verifyName: string, err: string, data: string) {
if (!!!err) {
this.content += \’\\n\’ + verifyName + \’:校验证书通过\’
this.content += \’\\n校验证书数据:\’ + data
} else {
this.content += \’\\n\’ + verifyName + \’ err:\’ +JSON.stringify(err)
}
this.content += \’\\n\’
}
setExtraOptions(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:setExtraOptions:data:设置成功\’
} else {
this.content += \’\\ntlsSoket:setExtraOptions:err:\’ + JSON.stringify(err)
}
}
offConnect(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:offConnect:data:\’ + data
} else {
this.content += \’\\ntlsSoket:offConnect:err:\’ + JSON.stringify(err)
}
}
offClose(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:offClose:data:\’ + data
} else {
this.content += \’\\ntlsSoket:offClose:err:\’ + JSON.stringify(err)
}
}
offMessage(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:offMessage:data:\’ + data
} else {
this.content += \’\\ntlsSoket:offMessage:err:\’ + JSON.stringify(err)
}
}
offError(err: string, data: string) {
if (!!!err) {
this.content += \’\\ntlsSoket:offError:data:\’ + data
} else {
this.content += \’\\ntlsSoket:offError:err:\’ + JSON.stringify(err)
}
}
}

自定义证书校验

将证书文件放入resources的rawfile文件件下,例如将CA证书ca.crt文件放入rawfile文件夹下

let client: HttpClient = new HttpClient
.Builder()
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10, TimeUnit.SECONDS)
.build();
Logger.info(TAG, \’HttpClient end\’);
let context: Context = getContext();
let CA: string = await new GetCAUtils().getCA(this.ca, context);
Logger.info(TAG, \’request begin\’);
Logger.info(TAG, \’CA:\’, JSON.stringify(CA));
let request: Request = new Request.Builder()
.url(this.url)
.method(\’GET\’)
.ca([CA])
.build();
Logger.info(TAG, \’request end\’);
client.newCall(request)
.checkCertificate(new SslCertificateManagerSuccess())
.enqueue((result: Response) => {
this.result = \”自定义证书return success, result: \” + JSON.stringify(JSON.parse(JSON.stringify(result)), null, 4);
Logger.info(TAG, \”自定义证书return success, result: \” + JSON.stringify(JSON.parse(JSON.stringify(result)), null, 4));
}, (err: Response) => {
this.result = \”自定义证书return failed, result: \” + JSON.stringify(err);
Logger.info(TAG, \”自定义证书return failed, result: \”, JSON.stringify(err));
});
export class SslCertificateManager implements X509TrustManager {
checkServerTrusted(X509Certificate: certFramework.X509Cert): void {
Logger.info(TAG, \’Get Server Trusted X509Certificate\’);
// 时间校验成功的设置值
let currentDayTime = StringUtil.getCurrentDayTime();
let date = currentDayTime + \’Z\’;
try {
X509Certificate.checkValidityWithDate(date); // 检查X509证书有效期
console.error(\’checkValidityWithDate success\’);
} catch (error) {
console.error(\’checkValidityWithDate failed, errCode: \’ + error.code + \’, errMsg: \’ + error.message);
error.message = \’checkValidityWithDate failed, errCode: \’ + error.code + \’, errMsg: \’ + error.message;
throw new Error(error);
}
}
checkClientTrusted(X509Certificate: certFramework.X509Cert): void {
Logger.info(TAG, \’Get Client Trusted X509Certificate\’);
let encoded = X509Certificate.getEncoded(); // 获取X509证书序列化数据
Logger.info(TAG, \’encoded: \’, JSON.stringify(encoded));
let publicKey = X509Certificate.getPublicKey(); // 获取X509证书公钥
Logger.info(TAG, \’publicKey: \’, JSON.stringify(publicKey));
let version = X509Certificate.getVersion(); // 获取X509证书版本
Logger.info(TAG, \’version: \’, JSON.stringify(version));
let serialNumber = X509Certificate.getCertSerialNumber(); //获取X509证书序列号
Logger.info(TAG, \’serialNumber: \’, serialNumber);
let issuerName = X509Certificate.getIssuerName(); // 获取X509证书颁发者名称
Logger.info(TAG, \’issuerName: \’, Utils.Uint8ArrayToString(issuerName.data));
let subjectName = X509Certificate.getSubjectName(); // 获取X509证书主体名称
Logger.info(TAG, \’subjectName: \’, Utils.Uint8ArrayToString(subjectName.data));
let notBeforeTime = X509Certificate.getNotBeforeTime(); // 获取X509证书有效期起始时间
Logger.info(TAG, \’notBeforeTime: \’, notBeforeTime);
let notAfterTime = X509Certificate.getNotAfterTime(); // 获取X509证书有效期截止时间
Logger.info(TAG, \’notAfterTime: \’, notAfterTime);
let signature = X509Certificate.getSignature(); // 获取X509证书签名数据
Logger.info(TAG, \’signature: \’, Utils.Uint8ArrayToString(signature.data));
let signatureAlgName = X509Certificate.getSignatureAlgName(); // 获取X509证书签名算法名称
Logger.info(TAG, \’signatureAlgName: \’, signatureAlgName);
let signatureAlgOid = X509Certificate.getSignatureAlgOid(); // 获取X509证书签名算法的对象标志符OID(Object Identifier)
Logger.info(TAG, \’signatureAlgOid: \’, signatureAlgOid);
let signatureAlgParams = X509Certificate.getSignatureAlgParams(); // 获取X509证书签名算法参数
Logger.info(TAG, \’signatureAlgParams: \’, Utils.Uint8ArrayToString(signatureAlgParams.data));
let keyUsage = X509Certificate.getKeyUsage(); // 获取X509证书秘钥用途
Logger.info(TAG, \’keyUsage: \’, Utils.Uint8ArrayToString(keyUsage.data));
let extKeyUsage = X509Certificate.getExtKeyUsage(); //获取X509证书扩展秘钥用途
Logger.info(TAG, \’extKeyUsage: \’, JSON.stringify(extKeyUsage));
let basicConstraints = X509Certificate.getBasicConstraints(); // 获取X509证书基本约束
Logger.info(TAG, \’basicConstraints: \’, JSON.stringify(basicConstraints));
let subjectAltNames = X509Certificate.getSubjectAltNames(); // 获取X509证书主体可选名称
Logger.info(TAG, \’subjectAltNames: \’, JSON.stringify(subjectAltNames));
let issuerAltNames = X509Certificate.getIssuerAltNames(); // 获取X509证书颁发者可选名称
Logger.info(TAG, \’issuerAltNames: \’, JSON.stringify(issuerAltNames));
let tbs = X509Certificate.getItem(certFramework.CertItemType.CERT_ITEM_TYPE_TBS).data; // 获取X509证书TBS(to be signed)
Logger.info(TAG, \’tbs: \’, base64.fromByteArray(tbs));
let pubKey = X509Certificate.getItem(certFramework.CertItemType.CERT_ITEM_TYPE_PUBLIC_KEY); // 获取X509证书公钥.
Logger.info(TAG, \’pubKey: \’, base64.fromByteArray(pubKey.data));
let extensions = X509Certificate.getItem(certFramework.CertItemType.CERT_ITEM_TYPE_EXTENSIONS).data;
Logger.info(TAG, \’extensions: \’, base64.fromByteArray(extensions));
}
}

WbSocket接口请求示例

import { HttpClient, RealWebSocket, Request, TimeUnit, WebSocket, WebSocketListener,Logger } from \’@ohos/httpclient\’;
class MyWebSocketListener implements WebSocketListener {
onOpen(webSocket: RealWebSocket, response: string) {
Logger.info(\”ws——onOpen\”);
};
onMessage(webSocket: RealWebSocket, text: string) {
Logger.info(\”ws——onMessage\”);
};
onClosing(webSocket: RealWebSocket, code: number, reason: string) {
Logger.info(\”ws——onClosing\”);
};
onClosed(webSocket: RealWebSocket, code: number, reason: string) {
Logger.info(\”ws——onClosed\”);
};
onFailure(webSocket: RealWebSocket, e: Error, response?: string) {
Logger.error(\”ws——onFailure–\” + e.message);
};
}
let client = new HttpClient
.Builder()
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10, TimeUnit.SECONDS)
.build();
let request = new Request.Builder()
.url(this.url)
.params(\”\”,\”\”)
.params(\”\”,\”\”)
.build();
//发起websocket请求
ws = client.newWebSocket(request, new MyWebSocketListener(this.connectStatus, this.chatArr));
//向服务器发送消息
ws.send(this.msg).then((isSuccess) => {
if (isSuccess) {
this.chatArr.push(new User(1, this.msg))
Logger.info(\”ws——sendMessage–success:\”);
} else {
Logger.error(\”ws——sendMessage–FAIL:\”);
}
})

自定义DNS解析

通过dns()接口lookup自定义DNS解析:只进行解析

import { Chain, Dns, HttpClient, Interceptor, Request, Response, TimeUnit, Utils,Logger } from \’@ohos/httpclient\’;
export class CustomDns implements Dns {
lookup(hostname: string): Promise<Array<connection.NetAddress>> {
return new Promise((resolve, reject) => {
connection.getAddressesByName(hostname).then((netAddress) => {
resolve(netAddress)
}).catch((err: BusinessError) => {
reject(err)
});
})
}
}
let client = new HttpClient.Builder()
.dns(new CustomDns())
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10, TimeUnit.SECONDS)
.build();
let request = new Request.Builder().url(this.url).build();
client.newCall(request).enqueue((result) => {
Logger.info(\”dns—success—\” + JSON.stringify(result));
}, (err) => {
Logger.error(\”dns—failed—\” + JSON.stringify(err));
});
通过dns()接口lookup自定义dns解析:传入自定义DNS

import { Chain, Dns, HttpClient, Interceptor, Request, Response, TimeUnit, Utils,Logger } from \’@ohos/httpclient\’;
export class CustomAddDns implements Dns {
lookup(hostname: string): Promise<Array<connection.NetAddress>> {
return new Promise((resolve, reject) => {
connection.getAddressesByName(hostname).then((netAddress) => {
netAddress.push({\’address\’: \’xx.xx.xx.xx\’});
resolve(netAddress)
}).catch((err: BusinessError) => {
reject(err)
});
})
}
}
let client = new HttpClient.Builder()
.dns(new CustomDns())
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10, TimeUnit.SECONDS)
.build();
let request = new Request.Builder().url(this.url).build();
client.newCall(request).enqueue((result) => {
Logger.info(\”dns—success—\” + JSON.stringify(result));
}, (err) => {
Logger.error(\”dns—failed—\” + JSON.stringify(err));
});
通过dns()接口lookup自定义dns解析:重定向DNS

import { Chain, Dns, HttpClient, Interceptor, Request, Response, TimeUnit, Utils,Logger } from \’@ohos/httpclient\’;
export class CustomAddDns implements Dns {
lookup(hostname: string): Promise<Array<connection.NetAddress>> {
return new Promise((resolve, reject) => {
connection.getAddressesByName(hostname).then((netAddress) => {
netAddress = [{\’address\’: \’xxx.xx.xx.xxx\’}];
resolve(netAddress)
}).catch((err: BusinessError) => {
reject(err)
});
})
}
}
let client = new HttpClient.Builder()
.dns(new CustomDns())
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10, TimeUnit.SECONDS)
.build();
let request = new Request.Builder().url(this.url).build();
client.newCall(request).enqueue((result) => {
Logger.info(\”dns—success—\” + JSON.stringify(result));
}, (err) => {
Logger.error(\”dns—failed—\” + JSON.stringify(err));
});
通过拦截器接口自定义DNS解析

export class CustomInterceptor implements Interceptor {
intercept(chain: Chain): Promise<Response> {
return new Promise<Response>(function (resolve, reject) {
let originRequest = chain.requestI();
let url = originRequest.url.getUrl();
let host = Utils.getDomainOrIp(url)
connection.getAddressesByName(host).then(function (netAddress) {
let newRequest = originRequest.newBuilder()
if (!!netAddress) {
url = url.replace(host, netAddress[0].address)
newRequest.url(url)
}
let newResponse = chain.proceedI(newRequest.build())
resolve(newResponse)
}).catch(err => {
resolve(err)
});
})
}
}
let client = new HttpClient
.Builder()
//设置自定义拦截器
.addInterceptor(new CustomInterceptor())
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10,TimeUnit.SECONDS)
.build();
let request = new Request.Builder().url(this.url).build();
client.newCall(request).enqueue((result) => {
Logger.info(\”dns—success—\” + JSON.stringify(result));
}, (err) => {
Logger.error(\”dns—failed—\” + JSON.stringify(err));
});

响应缓存示例

添加响应缓存示例

import {
Cache,
CacheControl,
Dns,
HttpClient,
Logger,
Request,
Response,
StringUtil,
TimeUnit,
Utils,
X509TrustManager
} from \’@ohos/httpclient\’;
import { Utils as GetCAUtils } from \”../utils/Utils\”;
caPem = \”noPassword/ca.pem\”;
let cache = new Cache.Cache(getContext().cacheDir, 10 * 1024 * 1024, getContext());
let httpClient = new HttpClient
.Builder().dns(new CustomDns())
.cache(cache)
.setConnectTimeout(10000, TimeUnit.SECONDS)
.setReadTimeout(10000, TimeUnit.SECONDS)
.build();
let caFile: string = await new GetCAUtils().getCA(this.caPem, context);
// 服务端返回header请求
let request = new Request.Builder()
.get()
.url(\’https://1.94.37.200:8080/cache/e/tag\’)
.ca([caFile])
.build();
// 手动设置header请求
request = new Request.Builder()
.get()
.url(\’https://1.94.37.200:8080/cache/max/age\’)
.addHeader(\”Cache-Control\”, \”max-age=30\”)
.ca([caFile])
.build();
// 手动设置cache请求
request = new Request.Builder()
.get()
.url(\’https://1.94.37.200:8080/cache/no/cache\’)
.cacheControl(CacheControl.FORCE_CACHE())
.ca([caFile])
.build();
httpClient
.newCall(request)
.checkCertificate(new sslCertificateManager())
.execute().then((result) => {

})
export class CustomDns implements Dns {

}
export class SslCertificateManager implements X509TrustManager {

}

请求配置responseData属性示例

添加请求配置responseData属性示例

import { HttpClient, Request, Logger, TimeUnit, Response , HttpDataType} from \’@ohos/httpclient\’;
let client: HttpClient = new HttpClient.Builder()
.setConnectTimeout(1000, TimeUnit.SECONDS)
.setReadTimeout(1000, TimeUnit.SECONDS)
.setWriteTimeout(1000, TimeUnit.SECONDS)
.build();
let request = new Request.Builder()
.get(\”https://postman-echo.com/get?foo1=bar1&foo2=bar2\”)
.addHeader(\”Content-Type\”, \”application/json\”)
.setHttpDataType(HttpDataType.STRING)
.build();
client.newCall(request).execute().then((result: Response) => {})

请求优先级

设置请求优先级

import { HttpClient, Request, Logger } from \’@ohos/httpclient\’;
// 配置请求优先级
let request = new Request.Builder()
.get(\’https://postman-echo.com/get?foo1=bar1&foo2=bar2\’)
.setPriority(5)
.build();
// 发起请求
this.client.newCall(request).enqueue((result) => {
if (result) {
this.status = result.responseCode.toString();
}
if (result.result) {
this.content = result.result;
} else {
this.content = JSON.stringify(result);
}
Logger.info(\”onComplete -> Status : \” + this.status);
Logger.info(\”onComplete -> Content : \” + JSON.stringify(this.content));
}, (error)=> {
this.status = error.code.toString();
this.content = error.data;
Logger.info(\”onError -> Error : \” + this.content);
});

网络事件监听

设置网络事件监听

import { Dns, HttpClient, Request, Response, BusinessError, IOException, EventListener, HttpCall } from \’@ohos/httpclient\’;
// 自定义网络事件监听
let client = new HttpClient.Builder()
.addEventListener(new HttpEventListener())
.build();
let request = new Request.Builder()
.get(this.url)
.build();
client.newCall(request).execute().then((result) => {})
class HttpEventListener extends EventListener {
protected startTime: number = 0;
logWithTime(message: string) {
const nosTime: number = new Date().getTime();
if (message == \’callStart\’) {
this.startTime = nosTime;
}
const elapsedTime: number = (nosTime – this.startTime) / 1000;
Logger.info(\’自定义EventListener\’ + elapsedTime + \’ \’ + message );
}

callStart(call: HttpCall) {
this.logWithTime(\’callStart\’);
};

requestHeadersStart(call: HttpCall) {
this.logWithTime(\’requestHeadersStart\’);
}
requestHeadersEnd(call: HttpCall, request: Request) {
this.logWithTime(\’requestHeadersEnd\’);
}

}

证书锁定示例

设置证书锁定

import {
Dns,
HttpClient,
Request,
Response,
TimeUnit,
CertificatePinnerBuilder
} from \’@ohos/httpclient\’;
import { Utils } from \”../utils/Utils\”;
let certificatePinner = new CertificatePinnerBuilder()
.add(\’1.94.37.200\’, \’sha256/WAFcHG6pAINrztx343nlM3jYzLOdfoDS9pPgMvD2XHk=\’)
.build()
let client: HttpClient = new HttpClient
.Builder()
.dns(new CustomDns())
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10, TimeUnit.SECONDS)
.build();
let context: Context = getContext();
let CA: string = await new Utils().getCA(\’caPin.crt\’, context);
let request: Request = new Request.Builder()
.url(\’https://1.94.37.200:8080/user/getUserByUuid?userUuid=1\’)
.method(\’GET\’)
.ca([CA])
.build();
client.newCall(request)
.setCertificatePinner(certificatePinner)
.enqueue((result: Response) => {
this.result = \”响应结果success\” + JSON.stringify(JSON.parse(JSON.stringify(result)), null, 4)
Logger.info(\”证书锁定—success—\” + JSON.stringify(JSON.parse(JSON.stringify(result)), null, 4));
}, (err: BusinessError) => {
this.result = \”响应结果fail\” + JSON.stringify(err)
Logger.info(\”证书锁定—failed— \”, JSON.stringify(err));
});

添加代理示例

添加代理示例

import {Cache,Chain,DnsResolve,FormEncoder,HttpClient,Interceptor, Mime,MultiPart,Request,RequestBody,Response} from \’@ohos/httpclient\’;
let client: HttpClient = new HttpClient
.Builder()
.setProxy(new Proxy(Type.HTTP,\’xx.xx.xx.xx\’,80))
.setConnectTimeout(10, TimeUnit.SECONDS)
.setReadTimeout(10, TimeUnit.SECONDS)
.build();
let request: Request = new Request.Builder()
.url(\’http://publicobject.com/helloworld.txt\’)
.method(\’GET\’)
.build();
CacheClient.newCall(request).execute().then((result) => {})

接口说明

Request.Builder

接口名参数返回值说明setAbilityContextabilityContextRequest.Builder设置上下文,用于上传下载的参数convertorconvertorTypeRequest.Builder设置转换器类型,用于将响应结果解析转换为需要的类型setCookieJarcookieJarRequest.Builder设置cookieJar,用于自动获取缓存的cookie,并自动设置给请求头setCookieManagercookieManagerRequest.Builder设置cookie管理器,用于设置cookie策略retryOnConnectionFailureisRetryOnConnectionFailureRequest.Builder设置当前请求失败是否重试retryMaxLimitmaxValueRequest.Builder设置当前请求可以重试的最大次数retryConnectionCountcountRequest.Builder设置当前请求当前已经重试次数followRedirectsaFollowRedirectsRequest.Builder设置当前请求是否是允许重定向redirectMaxLimitmaxValueRequest.Builder设置当前请求可以重定向的最大次数redirectionCountcountRequest.Builder设置当前请求当前已经重定向次数addInterceptorreq, respRequest.Builder添加拦截器,req参数是请求拦截器,resp是响应拦截器。本方法允许多次调用添加多个拦截器。参数允许为空。headersvalueRequest.Builder当前请求设置请求头cachevalueRequest.Builder设置当前请求开启缓存addHeaderkey, valueRequest.Builder当前请求的请求头添加参数setDefaultUserAgentvalueRequest.Builder当前请求设置默认的用户代理,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。setDefaultContentTypevalueRequest.Builder当前请求设置默认的媒体类型信息。bodyvalueRequest.Builder当前请求设置请求体urlvalueRequest.Builder当前请求设置请求地址tagvalueRequest.Builder当前请求设置标签,用于取消请求methodvalueRequest.Builder当前请求设置请求请求方式paramskey, valueRequest.Builder当前请求设置请求参数,用于拼接在请求路径url后面addFileParamsfiles, dataRequest.Builder当前请求添加文件上传参数setFileNamenameRequest.Builder当前请求设置文件名,用于下载请求geturlRequest.Builder当前请求的请求方式设置为GET,如果参数url不为空则还需为request设置请求路径urlputbodyRequest.Builder当前请求的请求方式设置为PUT,如果参数body不为空则还需为request设置请求体bodydelete暂无Request.Builder当前请求的请求方式设置为DELETEhead暂无Request.Builder当前请求的请求方式设置为HEADoptions暂无Request.Builder当前请求的请求方式设置为OPTIONSpostbodyRequest.Builder当前请求的请求方式设置为POST,如果参数body不为空则还需为request设置请求体bodyuploadfiles, dataRequest.Builder当前请求的请求方式设置为UPLOAD,同时设置文件列表参数files和额外参数datadownloadurl,filenameRequest.Builder当前请求的请求方式设置为DOWNLOAD,如果参数filename不为空则还需为request设置文件名filenametrace暂无Request.Builder当前请求的请求方式设置为TRACEconnect暂无Request.Builder当前请求的请求方式设置为CONNECTsetDefaultConfigdefaultConfigRequest.Builder当前请求添加默认配置,主要包括设置默认的content_type和user_agent,可以通过传入一个json文件的方式来全局配置build暂无Request.Builder当前请求根据设置的各种参数构建一个request请求对象setEntryObjvalue,flagRequest.Builder设置自定义请求对象,第一个参数是自定义实体空对象,第二个参数异步请求需要传入true用来表示是自定义请求,同步可不传,默认为falsesetHttpDataTypeHttpDataTypeRequest.Builder返回设置响应的数据类型,未设置该属性时,默认返回string数据类型。setPrioritynumberRequest.Builder当前请求设置优先级

HttpClient.Builder

接口名参数返回值说明addInterceptoraInterceptorHttpClient.Builder为HTTP请求客户端添加拦截器,用于在发起请求之前或者获取到相应数据之后进行某些特殊操作authenticatoraAuthenticatorHttpClient.Builder为HTTP请求客户端添加身份认证,可以在请求头中添加账号密码等信息。setConnectTimeouttimeout, unitHttpClient.Builder为HTTP请求客户端设置连接超时时间setReadTimeouttimeout, unitHttpClient.Builder为HTTP请求客户端设置读取超时时间setWriteTimeouttimeout, unitHttpClient.Builder为HTTP请求客户端设置写入超时时间_setTimeOuttimeout, timeUnit, timeoutTypeHttpClient.Builder为HTTP请求客户端设置超时时间,根据timeoutType来区分是连接超时时间还是读取超时时间或者是写入超时时间。build暂无HttpClient.Builder构建HTTP请求客户端对象dnsdns: DnsHttpClient.Builder设置自定义DNS解析addEventListenerEventListenerHttpClient.Builder添加网络事件监听setProxytype,host,portHttpClient.Builder

HttpCall

接口名参数返回值说明getRequest暂无Request获取当前请求任务的请求对象getClient暂无HttpClient获取当前请求任务的请求客户端execute暂无Promise当前请求任务发起同步请求enqueue暂无暂无当前请求任务发起异步请求getSuccessCallback暂无Callback获取当前请求任务的请求成功回调接口getFailureCallback暂无Callback获取当前请求任务的请求失败回调接口cancel暂无暂无取消当前请求任务isCancelled暂无Boolean获取当前请求任务是否成功取消了executed暂无Promise当前自定义请求任务发起同步请求checkCertificateX509TrustManagerHttpCall设置自定义证书校验函数setCertificatePinnercertificatePinnerHttpCall设置证书锁定

X509TrustManager

接口名参数返回值说明checkClientTrustedcertFramework.X509Certvoid校验客户端证书checkServerTrustedcertFramework.X509Certvoid校验服务器证书

WebSocket

接口名参数返回值说明request暂无Request获取RequestqueueSize暂无number获取队列大小sendtext: string ArrayBufferPromise向服务器发送消息closecode: number, reason?: stringPromise断开连接

WebSocketListener

接口名参数返回值说明onOpenwebSocket: RealWebSocket, response: stringvoidWebSocket连接成功监听回调onMessagewebSocket: RealWebSocket, text: string ArrayBuffervoidWebSocket服务端响应监听回调onClosedwebSocket: RealWebSocket, code: number, reason: stringvoidWebSocket连接关闭监听回调onFailurewebSocket: RealWebSocket, e: Error, response?: stringvoidWebSocket连接失败监听回调

RealWebSocket

接口名参数返回值说明request暂无Request获取RequestqueueSize暂无number获取队列大小sendtext: string ArrayBufferPromise向服务器发送消息closecode: number, reason?: stringPromise断开连接

RequestBody

接口名参数返回值说明createcontent : String/JSON Object of Key:Value pairRequestBody创建RequestBody对象

RequestBuilder

接口名参数返回值说明buildAndExecute无void构建并执行RequestBuildernewCall无void执行请求headername:String,value:StringRequestBuilder传入key、value构建请求头connectTimeouttimeout:LongRequestBuilder设置连接超时时间urlvalue:StringRequestBuilder设置请求urlGET无RequestBuilder构建GET请求方法PUTbody:RequestBodyRequestBuilder构建PUT请求方法DELETE无RequestBuilder构建DELETE请求方法POST无RequestBuilder构建POST请求方法UPLOADfiles:Array, data:ArrayRequestBuilder构建UPLOAD请求方法CONNECT无RequestBuilder构建CONNECT请求方法

MimeBuilder

接口名参数返回值说明contentTypevalue:Stringvoid添加MimeBuilder contentType。

FormEncodingBuilder

接口名参数返回值说明addname:String,value:Stringvoid以键值对形式添加参数build无void获取RequestBody对象

FileUploadBuilder

接口名参数返回值说明addFilefuri : Stringvoid添加文件URI到参数里用于上传addDataname:String,value:Stringvoid以键值对形式添加请求数据buildFile无void生成用于上传的文件对象buildData无void构建用于上传的数据

BinaryFileChunkUploadBuilder

接口名参数返回值说明addBinaryFileabilityContext, chunkUploadOptionsvoid添加分片上传配置信息addDataname:String,value:Stringvoid以键值对形式添加请求数据addUploadCallbackcallbackvoid添加上传完成/失败回调addUploadProgressuploadProgressCallbackvoid添加上传进度回调

RetryAndFollowUpInterceptor

接口名参数返回值说明interceptchain: ChainPromise拦截响应结果followUpRequestrequest: Request, userResponse: ResponseRequest根据请求结果生成重试策略retryAfteruserResponse: Response, defaultDelay: numbernumber获取响应header中的Retry-After

Dns

接口名参数返回值说明lookuphostname: StringPromise<Array<connection.NetAddress>>自定义DNS解析

NetAuthenticator

接口名参数返回值说明constructoruserName: string, password: stringvoid添加用户名和密码authenticaterequest: Request, response: ResponseRequest对请求头添加身份认证凭证

RealTLSSocket

接口名参数返回值说明setCaDataByResresourceManager: resmgr.ResourceManager, resName: string[], callBackvoid设置Ca证书或者证书链setCertDataByResresourceManager: resmgr.ResourceManager, resName: string, callBackvoid设置本地数字证书setKeyDataByResresourceManager: resmgr.ResourceManager, resName: string, callBackvoid设置密钥setOptionsoptions: socket.TLSSecureOptionsRealTLSSocket设置tls连接相关配置setAddressipAddress: stringvoid设置ip地址setPortport: numbervoid设置端口bindcallback?:(err,data)=>voidvoid绑定端口connectcallback?:(err,data)=>voidvoid建立tls连接senddata, callback?:(err,data)=>voidvoid发送数据getRemoteCertificatecallback:(err,data)=>voidvoid获取远程证书getSignatureAlgorithmscallback:(err,data)=>voidvoid获取签名算法setVerifyisVerify: booleanvoid设置是否校验证书verifyCertificatecallback:(err,data)=>voidvoid证书校验setCertificateManagercertificateManager: CertificateManagervoid自定义证书校验

TLSSocketListener

接口名参数返回值说明onBinderr: string, data: stringvoid绑定端口监听onMessageerr: string, data: stringvoid接收消息监听onConnecterr: string, data: stringvoid连接服务器监听onCloseerr: string, data: stringvoid关闭监听onErrorerr: string, data: stringvoid错误监听onSenderr: string, data: stringvoid发送监听setExtraOptionserr: string, data: stringvoid设置其他属性操作监听

CertificateVerify

接口名参数返回值说明verifyCertificatecallback:(err,data)=>voidvoid证书校验verifyCipherSuitecallback:(err,data)=>voidvoid加密套件验证verifyIpAddresscallback:(err,data)=>voidvoid地址校验verifyProtocolcallback:(err,data)=>voidvoid通信协议校验verifySignatureAlgorithmscallback:(err,data)=>voidvoid签名算法校验verifyTimecallback:(err,data)=>voidvoid有效时间校验

Cache

接口名参数返回值说明constructorfilePath: string,maxSize: number,context: Contextvoid设置journal创建的文件地址和大小keyurl:stringstring返回一个使用md5编码后的urlgetrequest: RequestResponse根据request读取本地缓存的缓存信息putresponse: Responsestring写入响应体removerequest: Requestvoid删除当前request的响应缓存信息evictAllNAvoid删除全部的响应缓存信息updatecache: Response, network: Responsevoid更新缓存信息writeSuccessCountNAnumber获取写入成功的计数sizeNAnumber获取当前缓存的大小maxSizeNAnumber获取当前缓存的最大值flushNAvoid刷新缓存closeNAvoid关闭缓存directoryNAstring获取当前文件所在的文件地址trackResponsecacheStrategy: CacheStrategy.CacheStrategyvoid设置命中缓存的计数trackConditionalCacheHitNAnumber增加跟踪条件缓存命中networkCountNAnumber添加网络计数hitCountNAnumber获取点击次数requestCountNAnumber获取请求的计数

CacheControl

接口名参数返回值说明FORCE_NETWORKNACacheControl强制请求使用网络请求FORCE_CACHENACacheControl强制请求使用缓存请求noCacheNAboolean获取当前请求头或者响应头是否包含禁止缓存的信息noStoreNAboolean获取当前请求头或者响应头是否包含禁止缓存的信息maxAgeSecondsNAnumber获取缓存的最大的存在时间sMaxAgeSecondsNAnumber获取缓存的最大的存在时间isPrivateNAboolean获取是否是私有的请求isPublicNAboolean获取是否是公有的请求mustRevalidateNAboolean获取是否需要重新验证maxStaleSecondsNAnumber获取最大的持续秒数minFreshSecondsNAnumber最短的刷新时间onlyIfCachedNAboolean获取是否仅缓存noTransformNAboolean没有变化immutableNAboolean获取不变的parseNACacheControl根据header创建 CacheControltoStringNAstring获取缓存控制器转字符串BuilderNABuilder获取CacheControl的Builder模式noCacheNABuilder设置不缓存noStoreNABuilder设置不缓存maxAgemaxAge: numberBuilder设置最大的请求或响应的时效maxStaleNABuilder设置不缓存onlyIfCachedNABuilder设置仅缓存noTransformNABuilder设置没有变化immutableNABuilder设置获取不变的buildNACacheControlBuilder模式结束返回CacheControl对象

gZipUtil

接口名参数返回值说明gZipStringstrvalue:stringany编码字符串ungZipStringstrvalue:anystring解码字符串gZipFilesrcFilePath:string, targetFilePath:stringvoid编码文件ungZipFilesrcFilePath:string, targetFilePath:stringvoid解码文件stringToUint8Arraystr:stringUint8Array字符串转Uint8Array

HttpDataType

指定返回数据的类型。

接口名值说明STRING0字符串类型。OBJECT1对象类型。ARRAY_BUFFER2二进制数组类型。

CertificatePinnerBuilder

指定证书锁定内容。

接口名参数返回值说明addhostname:string,sha:stringCertificatePinnerBuilder添加证书锁定参数buildNACertificatePinner构造证书锁定实例

约束与限制

在下述版本验证通过:

DevEco Studio 版本: 4.1 Canary(4.1.3.317), OpenHarmony SDK: API11 (4.1.0.36)

DevEco Studio 版本: 4.1 Canary(4.1.3.319), OpenHarmony SDK: API11 (4.1.3.1)

DevEco Studio 版本: 4.1 Canary(4.1.3.500), OpenHarmony SDK: API11 (4.1.5.6)

目录结构

|—- httpclient
| |—- entry # 示例代码文件夹
| |—- library # httpclient 库文件夹
|—- builders # 请求体构建者模块 主要用于构建不同类型的请求体,例如文件上传,multipart
|—- cache # 缓存的事件数据操作模块
|—- callback # 响应回调模块,用于将相应结果解析之后转换为常见的几种类型回调给调用者,例如string,JSON对象,bytestring
|—- code # 响应码模块,服务器返回的响应结果码
|—- connection # 路由模块,管理请求中的多路由
|—- cookies # cookie管理模块,主要处理将响应结果解析并根据设置的缓存策略缓存响应头里面的cookie,取出cookie,更新cookie
|—- core # 核心模块,主要是从封装的request里面解析请求参数和相应结果,调用拦截器,处理错误重试和重定向,dns解析,调用系统的@ohos.net.http模块发起请求
|—- dispatcher # 任务管理器模块,用于处理同步和异步任务队列
|—- http # 判断http method类型
|—- interceptor # 拦截器模块,链式拦截器处理网络请求
|—- protocols # 支持的协议
|—- response # 响应结果模块,用于接收服务端返回的结果
|—- utils # 工具类,提供dns解析,gzip解压缩,文件名校验,打印日志,获取URL的域名或者IP,双端队列等功能
|—- HttpCall.ts # 一个请求任务,分为同步请求和异步请求,封装了请求参数,请求客户端,请求成功和失败的回调,请求是否取消的标志。
|—- HttpClient.ts # 请求客户端,用于生成请求任务用于发起请求,设置请求参数,处理gzip解压缩,取消请求。
|—- Interceptor.ts # 拦截器接口
|—- Request.ts # 请求对象,用于封装请求信息,包含请求头和请求体。
|—- RequestBody.ts # 请求体,用于封装请求体信息。
|—- WebSocket.ts # websocket模块回调接口。
|—- RealWebSocket.ts # websocket模块,用于提供websocket支持。
|—- WebSocketListener.ts # websocket状态监听器。
|—- Dns.ts # 用于自定义自定义DNS解析器。
|—- CertificatePinner.ts # 证书锁定类构建器。
|—- DnsSystem.ts # 系统默认DNS解析器。
|—- Route.ts # 路由。
|—- RouteSelector.ts # 路由选择器。
|—- Address.ts # 请求地址。
|—- authenticator # 身份认证模块,用于提供网络请求401之后身份认证。
|—- tls # 证书校验模块,用于tls的证书解析和校验。
|—- enum # 参数对应的枚举类型。
|—- index.ts # httpclient对外接口
| |—- README.MD # 安装使用方法

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

 HarmonOS基础技能

HarmonOS就业必备技能  HarmonOS多媒体技术

鸿蒙NaPi组件进阶

HarmonOS高级技能

初识HarmonOS内核 实战就业级设备开发

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

 《鸿蒙开发基础》

ArkTS语言安装DevEco Studio运用你的第一个ArkTS应用ArkUI声明式UI开发.……

 《鸿蒙开发进阶》

Stage模型入门网络管理数据管理电话服务分布式应用开发通知与窗口管理多媒体技术安全技能任务管理WebGL国际化开发应用测试DFX面向未来设计鸿蒙系统移植和裁剪定制……

《鸿蒙进阶实战》

ArkTS实践UIAbility应用网络案例……

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 
#以上关于OpenHarmony三方库组件:httpclient的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/92593.html

(0)
CSDN's avatarCSDN
上一篇 2024年6月27日 下午5:17
下一篇 2024年6月27日 下午5:17

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注