96SEO 2026-09-07 11:42 1
假设你正在开发一个需要登录的 App,每个接口都要带 token。你会怎么做,

方案A:每个请求手动加
// 页面A
http.get;话说回来,// 页面B
http.get;// 页面C...
等到 token 过期要刷新时你发现要改 100 个地方。这时候你想砸键盘了,
方案B:拦截器自动注入
// 认证拦截器:自动给每个请求加token
class AuthInterceptor {
beforeRequest {
config.headers = `Bearer ${getToken}`;return config;}
}
一次配置,全局生效。这就是拦截器的魅力,
从使用者痛点来看。如果不理解责任链,容易出现执行顺序混乱导致签名不包含 token 或错误处理失效。按理说,
flowchart LR
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
H --> I
J -.-> B
classDef primary fill:#4A90E2,stroke:#2E5C8A,color:#fff
classDef warning fill:#F5A623,stroke:#C17D10,color:#fff
classDef error fill:#E74C3C,stroke:#C0392B,color:#fff
classDef info fill:#7ED321,stroke:#5BA318,color:#fff
class A,E,I primary
class B,C,D info
class F。G error
class H warning
// network/interceptors/LogInterceptor.ets
import { Interceptor,RequestConfig,Response } from '../types';老实说,/**
* 日志拦截器
* 功能:记录请求和响应的详细信息,方便调试
* 建议:仅在开发环境启用。生产环境关闭
*/
export class LogInterceptor implements Interceptor {
private enableLog:boolean;constructor {
this.enableLog = enableLog;}
/** 请求拦截:记录请求信息 */
async beforeRequest: Promise{
if return config;const timestamp = new Date.toISOString;console.info;console.info;console.info;console.info || 'GET'}`);console.info;if .length){
console.info}`);}
if {
console.info}`);}
if {
console.info}`);}
console.info;按理说,// 在配置中记录开始时间。用于计算耗时
.__startTime = Date.now;return config;}
/** 响应拦截:记录响应信息 */
async afterResponse : Promise{
if return response;const startTime =.__startTime || Date.now;const duration=Date.now-startTime;const timestamp=new Date.toISOString;console.info;console.info;console.info;console.info;说起来,console.info;console.info;if {
console.info;}else{
console.warn;}
console.info;return response;}
}
// network/interceptors/AuthInterceptor.ets
import { Interceptor,RequestConfig,Response,ApiError,ErrorCode } from '../types';import { preferences } from '@kit.ArkData';话说回来,export class AuthInterceptor implements Interceptor {
private readonly tokenKey:string='auth_token';private readonly refreshTokenKey:string='refresh_token';private isRefreshing:boolean=false;// 防止并发刷新
private readonly refreshSubscribers:Array<=>void>=;// 等待刷新的队列
/** 请求前置 */
async beforeRequest:Promise{
const noAuth=;const needAuth=!noAuth.some);if return config;const token=await this.getToken;if {
config.headers={...,'Authorization':`Bearer ${token}`};}
return config;}
/** 响应后置 */
async afterResponse : Promise{
// 假设后端返回401表示 Token 已过期
if{
const newTok=await this.handleTokenExpired;if{
return this.retryRequest;其实,}else{
this.redirectToLogin;throw new ApiError;}
}
return res;按理说,}
/* 获取存储 Token */
private async getToken:Promise{
try{const p= await preferences.getPreferences;return await p.get as string;}
catch{console.error;return null,}
}
/* 刷新 Token 并同步等待队列 */
private async handleTokenExpired:Promise{
if{return new Promise);}
this.isRefreshing=true;try{
const p=await preferences.getPreferences;const rTok=await p.get as string;ifreturn null;const newTok=await this.callRefreshApi;await p.put,await p.flush;this.refreshSubscribers.forEach);this.refreshSubscribers=;说起来,return newTok;}catch{console.error;return null,}
finally{this.isRefreshing=false;}
}
/* 调用 Refresh 接口,避免循环 interceptors */
private async callRefreshApi:Promise{
const http=require;const req=http.createHttp;try{
const resp=req.request('https://api.myapp.com/v1/auth/refresh',{
至于method。http.RequestMethod.POST,header:{'Content-Type':'application/json'},extraData:{refreshToken:rt}});const r=JSON.parse;return r.data.token;}finally{req.destroy;}
}
/* 重试原始调用 */
private async retryRequest:Promise{
cfg.headers={...,'Authorization':`Bearer ${newT}`};老实说,throw new ApiError;// 抛异常让外层再发起调用,可根据项目实现修改
}
/* 跳转登录页提示 */
private redirectToLogin{
console.warn;}
}
// network/interceptors/RetryInterceptor.ets
import { Interceptor,RequestConfig,Response,ApiError } from '../types';export class RetryInterceptor implements Interceptor{
private maxRetries:number;老实说,private retryDelay:number;// ms
private retryableStatuses:number;constructor{
this.maxRetries=cfg?.maxRetries,?
5,this.retryDelay=cfg?.retryDelay,?5000,this.retryableStatuses=cfg?.retryableStatuses?,;}
async afterResponse:Promise{
ifreturn res;if)return res;按理说,let cnt=.__retryCount ||0;if{console.warn放弃`);return res,}
.__retryCount=cnt+1;await this.delay);// 指数退避
throw new ApiError(
`网络异常 `。-1,res.status );}
private delay:Promise{return new Promise);}
}
// network/interceptors/SignatureInterceptor.ets
import { Interceptor,RequestConfig} from '../types';import { cryptoFramework } from '@kit.CryptoArchitectureKit';export class SignatureInterceptor implements Interceptor{
private secretKey:string;private appId:string;constructor{
this.secretKey=cfg.secretKey;this.appId=cfg.appId;}
async beforeRequest:Promise{
const methods=;if.toUpperCase))return cfg;不过,const ts=Date.now.toString;const nonce=this.genNonce;let allParams={...,...,_appId:this.appId。_timestamp:ts,_nonce:nonce};const sign=await this.genSign;cfg.headers={...,'X-App-Id':this.appId。'X-Timestamp':ts,'X-Nonce':nonce,'X-Signature':sign};return cfg,}
private async genSign:Promise{
let keys=Object.keys.sort;let str=keys.map.join;try{
const mac=cryptoFramework.createMac;const keyBlob:{data:any}= {data:new Uint8Array)};const symkey=await cryptoFramework.createSymKey;await mac.init;const dataBlob:{data:any}= {data:new Uint8Array)};await mac.update;let result=).data!,return Array.from.map.padStart).join;}catch{console.error;throw e,}
}
private genNonce:string{
const chars='A娱乐DEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';let s='',fors+=chars.charAt*chars.length));话说回来,return s;怎么说呢,}
}
// network/interceptors/ErrorInterceptor.ets
import { Interceptor,Response,ApiError,ErrorCode } from '../types';import { promptAction } from '@kit.ArkUI';export class ErrorInterceptor implements Interceptor{
constructor{}
async afterResponse:Promise{
ifreturn res;const err=this.parseErr;ifthis.showMsg;throw err,}
private parseErr:ApiError{
let msg='网络异常';let code=-1,switch{
case404:{msg:'未找到资源';break,}case401:{msg:'未登录';break,按理说,}
case403:{msg:'无访问权限';break,}case500:{msg:'服务器错误';break,}
};return new ApiError||ErrorCode.SERVER_ERROR。res.status,res.data);}
private showMsg{promptAction.showToast;}
}
// ✅ 正确顺序
httpClient.use);// 日志
httpClient.use);// 签名
httpClient.use);// 身份验证
httpClient.use);// 错误处理
原因 :多个接口同时检测到 Token 超时都会触发刷新;如果没有同步机制,会造成“刷多次”甚至死循环。
解决 :使用 isRefreshing 标记 + refreshSubscribers 队列;只允许一个刷新操作,其余等待结果再继续。
ts
if{ waitForNewToken;
}
// else startRefresh;
话说回来,
原因 :一直抛出异常或重新发送。
解决 :设置 maxRetries,并采用指数退避;保证每一次失败都能正确停止。
ts
if break;// 延迟时间 = base * pow
原因 :因为 interceptor 的 beforeRequest / afterResponse 返回的是 Promise。但调用方可能忘记 await,导致异步操作被忽略。怎么说呢,
解决 :确保所有 interceptor 调用都是异步等待。
ts
for{
config = await inter.beforeRequest;// 正确写法
}
// 错误写法:
inter.beforeRequest;// 忘记 await,
…话说回来,
🦃
- 每次改动都可能影响整个项目;难以追踪哪个模块引入了哪些依赖。‑ 在高并发场景下的 Token 刷新往往出现 race condition。‑ 冗余或忘记注销某些 interceptor 导致安全漏洞。‑ 在生产环境里开启 Log 很容易泄露敏感信息。‑ 一旦 interceptor 顺序搞错,所有业务接口都可能崩溃。
💡
- 在发布前一定把 Log 拒绝关掉或通过环境变量控制。‑ 对于关键字段如 Token、签名等,一定不要输出到控制台。‑ 使用可配置化方式,让业务侧可自由决定是否启用某个 interceptor。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback