如何在ASP.NET Core控制器中调用SOAP服务及解决WSDL报错
在ASP.NET Core 8中调用SOAP服务的TOOTREG方法及WSDL生成错误修复
问题背景
我基于C#开发ASP.NET Core 8 Web API项目,需要通过HTTPS调用外部SOAP服务的TOOTREG方法。该SOAP服务的WSDL地址为:https://ncfailid.emta.ee/index.php/s/wTKjgm9QQnWLZEH/download/tor.wsdl.txt,其中TOOTREG方法的定义如下:
<element name="TOOTREG"> <complexType> <sequence> <element name="request" type="tns:tootregRequestType"/> </sequence> </complexType> </element> <element name="TOOTREGResponse"> <complexType> <sequence> <element name="request" type="tns:tootregRequestType"/> <element name="response" type="tns:tootregResponseType"/> </sequence> </complexType> </element>
请求体tns:tootregRequestType和响应类型均为WSDL中定义的复杂类型。尝试通过Visual Studio社区版的「添加服务引用」功能生成客户端代码时,出现以下错误:
未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'address'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:service[@name='TorServiceV6']/wsdl:port[@name='TorV6WebServiceImpl'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'title'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:service[@name='TorServiceV6']/wsdl:port[@name='TorV6WebServiceImpl'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORTI'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORSPORT'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORT'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORRKA'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORINFO'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORIK'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TOOTREG'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'requirecontent'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TOOTREG_AMETNIK'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TOOTREG_AMETNIK'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'requirecontent'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORRGNO'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORRGNO'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'requirecontent'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORSYNK'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORSYNK'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'requirecontent'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORKODAK'] 未处理来自命名空间'http://x-road.eu/xsd/xroad.xsd'的可选WSDL扩展元素'version'。 XPath: //wsdl:definitions[@targetNamespace='http://emta-v6.x-road.eu']/wsdl:binding[@name='TorPortBinding']/wsdl:operation[@name='TORKODAK'] 导出期间生成的架构存在验证错误: 来源: 行:13 列:3 验证错误:属性组'http://www.w3.org/XML/1998/namespace:specialAttrs'已声明。 导出期间生成的架构存在验证错误: 来源: 行:12 列:3 验证错误:全局属性'http://www.w3.org/XML/1998/namespace:id'已声明。 导出期间生成的架构存在验证错误: 来源: 行:11 列:3 验证错误:全局属性'http://www.w3.org/XML/1998/namespace:base'已声明。 导出期间生成的架构存在验证错误: 来源: 行:3 列:3 验证错误:全局属性'http://www.w3.org/XML/1998/namespace:space'已声明。 导出期间生成的架构存在验证错误: 来源: 行:2 列:3 验证错误:全局属性'http://www.w3.org/XML/1998/namespace:lang'已声明。
错误原因分析
这些错误分为两类:
- X-Road扩展元素不支持:Visual Studio的服务引用工具无法识别
http://x-road.eu/xsd/xroad.xsd命名空间下的自定义WSDL扩展(如address、title、version等)。 - XML标准命名空间属性重复声明:WSDL中重复引入了
http://www.w3.org/XML/1998/namespace下的属性,导致架构验证时出现重复定义错误。
修复方案
方案一:修改WSDL后自动生成客户端代码
通过手动修改WSDL移除不兼容元素,再用工具生成代码:
- 下载WSDL到本地:访问上述WSDL地址,将内容保存为本地文件(如
tor-modified.wsdl)。 - 移除X-Road扩展元素:打开本地WSDL文件,删除所有属于
http://x-road.eu/xsd/xroad.xsd命名空间的节点(例如<xroad:address>、<xroad:version>等)。 - 解决重复属性声明:找到WSDL中重复定义
http://www.w3.org/XML/1998/namespace属性的部分,仅保留一处声明,删除其余重复的属性组或属性定义。 - 生成客户端代码:
- Visual Studio图形界面:右键项目 → 添加 → 服务引用 → 高级 → 添加Web引用,选择本地修改后的WSDL文件;
- SvcUtil命令行工具:执行以下命令生成代码:
svcutil.exe "C:\path\to\tor-modified.wsdl" /out:TorServiceClient.cs /namespace:YourProject.TorService
- 在API控制器中使用生成的代码:
[ApiController] [Route("api/tor")] public class TorApiController : ControllerBase { [HttpPost("tootreg")] public async Task<IActionResult> InvokeTootreg(TootregRequestType request) { using var client = new TorServiceV6Client(); // 替换为实际SOAP服务的Endpoint地址 client.Endpoint.Address = new EndpointAddress("https://实际SOAP服务地址"); var response = await client.TOOTREGAsync(new TOOTREG { request = request }); return Ok(response.response); } }
方案二:手动构造SOAP请求(无需代码生成)
若修改WSDL过于繁琐,可直接通过HttpClient手动发送SOAP请求:
- 手动定义模型类:根据WSDL中的
tns:tootregRequestType和tns:tootregResponseType结构,创建对应的C#模型:public class TootregRequest { // 按WSDL定义的字段添加属性,示例: public string RegNumber { get; set; } public string Name { get; set; } } public class TootregResponse { // 对应响应字段,示例: public string StatusCode { get; set; } public string StatusMessage { get; set; } // 其他复杂类型字段按需添加 } - 在API控制器中发送请求并解析响应:
[ApiController] [Route("api/tor")] public class TorApiController : ControllerBase { private readonly HttpClient _httpClient; public TorApiController(HttpClient httpClient) { _httpClient = httpClient; } [HttpPost("tootreg")] public async Task<IActionResult> CallTootreg(TootregRequest request) { // 构造SOAP信封XML var soapXml = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:emta=""http://emta-v6.x-road.eu"">
soapenv:Header/
soapenv:Body
emta:TOOTREG
emta:request
emta:regNumber{request.RegNumber}</emta:regNumber>
emta:name{request.Name}</emta:name>
</emta:request>
</emta:TOOTREG>
</soapenv:Body>
</soapenv:Envelope>";
var content = new StringContent(soapXml, Encoding.UTF8, "text/xml"); content.Headers.Add("SOAPAction", "http://emta-v6.x-road.eu/TOOTREG"); var httpResponse = await _httpClient.PostAsync("https://实际SOAP服务地址", content); httpResponse.EnsureSuccessStatusCode(); var responseXml = await httpResponse.Content.ReadAsStringAsync(); // 使用XmlSerializer解析响应 var serializer = new XmlSerializer(typeof(TootregResponse), new XmlRootAttribute("response")); using var reader = new StringReader(responseXml); var result = (TootregResponse)serializer.Deserialize(reader); return Ok(result); }
}
注意:需根据WSDL中实际的字段结构调整XML内容和模型类属性。 内容的提问来源于stack exchange,提问作者Andrus
相关产品推荐
相关产品推荐

