HarmonyOS鸿蒙Next中封装http请求,无法处理后台接口(result里面包含code,data,msg),统一处理错误信息弹窗(只在封装库显示,UI界面,只处理返回值数据回显)
HarmonyOS鸿蒙Next中封装http请求,无法处理后台接口(result里面包含code,data,msg),统一处理错误信息弹窗(只在封装库显示,UI界面,只处理返回值数据回显)

login页面调用
import HttpService from '../api/index';
async loginButton() {
try {
const data = await HttpService('/app/login', { username:this.phoneNumber,password:this.phonePsd } as Form,'post')
console.log('提交成功,返回 data:', data);
} catch (e) {
console.error('提交失败:', e);
}

确认接口调用成功,但是在封装统一的/api/index.ets文件中
promptAction.showToast不生效,希望根据result.msg显示信息,并且在login.ets页面中拿到result信息
更多关于HarmonyOS鸿蒙Next中封装http请求,无法处理后台接口(result里面包含code,data,msg),统一处理错误信息弹窗(只在封装库显示,UI界面,只处理返回值数据回显)的实战教程也可以访问 https://www.itying.com/category-93-b0.html
推荐一个全局的弹框。这种的就可以实现你的”统一处理错误信息弹窗“,可以在http模块内弹出错误信息,数据你可以再往下传递到页面内。
OpenHarmony三方库中心仓,名字:@lyb/loading-dialog
亲测好用,当然你也可以参考里面实现思路,自定义个自己所需要的弹框。
更多关于HarmonyOS鸿蒙Next中封装http请求,无法处理后台接口(result里面包含code,data,msg),统一处理错误信息弹窗(只在封装库显示,UI界面,只处理返回值数据回显)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
楼主可以参考一下下面的这种封装方式:
if (res.responseCode === 401) {
//401 token超时
//删除持久化数据token
AppStorage.set<string>(TOKEN_KEY, '')
promptAction.showToast({ message: 'token超时!' })
//回登录
router.replaceUrl({
url: 'pages/Login/LoginPage'
})
//返回错误 终止
return Promise.reject(new Error('token超时!'))
} else if (res.responseCode === 404) {
promptAction.showToast({ message: '请求地址不正确!' })
return Promise.reject(new Error('请求地址不正确!'))
} else {
//指定为字符串,然后再转成一个对象,类型是不明确的要使用泛型,返回第一层+泛型,泛型的定义是一个类和之前的有所差距
const result = JSON.parse(res.result as string) as ResponsesData<T>
//再判断返回的状态码进行处理,不是200都是失败
if (result.code === 200) {
return result.data as T
} else {
promptAction.showToast({ message: '服务器异常!' })
return Promise.reject(new Error(result.msg))
}
}
这里处理通用的拦截
import { http } from '@kit.NetworkKit'
// import { TOKEN_KEY } from '../constants'
// import { BASE_URL } from '../constants/url_var'
import { promptAction, router } from '@kit.ArkUI'
// import { ResponsesData } from './request2'
//传输参数token
export const TOKEN_KEY: string = 'token'
//请求网络的基底址
export const BASE_URL: string = 'https://slwl-api.itheima.net/'
// 最外层数据封装类的
export class ResponsesData<T> {
code: number = 0
msg: string = ""
data: T | null = null
}
interface EmptyInterface {}
//Next版本不支持在箭头函数上写纯泛型,
// function requestHttp(url: url地址, method: 请求方法类型,默认为get, data?: 参数类型) : 返回类型是: Promise里面的T类型的数据
async function requestHttp<T>(url: string = '', method: http.RequestMethod = http.RequestMethod.GET, data?: object): Promise<T> {
//创建一个网络请求
const httpRequest = http.createHttp()
//拼接地址
let urlStr = BASE_URL + url
//get方法需要自己拼接
if (method = http.RequestMethod.GET) {
//如果data里面有值并且里面有对象的时候
if (data && Object.keys(data).length) {
urlStr += "?" + Object.keys(data).map(key => {
if (data[key]) {
return `${key}=${data[key]}` // a=1 =>
}
return ""
}).join('&') //['a=1','b=2','c=3']
}
}
//设置请求对象
let config: http.HttpRequestOptions = {
//method同名方法赋值,参数名和属性名相同时只需要写一个method等价于method:method
method,
//超时时间
readTimeout: 10000,
//get的extraData参数在上面处理过了 在这儿不需要再传一遍
extraData: method === http.RequestMethod.GET ? '' : data || {} as EmptyInterface,
//响应参数的类型,指定为对象后有BUG,当结果有问题时,项目会直接瘫痪
// expectDataType: http.HttpDataType.OBJECT,
//请求头
header: {
'Content-Type': 'application/json',
"Authorization": AppStorage.get(TOKEN_KEY) as string || ''
}
}
//发请求
try {
const res = await httpRequest.request(urlStr, config)
console.log('请求url地址', urlStr)
//res.responseCode响应状态码,这里的401还会认为是请求成功
if (res.responseCode === 401) {
//401 token超时
//删除持久化数据token
AppStorage.set<string>(TOKEN_KEY, '')
promptAction.showToast({ message: 'token超时!' })
//回登录
router.replaceUrl({
url: 'pages/Login/LoginPage'
})
//返回错误 终止
return Promise.reject(new Error('token超时!'))
} else if (res.responseCode === 404) {
promptAction.showToast({ message: '请求地址不正确!' })
return Promise.reject(new Error('请求地址不正确!'))
} else {
//指定为字符串,然后再转成一个对象,类型是不明确的要使用泛型,返回第一层+泛型,泛型的定义是一个类和之前的有所差距
const result = JSON.parse(res.result as string) as ResponsesData<T>
//再判断返回的状态码进行处理,不是200都是失败
if (result.code === 200) {
return result.data as T
} else {
promptAction.showToast({ message: '服务器异常!' })
return Promise.reject(new Error(result.msg))
}
}
} catch (error) {
promptAction.showToast({ message: error })
return Promise.reject(error)
}
//执行最后销毁请求
finally {
//销毁请求请求结束
httpRequest.destroy()
}
}
//封装一个静态类的方法出来使用
export class Request {
static get<T>(url: string, data?: object): Promise<T> {
return requestHttp<T>(url, http.RequestMethod.GET, data)
}
static post<T>(url: string, data?: object): Promise<T> {
return requestHttp<T>(url, http.RequestMethod.POST, data)
}
static put<T>(url: string, data?: object): Promise<T> {
return requestHttp<T>(url, http.RequestMethod.PUT, data)
}
static delete<T>(url: string, data?: object): Promise<T> {
return requestHttp<T>(url, http.RequestMethod.DELETE, data)
}
}
小伙伴你好,可以通过封装一个 Toast 工具类,使用 PromptAction 来统一管理 showToast 和 closeToast 等方法。
详细说明
封装 Toast 工具类统一管理弹窗
方案说明:通过封装一个工具类,使用私有变量管理 PromptAction 实例,统一提供 showToast 和 closeToast 等方法,便于在项目中统一管理和使用 Toast 提示功能。详细使用指南请参考:PromptAction API 参考。
实现步骤:
1. 导入模块
导入语句:
import { UIContext, PromptAction } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
2. 封装 Toast 工具类
代码示例:
import { UIContext, PromptAction } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* Toast 显示选项接口
*/
interface ToastOptions {
message: string;
duration?: number;
bottom?: string | number;
}
/**
* Toast 工具类
* 统一管理 showToast 和 closeToast 等方法
*/
class ToastUtil {
private static instance: ToastUtil;
private promptAction: PromptAction | null = null;
/**
* 获取单例实例
*
* @returns ToastUtil 单例实例
*/
static getInstance(): ToastUtil {
if (!ToastUtil.instance) {
ToastUtil.instance = new ToastUtil();
}
return ToastUtil.instance;
}
/**
* 设置 UIContext,用于获取 PromptAction 实例
*
* @param context UIContext 实例
*/
setUIContext(context: UIContext): void {
try {
this.promptAction = context.getPromptAction();
} catch (err) {
const be = err as BusinessError;
console.error(`获取 PromptAction 失败: code = ${be.code}, message = ${be.message}`);
this.promptAction = null;
}
}
/**
* 显示 Toast 提示
*
* @param options Toast 显示选项
* @returns Promise<number> 返回 toastId,用于后续关闭
*/
async showToast(options: ToastOptions): Promise<number> {
if (!this.promptAction) {
console.error('PromptAction 未设置,无法显示 Toast');
throw new Error('PromptAction 未设置');
}
try {
// API 18+ 推荐使用 openToast,返回 toastId
const toastId = await this.promptAction.openToast({
message: options.message,
duration: options.duration || 2000,
bottom: options.bottom || '80vp'
});
return toastId;
} catch (err) {
const be = err as BusinessError;
console.error(`显示 Toast 失败: code = ${be.code}, message = ${be.message}`);
throw new Error(err);
}
}
/**
* 显示简单文本 Toast(兼容旧版本)
*
* @param message 提示信息
* @param duration 显示时长(毫秒),默认 2000ms
*/
showSimpleToast(message: string, duration: number = 2000): void {
if (!this.promptAction) {
console.error('PromptAction 未设置,无法显示 Toast');
return;
}
try {
// 兼容旧版本 API,使用 showToast(API 18+ 已弃用)
this.promptAction.showToast({
message: message,
duration: duration
});
} catch (err) {
const be = err as BusinessError;
console.error(`显示 Toast 失败: code = ${be.code}, message = ${be.message}`);
}
}
/**
* 关闭指定的 Toast
*
* @param toastId Toast ID,由 showToast 返回
*/
closeToast(toastId: number): void {
if (!this.promptAction) {
console.error('PromptAction 未设置,无法关闭 Toast');
return;
}
try {
this.promptAction.closeToast(toastId);
} catch (err) {
const be = err as BusinessError;
console.error(`关闭 Toast 失败: code = ${be.code}, message = ${be.message}`);
}
}
/**
* 显示成功提示
*
* @param message 提示信息
* @returns Promise<number> 返回 toastId
*/
async showSuccess(message: string): Promise<number> {
return this.showToast({
message: `✓ ${message}`,
duration: 2000
});
}
/**
* 显示错误提示
*
* @param message 提示信息
* @returns Promise<number> 返回 toastId
*/
async showError(message: string): Promise<number> {
return this.showToast({
message: `✗ ${message}`,
duration: 3000
});
}
/**
* 显示加载提示
*
* @param message 提示信息,默认为"加载中..."
* @returns Promise<number> 返回 toastId,可用于后续关闭
*/
async showLoading(message: string = '加载中...'): Promise<number> {
return this.showToast({
message: message,
duration: 10000 // 加载提示显示时间较长
});
}
}
export default ToastUtil;
3. 在组件中使用
代码示例:
import ToastUtil from '../utils/ToastUtil';
@ComponentV2
struct ToastTestPage {
private toastId: number | null = null;
/**
* 组件挂载时设置 UIContext
*/
aboutToAppear(): void {
const toastUtil = ToastUtil.getInstance();
toastUtil.setUIContext(this.getUIContext());
}
/**
* 显示简单提示
*/
showSimpleMessage(): void {
const toastUtil = ToastUtil.getInstance();
toastUtil.showSimpleToast('操作成功');
}
/**
* 显示可控制的 Toast
*/
async showControllableToast(): Promise<void> {
const toastUtil = ToastUtil.getInstance();
try {
// 显示 Toast 并获取 toastId
this.toastId = await toastUtil.showToast({
message: '正在处理中...',
duration: 5000
});
console.info('Toast ID:', this.toastId);
} catch (err) {
console.error('显示 Toast 失败:', err);
}
}
/**
* 提前关闭 Toast
*/
closeCurrentToast(): void {
if (this.toastId !== null) {
const toastUtil = ToastUtil.getInstance();
toastUtil.closeToast(this.toastId);
this.toastId = null;
}
}
/**
* 显示成功提示
*/
async showSuccessMessage(): Promise<void> {
const toastUtil = ToastUtil.getInstance();
await toastUtil.showSuccess('保存成功');
}
/**
* 显示错误提示
*/
async showErrorMessage(): Promise<void> {
const toastUtil = ToastUtil.getInstance();
await toastUtil.showError('操作失败,请重试');
}
/**
* 显示加载提示并自动关闭
*/
async showLoadingAndClose(): Promise<void> {
const toastUtil = ToastUtil.getInstance();
const toastId = await toastUtil.showLoading('正在加载数据...');
// 模拟异步操作
setTimeout(() => {
toastUtil.closeToast(toastId);
toastUtil.showSuccess('加载完成');
}, 2000);
}
build() {
Column() {
Button('显示简单提示')
.onClick(() => {
this.showSimpleMessage();
})
.margin({ bottom: 10 })
Button('显示可控制 Toast')
.onClick(() => {
this.showControllableToast();
})
.margin({ bottom: 10 })
Button('关闭当前 Toast')
.onClick(() => {
this.closeCurrentToast();
})
.margin({ bottom: 10 })
Button('显示成功提示')
.onClick(() => {
this.showSuccessMessage();
})
.margin({ bottom: 10 })
Button('显示错误提示')
.onClick(() => {
this.showErrorMessage();
})
.margin({ bottom: 10 })
Button('显示加载提示')
.onClick(() => {
this.showLoadingAndClose();
})
}
.width('100%')
.height('100%')
.padding(20)
.justifyContent(FlexAlign.Center)
}
}
注意事项
- UIContext 设置:必须在组件中调用
setUIContext()设置 UIContext,否则无法获取PromptAction实例。建议在aboutToAppear()生命周期中设置。 - API 版本兼容性:
openToast和closeToast方法需要 API version 18+showToast方法在 API 18+ 中已弃用,但为了兼容旧版本,工具类中提供了showSimpleToast方法- 建议优先使用
showToast方法(内部使用openToast),可以获取toastId进行控制
- Toast ID 管理:
showToast返回的toastId需要保存,才能通过closeToast关闭- 如果不需要手动关闭,可以使用
showSimpleToast方法 - 建议在组件中保存
toastId,在组件销毁时关闭未关闭的 Toast
- 错误处理:
- 所有方法都包含错误处理,失败时会输出错误日志
showToast方法会抛出异常,调用时需要处理异常showSimpleToast和closeToast方法失败时只输出日志,不会抛出异常
- 使用场景:
- 简单提示:使用
showSimpleToast或便捷方法showSuccess、showError - 需要控制关闭:使用
showToast获取toastId,然后调用closeToast - 加载提示:使用
showLoading显示长时间提示,操作完成后调用closeToast关闭
- 简单提示:使用
参考文档
如何解决“无法连接到远程服务器”错误
问题描述
当尝试连接到远程服务器时,可能会遇到“无法连接到远程服务器”的错误。此错误通常表示客户端无法与目标服务器建立网络连接。
常见原因
- 网络连接问题:本地网络不稳定或中断。
- 服务器故障:远程服务器已关机、崩溃或未运行所需服务。
- 防火墙阻止:本地防火墙或服务器防火墙阻止了连接。
- IP地址或端口错误:使用了错误的服务器地址或端口号。
- DNS解析失败:域名无法正确解析为IP地址。
- 连接超时:服务器响应时间过长,超过了客户端的等待时间。
解决方法
1. 检查本地网络连接
- 确保您的设备已连接到互联网。
- 尝试访问其他网站或服务,以确认网络连通性。
- 重启路由器或调制解调器。
2. 验证服务器状态
- 确认远程服务器已开机并正常运行。
- 检查服务器上所需的服务(如Web服务器、数据库等)是否已启动。
- 联系服务器管理员确认是否存在维护或故障。
3. 检查防火墙设置
- 本地防火墙:暂时禁用本地防火墙或安全软件,测试连接是否恢复。如果恢复,需在防火墙中添加允许规则。
- 服务器防火墙:确保服务器防火墙允许来自您IP地址的特定端口连接。
4. 确认IP地址和端口
- 仔细检查您使用的服务器IP地址和端口号是否正确。
- 对于Web服务,默认端口通常是80(HTTP)或443(HTTPS)。
5. 排查DNS问题
- 尝试使用服务器的IP地址直接连接,而非域名。
- 如果使用IP地址可以连接,则问题可能出在DNS解析上。可以尝试刷新本地DNS缓存或更换DNS服务器。
6. 调整超时设置
- 如果服务器响应较慢,可以尝试增加客户端的连接超时时间。
7. 使用网络诊断工具
- Ping:测试与服务器的基本连通性。
ping 服务器IP地址 - Telnet:测试特定端口是否开放。
telnet 服务器IP地址 端口号 - Traceroute:跟踪数据包路径,查找网络阻塞点。
tracert 服务器IP地址 (Windows) traceroute 服务器IP地址 (Linux/macOS)
8. 检查路由和中间设备
- 确保本地网络和服务器网络之间的所有路由器、交换机等网络设备工作正常。
预防措施
- 定期维护服务器,确保系统和应用更新。
- 监控服务器和网络设备的状态。
- 配置正确的防火墙规则,并定期审查。
- 为关键服务设置冗余和负载均衡。
总结
“无法连接到远程服务器”错误通常由网络或服务器端问题引起。通过系统性地检查网络连接、服务器状态、防火墙设置和配置信息,大多数情况下可以定位并解决问题。如果问题持续存在,建议联系网络管理员或服务器提供商寻求进一步帮助。
有没有符合你的要求。有帮到你的话,给我文章点个小小赞👍,
api调整了
this.getUIContext().getPromptAction().showToast({
message: '操作成功', // 提示内容
duration: 1000 // 显示1秒
});
在api/index.ets也就是request请求.then返回体中,this.getUIContext()报错,
调用请求封装时,传下context试试,
意思是页面请求的时候,在页面获取到this.getUIContext()的结果,然后随参数传到请求封装里面去。
在鸿蒙Next中封装HTTP请求,可通过拦截器统一处理错误信息。在拦截器中检查响应result中的code字段,若非成功状态,则直接在此处调用弹窗组件显示msg内容。UI界面仅接收处理后的data数据,不涉及错误处理逻辑。需使用ArkTS/TypeScript编写,避免引入Java或C语言相关代码。
根据你的需求,要实现统一错误处理并在UI层只处理业务数据,可以这样设计:
1. 封装HTTP请求层(api/index.ets):
import http from '@ohos.net.http';
import promptAction from '@ohos.promptAction';
class HttpService {
static async request(url: string, params: any, method: string = 'post'): Promise<any> {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(url, {
method: http.RequestMethod[method.toUpperCase()],
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify(params)
});
const result = JSON.parse(response.result as string);
// 统一错误处理
if (result.code !== 200) {
// 在封装层显示错误提示
promptAction.showToast({
message: result.msg || '请求失败',
duration: 3000
});
// 抛出错误,让UI层捕获
throw new Error(JSON.stringify(result));
}
// 只返回业务数据给UI层
return result.data;
} catch (error) {
// 网络错误等异常处理
promptAction.showToast({
message: '网络请求失败',
duration: 3000
});
throw error;
} finally {
httpRequest.destroy();
}
}
}
export default HttpService.request;
2. UI层使用(login.ets):
async loginButton() {
try {
const data = await HttpService('/app/login', {
username: this.phoneNumber,
password: this.phonePsd
}, 'post');
// 这里直接拿到业务数据,无需处理错误信息
console.log('登录成功,业务数据:', data);
// 处理业务逻辑...
} catch (error) {
// 这里捕获的是业务错误,可以获取完整result信息
console.error('业务错误:', error.message);
const result = JSON.parse(error.message);
// 可以在这里处理特定的业务逻辑,但不需要显示错误弹窗
}
}
关键点说明:
- 关注点分离:封装层负责统一错误提示和数据处理,UI层只关注业务逻辑
- 错误传递:通过throw将完整的result信息传递给UI层,便于特殊处理
- 数据净化:封装层只返回
result.data,UI层获得干净的业务数据 - 弹窗控制:所有错误提示都在封装层完成,UI层无需重复处理
这种设计符合HarmonyOS Next的开发规范,实现了代码的解耦和复用。


