C# 如何封装一个客户端,来调用“指定WSDL文件的WebService服务端“

根据指定WSDL文件,创建客户端,调用Webservice接口

  • IDE: Microsoft Visual Studio Community 2022 (64bit)
  • 平台:C#.Net6
  • 协议:Soap协议(Xml格式)

功能

  • 对方是一个WebService服务端,己方是客户端,双方要遵守对方提供的WSDL规范。

实现方式1

  • 使用BasicHttpBinding

通过WSDL文件,生成WEB服务

注意,这里,我的VS2022的提示步骤和网上查到的操作步骤略有不同,下面的步骤是我的VS2022的操作步骤:

  1. 首先,在 VS2022 中打开 .NET 6 项目或者新建一个 .NET 6 项目。

  2. 在“解决方案资源管理器”中,右键单击项目名称,然后选择“添加”->“服务引用”。

  3. 在“添加服务引用”窗口中,有三个选项,

    • OpenAPI
    • gRPC
    • WCF Web Service

    这里选择 WCF Web Service,进入窗口“添加新的WCF Web Service服务引用

  4. 点击“浏览”按钮,然后从文件系统中选择你的 WSDL 文件。

  5. 选中 WSDL 文件后,服务引用的配置将在窗口下方显示出来。这里可以在下面的输入框,修改命名空间为你想要的名称,点击下一步,在新窗口中,可以指定数据类型选项,默认不需要改,点击“完成”。

  6. 此时,VS2022 会根据你的 WSDL 文件生成 Web 服务的代码,即:原Webservice服务的代理类。

  7. 还有一个生成Webservice服务代理类的方法:

    • 用wsdl.exe工具的方式
    • 指令是:wsdl /language:c# /n:Fu /out:d:/MyService.cs C:\Users\Administrator\Desktop\MyService.wsdl)
      • “d:/MyService.cs”是输出目录
      • “C:\Users\Administrator\Desktop\MyService.wsdl”是源wsdl文件的位置
    • 具体用法,可以使用搜索引擎查询,这里不再赘述。
调用该WebService的方法
		public static async Task TestCallWs()  
        {
            mydemo.TestMyClient serv = null;
            try
            {
                var binding = new BasicHttpBinding();
                var endpoint = new EndpointAddress(UrlString); // url地址
                serv = new mydemo.TestMyClient(binding, endpoint);
                var result = await serv.invokeAsync(ParamsString); // xml格式的字符串参数
                Console.WriteLine("返回接口数据:\n " + result);
                
            }
            catch(HttpRequestException e)
            {
                Console.WriteLine(e.Message);
            }
            finally
            {
                if (serv != null) 
                {
                    serv.Close();
                }
            }
        }

实现方式2

使用HttpClient

简单做一下封装

- 定义:WsClient
	public class WsClient
    {
        // 接口地址
        private string mUrl = string.Empty;
        // 请求参数
        private string mParam = string.Empty;

         namespace命名空间
        //private string ns = "http://XXX:xx/xx/xx"; // TODO填写上你需要的命名空间名称
        
         请求方法名
        //private string funName = string.Empty;

        public WsClient() 
        { 
            throw new NotImplementedException();
        }

        public WsClient(string url)
        {
            this.mUrl = url;
        }

        public async Task<string> Request(string param)
        {
            if (string.IsNullOrEmpty(mUrl))
            {
                return "接口地址为空!";
            }

            if (!mUrl.Contains("http"))
            {
                return "非法接口地址!";
            }

            if (string.IsNullOrEmpty(param))
            {
                return "请求参数为空!";
            }

            this.mParam = param;

            try
            {
                HttpClient httpClient = new();

                // soap参数
                string payload =
                    @"<?xml version=""1.0""?>  
                        <soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">  
                            <soap:Body>  
                            <invoke xmlns=""http://xxxxx.yyyyyyyy.com"">  
                                <xmlData>" + mParam +
                           @"</xmlData>  
                            </invoke>  
                            </soap:Body>  
                        </soap:Envelope>";

                // 发送 SOAP 请求并获取响应  
                HttpResponseMessage response = await httpClient.PostAsync(mUrl, new StringContent(payload, Encoding.UTF8, "text/xml"));

                // 解析 SOAP 响应  
                string respStr = await response.Content.ReadAsStringAsync();
                return respStr;
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine("WsClient -> Request()异常 : {0} ", e.Message);
            }

            return "";
        }
    }

