TwoCheckout 抛出 System.ArgumentNullException:Value cannot be
结论
System.ArgumentNullException: Value cannot be null. Parameter name: s 表明 SDK 把一个 null 字符串传给了不接受空值的 .NET 方法,例如 Encoding.GetBytes(string s)、DateTime.Parse(string s) 或类似的解析函数。
这个异常通常与 IIS 的写入权限无关。给 IIS_IUSRS 增加 solution 目录权限一般解决不了问题。这里运行的是 NUnit 测试,执行进程很可能也不是 IIS。
仅凭异常消息无法确定哪个字符串是空值,需要查看完整堆栈。结合代码,建议优先检查:
- API 用户名和密码是否为真实有效的 API 凭据;
sale_id是否存在于当前 Sandbox 环境;- 旧版 SDK 是否收到了空响应,或无法解析服务器返回的错误响应;
- TLS、代理、网络连接或接口地址变化是否导致请求未获得正常响应;
- SDK 是否假定响应中一定包含某个字段,而服务器实际没有返回该字段。
先获取完整异常堆栈
当前代码只捕获 TwoCheckoutException,实际抛出的却是 ArgumentNullException,所以程序不会进入这个 catch。
调试时不要把异常当作测试成功。可以暂时改为记录完整异常信息:
[Test]
public void _004_TestSaleStop()
{
TwoCheckoutConfig.ApiUsername = "actual-api-username";
TwoCheckoutConfig.ApiPassword = "actual-api-password";
TwoCheckoutConfig.Sandbox = true;
var service = new SaleService();
var options = new SaleStopServiceOptions
{
sale_id = 9093729066354
};
try
{
var result = service.Stop(options);
Assert.That(result, Is.Not.Null);
Assert.That(result, Is.InstanceOf<TwoCheckoutResponse>());
}
catch (Exception ex)
{
TestContext.WriteLine(ex.ToString());
throw;
}
}
ex.ToString() 会包含:
- 实际异常类型;
- 完整调用栈;
InnerException;- SDK 中触发异常的类、方法以及可能的行号。
调用栈中第一个属于 TwoCheckout SDK 的位置最值得关注。例如,出现下面的内容:
System.Text.Encoding.GetBytes(String s)
TwoCheckout....(...)
说明 SDK 正在编码一个空字符串。如果出现 DateTime.Parse(String s),则表示服务器响应中的某个日期字段缺失。Parameter name: s 只是底层方法的形参名称,并不是名为 s 的 TwoCheckout 配置项。
检查配置和环境是否匹配
如果示例中的 "username" 和 "password" 只是占位符,需要将它们替换为 TwoCheckout 后台提供的 API 凭据。普通登录邮箱或账户密码通常不能代替 API 凭据。
还要确认下面的配置与数据来源一致:
TwoCheckoutConfig.Sandbox = true;
具体来说:
Sandbox = true时,sale_id必须属于 Sandbox;- 正式环境中的 sale ID 不能直接用于 Sandbox;
- Sandbox 凭据和正式环境凭据不能混用。
凭据无效或 sale ID 不存在时,服务器可能返回错误响应。旧版 SDK 对某些错误响应处理不完整,可能不会抛出清晰的 TwoCheckoutException,而是在解析缺失字段时触发 ArgumentNullException。
可以在调用前检查必要数据:
if (string.IsNullOrWhiteSpace(TwoCheckoutConfig.ApiUsername))
{
throw new InvalidOperationException("TwoCheckout API username is missing.");
}
if (string.IsNullOrWhiteSpace(TwoCheckoutConfig.ApiPassword))
{
throw new InvalidOperationException("TwoCheckout API password is missing.");
}
if (options.sale_id <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(options.sale_id),
"A valid sale ID is required.");
}
检查 SDK 收到的原始响应
如果配置无误,可以在 SDK 发起 HTTP 请求和反序列化响应的位置设置断点,查看:
- HTTP 状态码;
- 响应正文是否为
null或空字符串; - 服务器是否返回了 HTML 错误页,而不是 SDK 预期的 JSON/XML;
- 错误响应是否缺少 SDK 强制解析的字段;
- 请求是否因 TLS、证书、代理或连接问题提前失败。
日志中不要输出 Authorization、API 密码或其他敏感请求头。
如果确认服务器返回了空内容,而 SDK 随后直接尝试解析,可以在本地维护的 SDK 代码中加入明确的检查:
if (string.IsNullOrWhiteSpace(responseBody))
{
throw new TwoCheckoutException(
"TwoCheckout returned an empty response.");
}
如果响应不为空,应记录脱敏后的响应内容,再结合调用栈查找缺失的字段。不要仅凭 Parameter name: s 猜测字段名称。
检查旧版 SDK 与运行环境
2checkout-dotnet 是较早期的 SDK。如果项目使用较新的 .NET 运行环境,或者服务端接口、TLS 要求和响应格式发生了变化,旧 SDK 可能遇到兼容性问题。
可以按以下顺序检查:
- 项目引用的 SDK 具体版本和提交版本;
- SDK 中
SaleService.Stop最终访问的接口地址; - 当前运行环境是否支持服务端要求的 TLS 版本;
- 是否有更新版本、维护分支或针对空响应的修复;
- 使用相同凭据和 sale ID 通过官方接口工具直接请求,能否得到正常响应。
在旧版 .NET Framework 环境中,如果调用栈或网络日志明确显示 TLS 协商失败,可以在应用启动阶段验证 TLS 1.2 设置:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
只有确认问题来自 TLS 后才需要这样处理。单凭 ArgumentNullException 不能断定 TLS 是根因。
为什么修改目录权限无效
目录权限问题通常会引发以下异常:
UnauthorizedAccessExceptionSecurityExceptionIOException
一般不会只出现:
ArgumentNullException: Value cannot be null. Parameter name: s
NUnit 测试通常由测试运行器进程执行,而不是 IIS Application Pool。即使确实存在文件访问问题,也应给实际运行测试的账户授予目标目录所需的最小权限,而不是让 IIS_IUSRS 对整个 solution 目录拥有完全控制权限。
排查时应重点查看完整调用栈、真实 API 响应,以及凭据和运行环境是否匹配。如果调用栈最终证明 SDK 在处理空响应或缺失字段时传入了 null,可以升级 SDK,或在相应的解析位置加入空值检查。同时还需要继续查明响应为空的原因,例如认证失败、数据不匹配或网络异常。
备注:内容仅供参考。
