在C#项目开发中,对接第三方或内部系统的SOAP类型WebService接口是常见任务,根据接口稳定性不同,我们可以选择静态引用或动态调用两种实现方式,其中动态调用无需提前添加服务引用,适配性更强。

C#调用WebService的两种常用方式
1. 静态引用方式
静态引用适合WebService接口地址、方法定义长期固定的场景,操作步骤如下:
- 在Visual Studio项目中右键点击引用,选择添加服务引用
- 输入WebService的WSDL地址,设置命名空间后完成引用
- 直接实例化服务类,调用对应方法即可
这种方式操作简单,有智能提示,但是接口发生变动时需要重新更新引用,不够灵活。
2. 动态调用方式
动态调用不需要提前添加服务引用,通过拼接SOAP请求报文、发送HTTP请求、解析返回结果的方式实现调用,适合接口地址或方法可能变化的场景。
动态调用SOAP WebService的完整实现
核心实现步骤
- 获取WebService的WSDL描述,确认方法的命名空间、参数格式、SOAPAction等信息
- 拼接符合SOAP协议的XML请求报文
- 设置HTTP请求头,包含Content-Type和SOAPAction
- 发送POST请求到WebService地址,获取返回报文
- 解析返回的XML结果,提取需要的业务数据
完整代码示例
以下是动态调用WebService的通用工具类代码:
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
public class WebServiceHelper
{
/// <summary>
/// 动态调用SOAP WebService接口
/// </summary>
/// <param name="url">WebService地址</param>
/// <param name="methodName">调用的方法名</param>
/// <param name="soapAction">SOAPAction值,可从WSDL中获取</param>
/// <param name="params">方法参数,key为参数名,value为参数值</param>
/// <returns>接口返回的结果字符串</returns>
public static string CallWebService(string url, string methodName, string soapAction, System.Collections.Generic.Dictionary<string, string> params)
{
// 拼接SOAP请求报文
StringBuilder soapBuilder = new StringBuilder();
soapBuilder.Append("<?xml version="1.0" encoding="utf-8"?>");
soapBuilder.Append("<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ");
soapBuilder.Append("xmlns:xsd="http://www.w3.org/2001/XMLSchema" ");
soapBuilder.Append("xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">");
soapBuilder.Append("<soap:Body>");
// 方法节点,注意命名空间需要和WSDL中的一致
soapBuilder.Append($"<{methodName} xmlns="http://tempuri.org/">");
// 拼接参数
if (params != null)
{
foreach (var item in params)
{
soapBuilder.Append($"<{item.Key}>{item.Value}</{item.Key}>");
}
}
soapBuilder.Append($"</{methodName}>");
soapBuilder.Append("</soap:Body>");
soapBuilder.Append("</soap:Envelope>");
string soapXml = soapBuilder.ToString();
byte[] requestData = Encoding.UTF8.GetBytes(soapXml);
// 创建HTTP请求
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "text/xml; charset=utf-8";
request.Headers.Add("SOAPAction", soapAction);
request.ContentLength = requestData.Length;
// 写入请求数据
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(requestData, 0, requestData.Length);
}
// 获取响应并解析
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(responseStream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
}
/// <summary>
/// 从返回的SOAP XML中提取方法返回结果
/// </summary>
/// <param name="soapResult">SOAP返回报文</param>
/// <param name="resultNodeName">返回结果节点名,通常为方法名加Result</param>
/// <returns>解析后的结果</returns>
public static string ParseSoapResult(string soapResult, string resultNodeName)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(soapResult);
// 命名空间管理
XmlNamespaceManager nsManager = new XmlNamespaceManager(xmlDoc.NameTable);
nsManager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
nsManager.AddNamespace("ns", "http://tempuri.org/");
// 定位结果节点
XmlNode resultNode = xmlDoc.SelectSingleNode($"//ns:{resultNodeName}", nsManager);
return resultNode?.InnerText;
}
}
调用示例
假设有一个获取用户信息的WebService,地址为http://ipipp.com/UserService.asmx,方法名为GetUserInfo,SOAPAction为http://tempuri.org/GetUserInfo,参数为userId,调用代码如下:
class Program
{
static void Main(string[] args)
{
string url = "http://ipipp.com/UserService.asmx";
string methodName = "GetUserInfo";
string soapAction = "http://tempuri.org/GetUserInfo";
var params = new System.Collections.Generic.Dictionary<string, string>
{
{ "userId", "1001" }
};
// 调用接口
string soapResult = WebServiceHelper.CallWebService(url, methodName, soapAction, params);
// 解析结果,返回结果节点名为GetUserInfoResult
string userInfo = WebServiceHelper.ParseSoapResult(soapResult, "GetUserInfoResult");
Console.WriteLine($"用户信息:{userInfo}");
}
}
注意事项
- SOAP请求报文中的命名空间需要和WSDL描述中的完全一致,否则会返回调用失败的错误
- 如果WebService需要身份验证,需要在请求头中添加对应的认证信息,比如Basic认证的用户名密码
- 返回结果解析时如果命名空间不匹配,会导致无法获取节点,需要提前确认WSDL中的命名空间定义
- 动态调用方式没有智能提示,需要提前确认方法的参数名称、类型,避免拼接错误
C#SOAP_WebService动态调用接口调用修改时间:2026-06-17 07:57:31