前端网络请求与错误处理

举报
yd_223002268 发表于 2026/10/20 08:45:58 2026/10/20
【摘要】 前端网络请求与错误处理页面请求可能遇到网络中断、超时、权限失效、服务端错误和数据格式变化。若每个页面都自行拼装请求并解释错误,提示文案和重试行为会逐渐失去一致性。请求层应处理协议细节,上层根据业务语境决定如何反馈。 一、统一构建请求公共请求头、身份信息、序列化和响应解析应由共享模块处理。页面只需要描述业务请求所需的参数。async function request(path, option...

前端网络请求与错误处理

页面请求可能遇到网络中断、超时、权限失效、服务端错误和数据格式变化。若每个页面都自行拼装请求并解释错误,提示文案和重试行为会逐渐失去一致性。请求层应处理协议细节,上层根据业务语境决定如何反馈。

一、统一构建请求

公共请求头、身份信息、序列化和响应解析应由共享模块处理。页面只需要描述业务请求所需的参数。

async function request(path, options = {}) {
    const response = await fetch(path, {
        ...options,
        headers: {
            "Content-Type": "application/json",
            ...options.headers,
        },
    });

    if (!response.ok) throw new HttpError(response.status);
    return response.json();
}

请求层不应混入页面跳转或弹窗展示,这些行为属于更上层的业务决策。实际项目应由统一配置管理请求目标和身份信息。

二、区分传输错误与业务错误

HTTP 状态表示协议处理结果,响应体中的业务码表示业务执行结果。两者应分别解析,再映射成上层可以处理的错误类型。

class HttpError extends Error {
    constructor(status) {
        super("HTTP 请求失败:" + status);
        this.status = status;
    }
}

网络断开不等同于业务拒绝;权限失效也不等同于服务器故障。分类清楚后,页面才能分别决定重试、重新登录或展示业务提示。

三、取消失效请求

用户切换筛选条件或离开页面后,旧请求可能仍在运行。使用 AbortController 取消失效请求,并避免旧结果覆盖新状态。

const controller = new AbortController();

fetchData({ signal: controller.signal })
    .then(render)
    .catch((error) => {
        if (error.name !== "AbortError") showError(error);
    });

controller.abort();

取消是正常控制流程,不应该显示成网络故障。请求状态也应与当前页面实例或查询条件绑定。

四、谨慎设计重试

重试策略要考虑请求是否幂等。读取数据通常可以有限重试;创建订单、扣款等操作若没有幂等标识,自动重试可能重复执行。

function shouldRetry(method, attempt, error) {
    const retryableMethod = method === "GET" || method === "HEAD";
    const retryableFailure = error instanceof TypeError;
    return retryableMethod && retryableFailure && attempt < 2;
}

重试间隔应逐步增加,并加入随机扰动,避免客户端同时重试。重要写操作还要防止用户连续提交。

五、分离接口模型与页面模型

接口响应常带有协议字段、编码枚举或兼容字段。把接口对象直接传到所有页面,会让协议变化扩散到视图中。

function toOrderModel(response) {
    return {
        id: response.order_id,
        total: Number(response.total_amount),
        isPaid: response.payment_status === "paid",
    };
}

转换层只处理真实存在的差异,不要把所有字段都改成可选或随意填默认值。必需字段缺失应尽早暴露。

六、让页面状态完整

异步页面通常需要区分初次加载、成功、空数据和失败。刷新已有内容时,可以保留当前数据并显示轻量加载状态,避免屏幕突然变空。

idle -> loading -> success
                 -> empty
                 -> error

七、响应体解析也属于请求契约

fetch 在收到 HTTP 响应时即完成网络 Promise,即使状态码是错误码也不会自动拒绝。请求层需要检查响应状态,再按协议读取响应体。响应体通常只能消费一次,因此要先判断内容类型和空响应情况,避免成功响应却因无内容而 JSON 解析失败。

不同接口可能返回 JSON、文件或无内容结果。请求模块可以提供明确的 JSON 请求和文件请求方法,不要让每个页面重复猜测响应格式。解析失败应保留接口操作、状态码和字段路径等必要诊断信息,但不要将完整敏感响应写入日志。

八、为请求设置生命周期

请求应有超时和页面取消策略。现代浏览器可以使用 AbortController 传递取消信号;如果需要兼容目标环境,可在共享请求层封装定时器并确保完成后清理。超时、主动取消和网络失败应使用不同错误类型。

async function loadList(signal) {
    const response = await fetchData({ signal });
    if (!response.ok) throw new HttpError(response.status);
    if (response.status === 204) return [];
    return response.json();
}

调用方负责在页面退出或查询条件改变时中止旧请求;请求模块负责把取消原因传递清楚。

九、并发刷新与令牌更新

多个接口同时收到认证失败时,不应各自触发一次凭据刷新。可以让同一会话中的刷新任务共享一个 Promise,刷新成功后按幂等规则重放允许重试的请求。刷新失败时清理会话并统一通知上层,避免弹出多个登录窗口。

请求重放尤其要谨慎处理写操作。提交订单或上传文件可能已在服务端执行,只有明确支持幂等键的操作才适合自动重发。

十、将协议错误转换成页面状态

仓库层可把网络错误、服务端错误和业务拒绝转换成稳定结果,页面再决定保留旧数据、展示字段错误或提示重试。错误文案面向用户,技术诊断信息面向监控,两者不能直接混用。

实施中的取舍与复核

请求状态应按一次查询或一次提交建模,而不是由多个页面布尔值拼接。列表首次进入、分页加载、后台刷新和保存操作的反馈方式不同;保留旧数据刷新时,界面可显示轻量更新状态而不清空列表,首次加载失败则给出重试入口。请求层负责解析协议和统一错误类别,页面负责选择对用户有意义的说明,不应把底层异常堆栈直接展示。

并发请求要定义结果归属。搜索词变化时可取消前一次读取,也可以给每次请求分配递增序号,只接受当前序号的结果;否则响应较慢的旧查询可能覆盖最新结果。取消网络等待后仍要确认异步解析和后续状态更新不会继续执行。提交类操作应在请求期间防止重复触发,并根据服务端幂等能力决定是否允许重试。

排查时把请求耗时拆为等待、传输、解析和界面更新几个阶段。网络面板只能说明请求发生了什么,若响应已经到达而界面仍迟迟不更新,还要检查大 JSON 解析或列表渲染是否占用主线程。记录必要的状态码、请求类别和耗时分布即可,避免把认证凭证、完整请求体或个人信息写入日志。

总结

请求层负责构建、发送和解析协议;业务层负责理解错误语义并选择反馈方式。区分 HTTP 与业务错误,取消失效请求,按幂等性控制重试,并在接口模型进入视图前完成转换,可以让请求行为更一致。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。