在老项目中添加 ASMX WebService 引用需右键项目→“添加服务引用”→“高级”→“添加 Web 引用”,输入 WSDL 地址并设命名空间;遇 TLS 错误须设 SecurityProtocol = Tls | Tls11;SoapHeader 需按 WSDL 定义实例化并赋值给代理类对应属性,且命名空间与元素名须严格匹配。

怎么在老项目里添加 WebService 引用(.NET Framework)
Visual Studio 2019 及更早版本对老式 ASMX WebService 的支持仍完整,但默认“添加服务引用”走的是 WCF 路线(svcutil.exe 或 Connected Services),会生成 System.ServiceModel 相关代码,和传统 WebReference 不兼容。你得手动选回旧模式。
操作路径:右键项目 → “添加服务引用…” → 点击左下角“高级…” → 勾选“在‘Web 引用’下添加 Web 引用” → 输入 WSDL 地址(如 http://example.com/Service.asmx?wsdl)→ 命名空间填 MyWebService 即可。
- 生成的代理类继承自
System.Web.Services.Protocols.SoapHttpClientProtocol,天然支持SoapHeader - 如果提示“无法下载元数据”,先用浏览器打开 WSDL 地址确认能访问;若含重定向或 HTTPS 证书问题,需在代码中临时绕过验证(仅开发环境):
ServicePointManager.ServerCertificateValidationCallback += (a, b, c, d) => true;
- 生成后检查引用的命名空间是否和调用处一致,老项目常因命名空间冲突导致编译失败
怎么传自定义 SoapHeader(比如认证 Token)
ASMX 的 SoapHeader 必须由服务端定义好 Header 类型,客户端才能实例化并挂载。不能凭空构造 XML 或手动拼 SOAP 包——那是绕过框架的高危操作。
假设 WSDL 中声明了 AuthHeader,且代理类已生成对应字段 public MyWebService.AuthHeader AuthHeaderValue { get; set; },那么正确用法是:
var client = new MyWebService.Service();
client.AuthHeaderValue = new MyWebService.AuthHeader {
Token = "abc123",
Timestamp = DateTime.Now.ToString("o")
};
- 必须在调用方法前赋值,赋值后所有后续请求都会携带该 Header
- 如果 Header 是可选的(
required="false"),不赋值就为空,不会报错;但服务端可能拒绝无 Header 的请求 - 注意 Header 类型中的属性名、命名空间、序列化方式要和服务端完全一致,大小写敏感,否则服务端反序列化失败,常见错误是
System.InvalidOperationException: There is an error in XML document
调用时出现 “The HTTP request is unauthorized” 怎么排查
这不是 SOAP 协议层错误,而是底层 HTTP 层被拦截了。老项目常见于三类原因:
- 服务启用了 Windows 集成认证(NTLM/Kerberos),而客户端没配凭据:
client.Credentials = CredentialCache.DefaultCredentials;
或显式指定:client.Credentials = new NetworkCredential("user", "pass", "domain"); - 服务要求 Basic Auth,但代理类没暴露
Credentials设置入口(部分旧版生成器会隐藏),此时需手动设置client.PreAuthenticate = true;并覆写GetWebRequest方法注入 Authorization 头 - HTTPS + 自签名证书,未注册证书验证回调(见上一节),或服务端 TLS 版本太新(如只支持 TLS 1.2),而 .NET Framework 4.0 默认只到 TLS 1.0,需提前设置:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
为什么 SoapHeader 传过去了,服务端却收不到
最隐蔽的问题:WSDL 定义的 Header 名称和服务端实际期望的 XML 元素名不一致。ASMX 对 Header 的匹配依赖严格 QName(命名空间 + 本地名),而 Visual Studio 生成代理时可能做了简化或省略命名空间。
解决办法是抓包看真实 SOAP 请求(用 Fiddler 或 Wireshark):
- 对比 WSDL 中
<soap:header>的namespace和part名称,和抓包里<AuthHeader xmlns="...">是否一致 - 若不一致,手动修改代理类中 Header 类型的
[XmlRoot]或[XmlElement]特性(生成的 Reference.cs 是 partial 类,可另建文件扩展) - 某些老服务把 Header 放在
<soap:Header>外层(非常规),这时代理类根本不会序列化它——只能放弃自动生成,改用HttpWebRequest手写 SOAP 请求
这类问题往往卡住半天,根源不在代码逻辑,而在 WSDL 解析和命名空间映射的细节里。别急着重写,先看包。


