调用方式:

string result = await new WsClient(wsUrl).Request(xmlData);

wsUrl: 是你的Webservice接口地址;
xmlData: 是你拼装好协议里约定的xml格式的数据参数,示例如下:
result: 是请求接口,返回的数据,也是xml格式。

<?xml version=""1.0"" encoding=""UTF-8""?>
<Request>
    <Name>
        QueryUsers
    </Name>
    <Info>
        <ID>21202012211211</ID>
    </Info>
</Request>

实现方式3 - 方式2的优化

来自朋友的优化版

public class WsClient
{
    /// <summary>
    /// 声明 HttpClient
    /// </summary>
    private HttpClient httpClient;

    /// <summary>
    /// 超时设定
    /// </summary>
    private TimeSpan Timeout = new TimeSpan(0, 0, 15);


    #region 事件声明

    /// <summary>
    /// 接收消息事件通知
    /// </summary>
    public event EventHandler<RespBean> RecvDataEvent;

    /// <summary>
    /// 请求响应异常事件通知
    /// </summary>
    public event EventHandler<ReqError> RespErrorEvent;

    #endregion

    #region 单例

    private static readonly Lazy<WsClient> _instance = new Lazy<WsClient>(() => new WsClient());

    public static WsClient Instance
    {
        get { return _instance.Value; }
    }

    private WsClient()
    {
        if (httpClient == null)
        {
            httpClient = new HttpClient(new HttpClientHandler { UseCookies = false });
            httpClient.Timeout = Timeout;
        }
    }

    #endregion


    /// <summary>
    /// 生成SOAP协议
    /// </summary>
    /// <param name="reqData"></param>
    /// <returns>完整SOAP协议,用于请求</returns>
    private string GetSoapData(string reqData)
    {
        StringBuilder soapBuilder = new StringBuilder();
        soapBuilder.Append("<?xml version=\"1.0\"?>")
                   .Append("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">")
                   .Append("<soap:Body>")
                   .AppendFormat("<invoke xmlns=\"http://specialmap.chinamap.com\"><data>{0}</data></invoke>", reqData)
                   .Append("</soap:Body>")
                   .Append("</soap:Envelope>");
        return soapBuilder.ToString();
    }

    /// <summary>
    /// Post 请求
    /// </summary>
    /// <param name="url">请求地址</param>
    /// <param name="data">请求数据体</param>
    /// <param name="header">请求头</param>
    public async Task PostData(string url, string data, List<KeyValuePair<string, string>> headers = null)
    {
        StringContent content = new StringContent(data, Encoding.UTF8, "text/xml");

        if (headers != null && headers.Count > 0)
        {
            content.Headers.Clear();

            foreach (var header in headers)
            {
                content.Headers.Add(header.Key, header.Value);
            }
        }

        Uri uri = new Uri(url);

        try
        {
            HttpResponseMessage response = await httpClient.PostAsync(uri, content);

            if (response.StatusCode == HttpStatusCode.OK)
            {
                // 接收数据
                string soapStr = await response.Content.ReadAsStringAsync();

                

                // 实际的数据协议内容
                string realStr = GetNodeValue(soapStr, "return");

                // 具体的数据类
                BaseInfo baseInfo = DeserializeObject<BaseInfo>(realStr);

                if (baseInfo == null || baseInfo.Data == null)
                {
                    RespErrorEvent?.Invoke(this, new ReqError()
                    {
                        IpAddr = new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port),
                        ErrorMsg = "协议解析失败"
                    });

                    return;
                }

                RecvDataEvent?.Invoke(this, new RespBean { Ip = url, Data = soapStr });

            }
            else // HTTP 响应错误
            {
                RespErrorEvent?.Invoke(this, new ReqError()
                {
                    IpAddr = new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port),
                    ErrorMsg = $"响应错误 HttpCode={response.StatusCode}"
                });
            }
        }
        catch (TaskCanceledException ex)
        {
            RespErrorEvent?.Invoke(this, new ReqError()
            {
                IpAddr = new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port),
                ErrorMsg = "请求超时"
            });
        }
        catch (Exception ex)
        {
            RespErrorEvent?.Invoke(this, new ReqError()
            {
                IpAddr = new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port),
                ErrorMsg = "请求错误"
            });
        }
    }

    /// <summary>
    /// 发送 Http 请求数据
    /// </summary>
    public void Request(string host, int port, string content)
    {
        Task.Run(async () =>
        {
            string url = $"http://{host}:{port}/services/FSUService";
            string soapData = GetSoapData(content);
            await PostData(url, soapData);
        });
    }

    /// <summary>
    /// 从{xmlStr}中获取节点{nodeName}的值
    /// </summary>
    public static string GetNodeValue(string xmlStr, string nodeName)
    {
        try
        {
            XDocument doc = XDocument.Parse(xmlStr);
            XElement element = doc.Descendants(nodeName).FirstOrDefault();
            return element != null ? element.Value : null;
        }
        catch (Exception)
        {
            throw;
        }

    }

    /// <summary>
    /// XML 反序列化
    /// </summary>
    public static T DeserializeObject<T>(string xmlStr)
    {
        try
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
            using (StringReader textReader = new StringReader(xmlStr))
            {
                return (T)xmlSerializer.Deserialize(textReader);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("反序列化失败:"+ex.Message);
            throw;
        }

    }
}

