如何在单个方法中调用Action、Func并自动处理Token过期重试
解决Token过期自动重试并动态传递签名参数的问题
核心解决方案:利用闭包捕获动态参数
不需要在InvokeMethodRefreshToken内部直接处理CreateSignature的参数变化,而是在调用封装方法时,通过匿名委托闭包捕获当前请求的uri、body等参数,把带参数的CreateSignature调用包装成无参的Action传递进去。闭包会保留当前请求的参数上下文,确保CreateSignature能适配不同请求的参数变化。
修正后的完整代码
1. 优化InvokeMethodRefreshToken方法(增加类型安全检查)
private async Task<T> InvokeMethodRefreshToken<T>(Action createSignature, Func<Task<T>> func) { var result = await func.Invoke(); // 用模式匹配确保类型安全,避免空引用异常 if (result is BaseResponse baseResponse && baseResponse.Message == "Token Expired") { var token = await GetAccessToken(); if (!string.IsNullOrWhiteSpace(token.AccessToken)) { // 闭包自动携带当前请求的参数,重新生成签名 createSignature.Invoke(); // 重试请求 result = await func.Invoke(); } else { // 获取Token失败,将错误信息复制到结果中 result = (T)token.Copy(result.GetType()); } } return result; }
2. 修正GetBalance方法的调用逻辑
在调用InvokeMethodRefreshToken时,用匿名委托捕获当前请求的uri和body,包装成无参Action传递:
private async Task<Balance> GetBalance() { string uri = "/user/balance"; var body = new { phoneAccount = "xxx" }; // 匿名委托捕获uri、body,包装成无参Action;请求逻辑也要包装成Func<Task<T>> return await InvokeMethodRefreshToken( () => CreateSignature(uri, body), () => httpClient.PostJson<Balance>(uri, body) ); }
3. 辅助方法保持不变
private async Task<Token> GetAccessToken() { // 实现获取并更新AccessToken的逻辑,比如设置HttpClient的Bearer Token头 var token = await /* 调用获取Token的API */; if (!string.IsNullOrWhiteSpace(token.AccessToken)) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken); } return token; } private void CreateSignature(string uri, object body) { // 根据当前uri、body和AccessToken生成签名,并设置到请求头 var signature = /* 签名生成逻辑 */; httpClient.DefaultRequestHeaders.Add("Signature", signature); }
关键说明
- 闭包的作用:匿名委托
() => CreateSignature(uri, body)会捕获当前方法的uri和body变量,即使在InvokeMethodRefreshToken内部调用,也会使用捕获的参数执行签名生成,完美适配不同请求的参数差异。 - 委托传递注意事项:
PostJson方法必须包装成Func<Task<T>>(即() => httpClient.PostJson<Balance>(uri, body)),否则会立即执行请求,而非传递请求逻辑。 - 类型安全优化:使用
result is BaseResponse baseResponse的模式匹配,避免强制转换后的空引用问题,提升代码健壮性。
可选优化:增加重试次数限制
为避免Token过期后重试仍失败导致的无限循环,可给封装方法增加重试次数参数:
private async Task<T> InvokeMethodRefreshToken<T>(Action createSignature, Func<Task<T>> func, int maxRetryCount = 1) { var retryCount = 0; T result; do { result = await func.Invoke(); if (result is not BaseResponse baseResponse || baseResponse.Message != "Token Expired") { break; } retryCount++; if (retryCount > maxRetryCount) break; var token = await GetAccessToken(); if (string.IsNullOrWhiteSpace(token.AccessToken)) { result = (T)token.Copy(result.GetType()); break; } createSignature.Invoke(); } while (retryCount <= maxRetryCount); return result; }
内容的提问来源于stack exchange,提问作者derodevil
相关产品推荐
相关产品推荐

