鸿蒙开发-RCP 远程通信请求封装
官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/remote-communication-customdnsconfig
模拟机 / 真机调试遇到的问题
{
"code": 1007900007,
"data": "Couldn't connect to server",
"extendInfo": {
"httpPhase": "110000",
"dnsDur": "0.10",
"tcpDur": "0.00",
"tlsDur": "0.00",
"sndDur": "0.00",
"rcvDur": "0.00",
"totDur": "0.66",
"redDur": "0.00",
"osErr": "111",
"sptIP6": "0",
"proxyType": "none",
"sock": "98",
"tcpConnE": "115",
"tryConnV4": "1",
"tryConnV6": "0"
}
}
说是无法连接到服务器。
原因分析
调试时使用的基地址为 localhost:8080,因为我们运行是在 模拟机 或 真机上,所有访问的 localhost:8080 无法映射到电脑的服务端。
解决方案
- 可以使用云服务器。
- 内网穿透。
因为只是在本地进行测试封装的RCP工具是否可用,所以用云服务器大材小用了。所以这里使用内网穿透。内网穿透有很多的方式。
- cpolar。(https://www.cpolar.com/docs)
- 路由侠。(https://www.luyouxia.com/)
- 等等。
在这里使用路由侠进行操作。将基地址换成内网穿透后的公网地址即可。
工具封装
目前实现的请求类型有:GET、POST、PUT、DELETE、FETCH。并自定义了请求拦截器和响应拦截器。
import { rcp } from '@kit.RemoteCommunicationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';
export interface ApiResponse<T> {
code: number
message: string
data: T
}
export class RcpUtil {
// 基地址
private static BASE_URL = "http://chen.w1.luyouxia.net";
// 获取会话
private static getSession(): rcp.Session {
// 会话配置
const sessionConfig: rcp.SessionConfiguration = {
// 定义拦截器
interceptors: [
new RequestInterceptor(),
new ResponseInterceptor()
],
requestConfiguration: {
security: {
tlsOptions: {
tlsVersion: 'TlsV1.3'
}
},
transfer: {
timeout: {
connectMs: 3600,
transferMs: 6000
}
}
}
}
// 创建会话并返回
return rcp.createSession(sessionConfig);
}
// 关闭会话
private static closeSession(session: rcp.Session) {
session.close()
}
// get 请求处理参数拼接
private static requestParamHandle(url: string, data: object): string {
let tempUrl = url
// data 参数都需要拼接到地址上,如:?a=1&b=2
if (data && Object.keys(data).length) {
tempUrl += "?" + Object.keys(data).filter(key => !!data[key]).map(key => `${key}=${data[key]}`).join('&')
}
return tempUrl
}
// fetch 请求
public static fetch<T>(path: string, method: rcp.HttpMethod, data?: object): Promise<T> {
let url = RcpUtil.BASE_URL + path
if (method === 'GET' && data) {
// GET 请求需要处理地址拼接
url = RcpUtil.requestParamHandle(url, data)
}
// 创建 request 请求
const request = new rcp.Request(url, method);
request.content = method === 'GET' ? '' : data
// 创建会话
const session = RcpUtil.getSession();
// 发起请求
return new Promise((resolve, reject) => {
session.fetch(request)
.then((res: rcp.Response) => {
const result = res.toJSON() as ApiResponse<T>
resolve(result?.data)
})
.catch((err: BusinessError) => {
reject(err)
})
.finally(() => {
RcpUtil.closeSession(session) // 关闭会话
})
})
}
// get 请求
public static get<T>(path: string, param?: object): Promise<T> {
let url = RcpUtil.requestParamHandle(RcpUtil.BASE_URL + path, param as object)
// 创建会话
const session = RcpUtil.getSession();
// 发起请求
return new Promise((resolve, reject) => {
session.get(url)
.then((res: rcp.Response) => {
const result = res.toJSON() as ApiResponse<T>
resolve(result?.data)
})
.catch((err: BusinessError) => {
reject(err)
})
.finally(() => {
RcpUtil.closeSession(session)
})
})
}
// post 请求
public static post<T>(path: string, data?: object): Promise<T> {
// 定义 content,请根据实际情况选择
const postContent: rcp.RequestContent = data as object
// 创建会话
const session = RcpUtil.getSession();
// 发起请求
return new Promise((resolve, reject) => {
session.post(RcpUtil.BASE_URL + path, postContent)
.then((res: rcp.Response) => {
const result = res.toJSON() as ApiResponse<T>
resolve(result?.data)
})
.catch((err: BusinessError) => {
reject(err)
})
})
}
// put 请求
public static put<T>(path: string, data?: object): Promise<T> {
// 定义 content,请根据实际情况选择
const putContent: rcp.RequestContent = data as object
// 创建会话
const session = RcpUtil.getSession();
// 发起请求
return new Promise((resolve, reject) => {
session.put(RcpUtil.BASE_URL + path, putContent)
.then((res: rcp.Response) => {
const result = res.toJSON() as ApiResponse<T>
resolve(result?.data)
})
.catch((err: BusinessError) => {
reject(err)
})
})
}
// delete
public static delete<T>(path: string): Promise<T> {
// 创建会话
const session = RcpUtil.getSession();
// 发起请求
return new Promise((resolve, reject) => {
session.delete(RcpUtil.BASE_URL + path)
.then((res: rcp.Response) => {
const result = res.toJSON() as ApiResponse<T>
resolve(result?.data)
})
.catch((err: BusinessError) => {
reject(err)
})
})
}
}
// 定义RequestInterceptor拦截器
class RequestInterceptor implements rcp.Interceptor {
// 自定义请求处理逻辑
async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
if (context.request.headers) {
context.request.headers.authorization = 'token'
} else {
context.request.headers = {
authorization: 'token',
"content-type": 'application/json',
}
}
return next.handle(context);
}
}
// 定义ResponseInterceptor拦截器
class ResponseInterceptor implements rcp.Interceptor {
// 自定义响应处理逻辑
async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
const response = await next.handle(context);
const result = response.toJSON() as ApiResponse<object>
console.log("服务端返回结果: " + JSON.stringify(result))
if (response.statusCode === 200) {
// 判断业务状态码
if (result.code === 200) {
// 成功响应
return Promise.resolve(response)
} else {
// 失败响应
promptAction.showToast({ message: result?.message })
// 抛出异常
return Promise.reject(new Error(result.message))
}
} else {
// 失败响应,比如 401
// 后端通过 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); 这种形式返回才会进入
return Promise.reject(response);
}
}
}

浙公网安备 33010602011771号