pnp/sp v3.14:如何重新生成令牌并延长请求超时时间?
解决方案
1. 自动刷新请求令牌
请求令牌(Request Digest)默认有效期约30分钟,这是导致超时后403错误的核心原因,可通过两种方式处理:
方式一:循环中定期重置SPFI实例
每处理一定数量的项(比如500个)后,重新初始化SPFI实例,自动获取新的请求令牌:
for(let i=0;i<item_data_id.length;i++) { // 每处理500项刷新一次令牌 if (i % 500 === 0) { sp2 = spfi().using(SPBrowser({ baseUrl: props.context.pageContext.web.absoluteUrl })).using(RequestDigest()); } id = item_data_id[i].id; per = item_data_id[i].permissions; await assignPermissionsbyID(id, per, roleDefId, froleDefId, current_user, sp2); }
方式二:自定义令牌自动刷新逻辑
封装RequestDigest行为,添加过期检查,每次请求前自动刷新即将过期的令牌:
import { spfi, SPBrowser, RequestDigest, IRequestDigestInfo } from "@pnp/sp"; class AutoRefreshDigest { private digest: IRequestDigestInfo | null = null; async getValidDigest(sp: any): Promise<IRequestDigestInfo> { // 提前5分钟刷新令牌,避免过期 const now = Date.now(); if (!this.digest || now > (this.digest.expiration - 5 * 60 * 1000)) { this.digest = await RequestDigest().getDigest(sp); } return this.digest; } } // 创建带自动刷新令牌的SPFI实例 const sp2 = spfi().using(SPBrowser({ baseUrl: props.context.pageContext.web.absoluteUrl })) .using({ async fetch(url: string, options: any, sp: any) { const validDigest = await new AutoRefreshDigest().getValidDigest(sp); options.headers = { ...options.headers, "X-RequestDigest": validDigest.value, }; return fetch(url, options); }, });
2. 延长请求超时时间
pnp/sp v3.x可通过自定义fetch行为,结合AbortController实现全局或单个请求的超时设置:
方式一:全局设置超时
为SPFI实例添加全局超时逻辑,所有请求统一应用超时时间:
const sp2 = spfi().using(SPBrowser({ baseUrl: props.context.pageContext.web.absoluteUrl })) .using(RequestDigest()) .using({ async fetch(url: string, options: any) { const controller = new AbortController(); // 设置2分钟超时,可根据需求调整 const timeoutId = setTimeout(() => controller.abort(), 120000); try { const response = await fetch(url, { ...options, signal: controller.signal, }); clearTimeout(timeoutId); return response; } catch (e) { clearTimeout(timeoutId); throw e; } }, });
方式二:单个请求设置超时
针对耗时较长的单个请求单独设置超时,比如在权限分配方法中:
async function assignPermissionsbyID(id:number,permission_ids:Array<number>,roleDefId:number,froleDefId:number,current_user: ISiteUserInfo,sp:SPFI):Promise<void> { const list_id=props.list_id.trim(); const list = await sp.web.lists.getByTitle(list_id); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 60000); // 1分钟超时 try { await list.items.getById(id).breakRoleInheritance(false, { signal: controller.signal, // 传递超时信号 }); } finally { clearTimeout(timeoutId); } // 其他权限操作同理... }
额外优化建议
- 批量操作减少请求次数:使用pnp/sp的Batch功能,将单个项的多个权限操作合并为一个批量请求,降低API调用频率,减少令牌消耗。
- 添加403重试逻辑:捕获403错误后,刷新令牌并重试当前操作,提升稳定性。
内容的提问来源于stack exchange,提问作者Zeeshan Fakhar
相关产品推荐
相关产品推荐

