在HarmonyOS应用开发中,通过HTTP访问网络,可以使用官方提供的@ohos.net.http模块。但是官方提供的直接使用不太好使用,需要封装下才好。这里采用TS的装饰器实现AOP效果 完成前后端接口风格的统一
目录
- @ohos.net.http (数据请求)官方示例
- 声明网络权限
- 封装实现步骤:
- 一、统一接口请求结果
- 二、网络请求配置类
- 三、网络请求核心类 引用官方Api
- 四、导出对外工具类
- 五、利用装饰器实现AOP效果
- 使用示例
- 实现案例
文档中心:@ohos.net.http (数据请求)
TS装饰器介绍:TypeScript装饰器
实现效果:
@ohos.net.http (数据请求)官方示例
// 引入包名
import http from '@ohos.net.http';
// 每一个httpRequest对应一个HTTP请求任务,不可复用
let httpRequest = http.createHttp();
// 用于订阅HTTP响应头,此接口会比request请求先返回。可以根据业务需要订阅此消息
// 从API 8开始,使用on('headersReceive', Callback)替代on('headerReceive', AsyncCallback)。 8+
httpRequest.on('headersReceive', (header) => {
console.info('header: ' + JSON.stringify(header));
});
httpRequest.request(
// 填写HTTP请求的URL地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定
"EXAMPLE_URL",
{
method: http.RequestMethod.POST, // 可选,默认为http.RequestMethod.GET
// 开发者根据自身业务需要添加header字段
header: {
'Content-Type': 'application/json'
},
// 当使用POST请求时此字段用于传递内容
extraData: {
"data": "data to send",
},
expectDataType: http.HttpDataType.STRING, // 可选,指定返回数据的类型
usingCache: true, // 可选,默认为true
priority: 1, // 可选,默认为1
connectTimeout: 60000, // 可选,默认为60000ms
readTimeout: 60000, // 可选,默认为60000ms
usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定
}, (err, data) => {
if (!err) {
// data.result为HTTP响应内容,可根据业务需要进行解析
console.info('Result:' + JSON.stringify(data.result));
console.info('code:' + JSON.stringify(data.responseCode));
// data.header为HTTP响应头,可根据业务需要进行解析
console.info('header:' + JSON.stringify(data.header));
console.info('cookies:' + JSON.stringify(data.cookies)); // 8+
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,调用destroy方法主动销毁
httpRequest.destroy();
} else {
console.info('error:' + JSON.stringify(err));
// 取消订阅HTTP响应头事件
httpRequest.off('headersReceive');
// 当该请求使用完毕时,调用destroy方法主动销毁。
httpRequest.destroy();
}
}
);
声明网络权限
使用Stage模型的应用,需要在module.json5配置文件中声明权限。
{
"module" : {
"requestPermissions":[
{
"name": "ohos.permission.INTERNET"
}
]
}
}
封装实现步骤:
一、统一接口请求结果
export class ApiResult {
code : string
msg ?: string
data ?: any
}
二、网络请求配置类
/**
* <pre>
* @desc : 网络请求配置
* </pre>
*/
export interface RequestOptions {
/**
* Request url.
*/
url?: string;
/**
* Request method.
*/
method?: RequestMethod; // default is GET
/**
* Additional data of the request.
* extraData can be a string or an Object (API 6) or an ArrayBuffer(API 8).
*/
extraData?: string | Object | ArrayBuffer;
/**
* Request url queryParams .
*/
queryParams ?: Record<string, string>;
/**
* HTTP request header.
*/
header?: Object; // default is 'content-type': 'application/json'
}
export enum RequestMethod {
OPTIONS = "OPTIONS",
GET = "GET",
HEAD = "HEAD",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE",
TRACE = "TRACE",
CONNECT = "CONNECT"
}
三、网络请求核心类 引用官方Api
import http from '@ohos.net.http';
import { RequestOptions } from './RequestOptions';
import { ApiResult } from '../ApiResult/ApiResult';
// 引入弹窗
import PreferenceUtil from '../../utils/PreferenceUtil'
import promptAction from '@ohos.promptAction';
import { LoadingProgressDialog } from '../../utils/LoadingProgressDialog';
import router from '@ohos.router';
/**
* Http请求器
*/
export class HttpCore {
loadingDialog: CustomDialogController = new CustomDialogController({
builder: LoadingProgressDialog(),
alignment:DialogAlignment.Center,
autoCancel: true,
customStyle: true
});
/**
* 发送请求
* @param requestOption
* @returns Promise
*/
request(requestOptions: RequestOptions): Promise<ApiResult> {
let p = new Promise<ApiResult>(async (resolve, reject) => {
// 每一个httpRequest对应一个HTTP请求任务,不可复用
let httpRequest = http.createHttp();
let token : string = await getToken();
let promise = httpRequest.request(requestOptions.url, {
method: requestOptions.method,
connectTimeout: 60000,
readTimeout: 60000,
header:{
'Content-Type': 'application/json',
'token': token,
'client_type': 'HarmonyOS'
},
extraData: requestOptions.extraData
});
promise.then((data) => {
//TODO:此处补充数据拆包的操作
let resultObj = JSON.parse(data.result.toString());
//弹窗提示接口返回msg
setTimeout(() => {
promptAction.showToast({
message: resultObj.msg
})
}, 500);
//如果业务码为20000 则返回ApiReslut
if (resultObj.code == 20000) {
console.log(JSON.stringify(resultObj))
resolve(resultObj);
}
if (resultObj.code == 0){
router.replaceUrl({
url: "pages/MainPage/Login"
}).then(() => {
console.log('router successful')
}).catch(err => {
console.log('router err')
})
}
//如果返回数据包含token信息 则刷新token
if (resultObj.token != undefined) {
PreferenceUtil.putPreference('token',resultObj.token)
}
}).catch((err) => {
//这里做全局异常统一处理 根据Http状态码做出处理
console.info('error:' + JSON.stringify(err));
reject(err);
});
httpRequest.destroy();
})
return p;
}
}
async function getToken(): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
try {
const data = await PreferenceUtil.getPreference('token');
if (typeof data === 'string') {
resolve(data);
} else {
reject(new Error('Invalid token'));
}
} catch (err) {
reject(err);
}
});
}
export const httpCore = new HttpCore();
四、导出对外工具类
import { RequestMethod, RequestOptions } from './RequestOptions';
import { httpCore } from './HttpCore';
import { ApiResult } from '../ApiResult/ApiResult';
/**
* <pre>
* @desc : 对外工具包
* </pre>
*/
export class HttpUtil {
private static mInstance: HttpUtil;
// 防止实例化
private constructor() {
}
static getInstance(): HttpUtil {
if (!HttpUtil.mInstance) {
HttpUtil.mInstance = new HttpUtil();
}
return HttpUtil.mInstance;
}
request (option: RequestOptions): Promise<ApiResult> {
return httpCore.request(option);
}
/**
* 封装Post网络请求
* @param option
* @returns
*/
Post(option:RequestOptions){
if(option != null){
option.method = RequestMethod.POST
}
return this.request(option);
}
/**
* 封装Get网络请求
* @param option
* @returns
*/
Get(option:RequestOptions){
if(option != null){
option.method = RequestMethod.GET
}
return this.request(option);
}
}
export const httpUtil = HttpUtil.getInstance();
五、利用装饰器实现AOP效果
import { ApiResult } from '../ApiResult/ApiResult';
import { httpUtil } from '../Http/HttpUtil';
import { RequestOptions } from '../Http/RequestOptions';
/**
* 利用Map保存参数和值的映射关系 为避免参数名及方法名重复 采用组合Key的方法
*/
type FunParamMapKey = {
target: Object; //所在类
methodName: string; //所在方法
index: number; //参数名索引值
}
let funParamMap = new Map<string, string>();
// @Get注解 拿到url 从函数的@Param拿到参数名及参数值 利用HttpUtil进行网络请求
//Get 需要拼接URl
export function Get(url: string) {
return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
let URL: string = url;
let options: RequestOptions = {
url: URL,
queryParams: {}
};
descriptor.value = async function (...args: any[]) {
//对于方法中的每一个(methodName,arg)遍历加入到网络请求配置中
args.forEach((arg, index) => {
// 通过已知的信息构造组合Key对象
let obj: FunParamMapKey = { target: target, methodName: methodName, index: index };
// 通过组合Key(通过对内容的比较而不是值 节省内存)从内存map中获取@Param修饰的内容
let paramName = funParamMap[JSON.stringify(obj)];
// 将正确的参数名及值添加至网络请求参数中
if (typeof paramName !== 'undefined') {
if (typeof arg === 'string' || typeof arg === 'object' || arg instanceof ArrayBuffer || typeof arg === 'number') {
options.queryParams[paramName] = arg
} else {
console.log('参数类型不对')
throw new Error(`Invalid parameter type at index ${index}.`);
}
}
});
//拼接请求参数
const urlParams = Object.keys(options.queryParams).map(key => `${key}=${options.queryParams[key]}`).join('&')
console.log('urlParams:', urlParams)
if (URL.includes("?")) {
options.url = `${URL}${urlParams}`
} else {
options.url = `${URL}?${urlParams}`
}
const p = new Promise<ApiResult>((resolve, reject) => {
httpUtil.Get(options).then((response) => {
const result: ApiResult = response;
resolve(result);
}).catch((error) => {
reject(error);
});
});
return await p;
};
};
}
// @Post注解 拿到url 从函数的@Param拿到参数名及参数值 利用HttpUtil进行Post网络请求
export function Post(url: string) {
return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
let options: RequestOptions = {
url: url,
extraData: {}
};
descriptor.value = async function (...args: any[]) {
//对于方法中的每一个(methodName,arg)遍历加入到网络请求配置中
args.forEach((arg, index) => {
console.log("参数值",JSON.stringify(arg))
// 通过已知的信息构造组合Key对象
let obj: FunParamMapKey = { target: target, methodName: methodName, index: index };
// 通过组合Key(通过对内容的比较而不是值 节省内存)从内存map中获取@Param修饰的内容
let paramName = funParamMap[JSON.stringify(obj)];
console.log("参数名:",paramName)
// 将正确的参数名及值添加至网络请求参数中
if (typeof paramName !== 'undefined') {
if (typeof arg === 'string' || typeof arg === 'object' || arg instanceof ArrayBuffer) {
options.extraData[paramName] = arg;
} else {
throw new Error(`Invalid parameter type at index ${index}.`);
}
}else {
//如果获取不到形参名 及未被@Param标记 并且参数的类型是对象
if (typeof arg === 'object') {
options.extraData = JSON.stringify(arg)
}
}
});
console.log('extraData', JSON.stringify(options.extraData))
const p = new Promise<ApiResult>((resolve, reject) => {
httpUtil.Post(options).then((response) => {
const result: ApiResult = response;
resolve(result);
}).catch((error) => {
reject(error);
});
});
return await p;
};
};
}
/**
* @Param 注解将想要获取的Param添加至内存Map中
* @param paramName
* @returns
*/
export function Param(paramName: string) {
return function (target: any, methodName: string, parameterIndex: number) {
let obj: FunParamMapKey = { target: target, methodName: methodName, index: parameterIndex };
funParamMap[JSON.stringify(obj)] = paramName;
};
}
使用示例
import { UserEditModel } from '../models/UserEditModel';
import{Post,Get, Param} from './aop/aop'
import { ApiResult } from './ApiResult/ApiResult';
export class UserApi {
/**
* 根据用户ID查询用户
* @param @Param("userId") userId
* @returns
*/
@Get("http://localhost:8080/api/user/get")
getUser(@Param("userId") userId: Number):Promise<ApiResult> {
return
}
/**
* 获取所有用户
* @returns
*/
@Get("http://localhost:8080/api/user/getAllUser")
getAllUser():Promise<ApiResult> {
return
}
/**
* 更新用户信息
* @returns
*/
@Post("http://localhost:8080/api/user/update")
updateUser( user : UserEditModel):Promise<ApiResult> {
return
}
}
export const userApi = new UserApi();
userApi.getAllUser().then((apiResult)=>{
this.userList = apiResult.data
})
//TODO 加载用户信息
userApi.getUser(Number(this.userID)).then((apiResult)=>{
this.user = apiResult.data
})
实现案例
gitee项目:鸿蒙前端 简易RBAC管控界面