调用方式:

// 1 监听事件
WsClient.Instance.RecvDataEvent+= Recv_DataEvent;
WsClient.Instance.RespErrorEvent+= Resp_ErrorEvent;

// 2 发送请求
WsClient.Instance.Request(ip, port, paramsXml);

总结

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是C#调用wsdl接口的三种方法示例: 1. 使用Visual Studio自动生成代理类: ```csharp // 引用WebService接口 using WebServiceNamespace; // 创建代理类实例 WebServiceClient client = new WebServiceClient(); // 调用接口方法 string result = client.MethodName(parameter); ``` 2. 使用命令行工具wsdl.exe生成代理类: ```shell // 打开命令提示符 // 进入wsdl.exe所在目录 // 执行以下命令生成代理类 wsdl /language:c# /out:生成类的物理路径 /url:WebService接口URL或wsdl文件路径 // 在代码中引用生成的代理类 using WebServiceNamespace; // 创建代理类实例 WebServiceClient client = new WebServiceClient(); // 调用接口方法 string result = client.MethodName(parameter); ``` 3. 手动解析wsdl文件: ```csharp // 引用System.Web.Services和System.Web.Services.Description命名空间 using System.Web.Services; using System.Web.Services.Description; using System.Xml; // 创建WebService描述文件的URL string wsdlUrl = "WebService接口URL或wsdl文件路径"; // 创建ServiceDescription对象 ServiceDescription serviceDescription = ServiceDescription.Read(wsdlUrl); // 创建ServiceDescriptionImporter对象 ServiceDescriptionImporter importer = new ServiceDescriptionImporter(); importer.ProtocolName = "Soap"; // 指定协议为Soap // 添加ServiceDescription对象 importer.AddServiceDescription(serviceDescription, null, null); // 创建CodeNamespace对象 CodeNamespace codeNamespace = new CodeNamespace("WebServiceNamespace"); // 创建CodeCompileUnit对象 CodeCompileUnit codeCompileUnit = new CodeCompileUnit(); codeCompileUnit.Namespaces.Add(codeNamespace); // 生成代理类代码 ServiceDescriptionImportWarnings warnings = importer.Import(codeNamespace, codeCompileUnit); // 使用CodeDomProvider编译代理类 CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters compilerParameters = new CompilerParameters(); compilerParameters.GenerateExecutable = false; compilerParameters.GenerateInMemory = true; CompilerResults compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters, codeCompileUnit); // 获取生成的代理类类型 Type proxyType = compilerResults.CompiledAssembly.GetTypes().FirstOrDefault(t => t.Name == "WebServiceClient"); // 创建代理类实例 object proxyInstance = Activator.CreateInstance(proxyType); // 调用接口方法 MethodInfo method = proxyType.GetMethod("MethodName"); string result = (string)method.Invoke(proxyInstance, new object[] { parameter }); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值