动态调用Webservice

本文介绍了一种动态调用Webservice的方法,通过解析WSDL文档获取方法和参数信息,然后构造SOAP XML请求并利用HTTP发送。文中提供了一个C#实现的工具类,能加载WSDL、解析方法、组装调用XML,并展示了如何在VS之外进行灵活的Webservice调用。源码已公开,适用于理解Webservice交互和不支持Webservice的语言自行实现调用。
摘要由CSDN通过智能技术生成

实际场景中有很多系统交互模块使用Webservice方式。Webservice允许不同语言、平台的相互调用、提供很好的互操作性。Webservice构建在HTTP协议只上。采用HTTP协议发送符合Webservice协议的XML,既Soap文档,服务发布者实现Soap约定,服务调用者按Soap约定通过HTTP发送Soap请求,以此实现跨语言和平台。

平常开发工具比如VS都提供的有在工程上右键添加服务引用的功能。添加引用后VS会自动生成Webservice的客户端调用类。这种模式在固定Webservice的情况是没问题的。但是如果作为平台或者Webservice测试来说这种模式太僵化了。

为了得到动态调用Webservice的功能,这次按Soap协议下载Webservice的MSDL文档,通过MSDL的XML解析Webservice提供的方法,和每个方法的参数,然后渲染出方法和参数页面。再把填的参数组装成Soap调用XML通过HTTP发送给服务端,然后解析服务返回的XML结果,以此实现动态调Webservice,理解Webservice交互机制,也可供没支持Webservice的语言自己实现Webservice调用。

源码地址:https://download.csdn.net/download/zhanglianzhu_91/34445937

效果如下:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

主要类(包装了Soap的WSDL解析和构造调用XML及调用)

using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Collections.Generic;
using System.Xml;
using System.Net.Http;
using System.Text;

namespace SoapUtil
{
    ///<summary  NoteObject="Class">
    /// [功能描述: Webservice动态调用的工具类,实现下载WSDL、解析、组装调用webservice]<br></br>
    /// [创建者:   zlz]<br></br>
    /// [创建时间: 20211024]<br></br>
    /// <说明>
    ///    
    /// </说明>
    /// <修改记录>
    ///     <修改时间></修改时间>
    ///     <修改内容>
    ///            
    ///     </修改内容>
    /// </修改记录>
    /// </summary>
    public class SoapCallUtil
    {
        /// <summary>
        /// 通过URL信息得到Soap信息
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static SoapInfo GetSoapInfo(string url)
        {
            if (url == "")
            {
                return null;
            }
            
            SoapInfo retSoap = new SoapInfo();
            retSoap.UserName = "";
            retSoap.Password = "";
            //提取用户名和密码
            if (url.Contains("UserName=") && url.Contains("Password="))
            {
                //分割数据
                string[] arr = url.Split('&');
                for (int i = 1; i < arr.Length; i++)
                {
                    string[] oneParaArr = arr[i].Split('=');
                    if (oneParaArr.Length > 1)
                    {
                        if (arr[i].Contains("UserName="))
                        {
                            retSoap.UserName = oneParaArr[1];
                        }
                        else if (arr[i].Contains("Password="))
                        {
                            retSoap.Password = oneParaArr[1];
                        }
                    }
                }
            }
            retSoap.Url = url;
            retSoap.MethodList = new Dictionary<string, SoapMethodInfo>();
            //加载msdl
            string xml = LoadMsdlByUrl(url);
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);
            XmlNode xndf = null;
            foreach (XmlNode dif in doc.ChildNodes)
            {
                if (dif.LocalName == "definitions")
                {
                    xndf = dif;
                    break;
                }
            }

            //得到根节点的所有子节点
            XmlNodeList xnl = xndf.ChildNodes;
            if (xnl.Count > 0)
            {
                foreach (XmlNode n in xnl)
                {
                    if (n.LocalName == "types")
                    {
                        foreach (XmlNode n1 in n.ChildNodes)
                        {
                            if (n1.LocalName == "schema")
                            {
                                foreach (XmlNode n2 in n1.ChildNodes)
                                {
                                    if (n2.LocalName == "element" && n2.ChildNodes.Count > 0)
                                    {
                                        string MethodName = n2.Attributes.GetNamedItem("name").Value;
                                        SoapMethodInfo methodInfo = new SoapMethodInfo();
                                        methodInfo.MethodName = MethodName;
                                        methodInfo.TargetNamespace=n1.Attributes.GetNamedItem("targetNamespace").Value;
                                        methodInfo.ParaList = new List<string>();
                                        XmlNode seqNode = n2.FirstChild.FirstChild;
                                        foreach (XmlNode pn in seqNode.ChildNodes)
                                        {
                                            string ParaName = pn.Attributes.GetNamedItem("name").Value;
                                            methodInfo.ParaList.Add(ParaName);
                                        }
                                        retSoap.MethodList.Add(MethodName, methodInfo);
                                    }
                                }
                            }
                        }
                    }
                    if (n.LocalName == "binding")
                    {
                        foreach (XmlNode n1 in n.ChildNodes)
                        {
                            if (n1.LocalName == "operation")
                            {
                                string MethodName = n1.Attributes.GetNamedItem("name").Value;
                                XmlNode actionNode = n1.FirstChild;
                                string action = actionNode.Attributes.GetNamedItem("soapAction").Value;
                                if (retSoap.MethodList.ContainsKey(MethodName))
                                {
                                    retSoap.MethodList[MethodName].SoapAction = action;
                                }
                            }
                        }
                    }
                    if (n.LocalName == "service")
                    {
                        XmlNode sNode = n.FirstChild.FirstChild;
                        retSoap.Url = sNode.Attributes.GetNamedItem("location").Value;
                    }
                }
            }
            if (retSoap.MethodList.Count > 0)
            {
                List<string> delList = new List<string>();
                foreach (var m in retSoap.MethodList)
                {
                    if (m.Value.SoapAction == null || m.Value.SoapAction == "")
                    {
                        delList.Add(m.Key);
                    }
                }
                for (int i = 0; i < delList.Count; i++)
                {
                    retSoap.MethodList.Remove(delList[i]);
                }
            }
            return retSoap;
        }

        /// <summary>
        /// 得到Soap调用的xml串
        /// </summary>
        /// <param name="address">webservice地址</param>
        /// <param name="methodInfo">方法信息</param>
        /// <param name="para">参数</param>
        /// <param name="userName">用户名(有的话就给)</param>
        /// <param name="userPass">密码(有的话就给)</param>
        /// <returns>调用xml</returns>
        public static string GetSoapXml(string address, SoapMethodInfo methodInfo, Dictionary<string, string> para, string userName = "", string userPass = "")
        {
            StringBuilder sbxml = new StringBuilder();
            sbxml.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            sbxml.Append("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">");

            if (userName != "" && userPass != "")
            {
                sbxml.Append("<soap:Header>");
                sbxml.Append("<wsa:Action>" + methodInfo.SoapAction + "</wsa:Action>");
                sbxml.Append("<wsa:ReplyTo>");
                sbxml.Append("<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>");
                sbxml.Append("</wsa:ReplyTo>");
                sbxml.Append("<wsa:To>" + address + "</wsa:To>");
                sbxml.Append("<wsse:Security soap:mustUnderstand=\"1\">");
                sbxml.Append("<wsse:UsernameToken xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" wsu:Id=\"SecurityToken-29db7c92-1098-4717-b7fb-aac473632fec\">");
                sbxml.Append("<wsse:Username>" + userName + "</wsse:Username>");
                sbxml.Append("<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" + userPass + "</wsse:Password>");
                sbxml.Append("</wsse:UsernameToken>");
                sbxml.Append("</wsse:Security>");
                sbxml.Append("</soap:Header>");
            }
            sbxml.Append("<soap:Body>");
            if(methodInfo.TargetNamespace!=null&& methodInfo.TargetNamespace!="")
            {
                sbxml.Append("<" + methodInfo.MethodName + " xmlns=\""+ methodInfo.TargetNamespace + "\">");
            }
            else
            {
                sbxml.Append("<" + methodInfo.MethodName + ">");
            }
            
            foreach (var m in methodInfo.ParaList)
            {
                sbxml.Append("<" + m + ">" + para[m].Replace("&", "&amp;").Replace("'", "&apos;").Replace("\"", "&quot;").Replace(">", "&gt;").Replace("<", "&lt;") + "</" + m + ">");
            }
            sbxml.Append("</" + methodInfo.MethodName + ">");
            sbxml.Append("</soap:Body>");
            sbxml.Append("</soap:Envelope>");
            return sbxml.ToString();
        }

        /// <summary>
        /// 到指定地址发送xml执行Soap
        /// </summary>
        /// <param name="address">webservice地址</param>
        /// <param name="xml">soap的xml</param>
        /// <returns></returns>
        public static string InVokeSoapMethod(string address, string action, string xml)
        {
            string result = string.Empty;
            HttpContent content = new StringContent(xml, Encoding.UTF8, "text/xml");
            content.Headers.Add("SOAPAction", action);
            IHttpClientFactory factory = HttpClientFactoryUtil.GetFactory();
            using (HttpClient client = factory.CreateClient("webservice"))
            {
                client.Timeout = new TimeSpan(1, 0, 0);
                using (var response = client.PostAsync(address, content))
                {
                    result = response.Result.Content.ReadAsStringAsync().Result;
                    client.Dispose();
                    //创建一个xml文档
                    XmlDocument xmlDoc = new XmlDocument();
                    //为文档导入数据
                    xmlDoc.LoadXml(result);
                    result = xmlDoc.InnerText;
                }
            }
            return result;
        }

        /// <summary>
        /// 通过url加载msdl
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private static string LoadMsdlByUrl(string url)
        {
            string retStr = "";
            HttpWebRequest request = null;
            if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
            {
                request = WebRequest.Create(url) as HttpWebRequest;
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
                request.ProtocolVersion = HttpVersion.Version11;
                // 这里设置了协议类型。
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
                request.KeepAlive = true;
                ServicePointManager.CheckCertificateRevocationList = true;
                ServicePointManager.DefaultConnectionLimit = 100;
                ServicePointManager.Expect100Continue = false;
            }
            else
            {
                request = (HttpWebRequest)WebRequest.Create(url);
            }
            //重要,设置Cookie才能跳转会话不失效
            CookieContainer myCookieContainer = new CookieContainer();
            request.CookieContainer = myCookieContainer;
            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            Stream responseStream = response.GetResponseStream();
            StreamReader myread = new StreamReader(responseStream);
            retStr = myread.ReadToEnd();
            retStr = retStr.Replace("\r\n", "");
            responseStream.Close();
            return retStr;
        }

        /// <summary>
        /// 回调
        /// </summary>
        /// <param name="sender">触发者</param>
        /// <param name="certificate">证书</param>
        /// <param name="chain"></param>
        /// <param name="errors"></param>
        /// <returns></returns>
        private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            //总是接受  
            return true;
        }
    }

    /// <summary>
    /// Soap信息类
    /// </summary>
    public class SoapInfo
    {
        /// <summary>
        /// URL
        /// </summary>
        public string Url
        {
            get;
            set;
        }

        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName
        {
            get;
            set;
        }

        /// <summary>
        /// 密码
        /// </summary>
        public string Password
        {
            get;
            set;
        }

        /// <summary>
        /// 方法列表
        /// </summary>
        public Dictionary<string, SoapMethodInfo> MethodList
        {
            get;
            set;
        }
    }

    /// <summary>
    /// 方法信息类
    /// </summary>
    public class SoapMethodInfo
    {
        /// <summary>
        /// 方法名字
        /// </summary>
        public string MethodName
        {
            get;
            set;
        }

        /// <summary>
        /// 触发路径
        /// </summary>
        public string SoapAction
        {
            get;
            set;
        }

        /// <summary>
        /// 方法命名空间
        /// </summary>
        public string TargetNamespace
        {
            get;
            set;
        }

        /// <summary>
        /// 参数列表
        /// </summary>
        public List<string> ParaList
        {
            get;
            set;
        }
    }
}

界面代码(根据Soap信息动态渲染调用界面和执行调用)

using SoapUtil;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SoapCall
{
    ///<summary  NoteObject="Class">
    /// [功能描述: 基于动态调用Webservice工具类包装的动态调用Webservice工具]<br></br>
    /// [创建者:   zlz]<br></br>
    /// [创建时间: 20211024]<br></br>
    /// <说明>
    ///    
    /// </说明>
    /// <修改记录>
    ///     <修改时间></修改时间>
    ///     <修改内容>
    ///            
    ///     </修改内容>
    /// </修改记录>
    /// </summary>
    public partial class Form1 : Form
    {
        /// <summary>
        /// 返回信息
        /// </summary>
        RichTextBox retTxt = new RichTextBox();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// 加载msdl
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLoad_Click(object sender, EventArgs e)
        {
            pnlMethod.Controls.Clear();
            pnlPara.Controls.Clear();
            //得到Soap信息
            SoapInfo info=SoapUtil.SoapCallUtil.GetSoapInfo(txtUrl.Text);
            pnlMethod.Tag = info;
            if (info.MethodList!=null&&info.MethodList.Count>0)
            {
                int top = 10;
                Label labp = new Label();
                labp.Left = 20;
                labp.Top = top;
                labp.ForeColor = Color.Red;
                labp.Text = "服务包含以下方法";
                labp.Width = pnlMethod.Width - 30;
                pnlMethod.Controls.Add(labp);
                top += 30;
                foreach (string method in info.MethodList.Keys)
                {
                    Label lab = new Label();
                    lab.Left = 20;
                    lab.Top = top;
                    lab.ForeColor = Color.Blue;
                    top += 30;
                    lab.Text = method;
                    lab.Width = pnlMethod.Width - 30;
                    lab.Tag = info.MethodList[method];
                    lab.Click += Lab_Click;
                    pnlMethod.Controls.Add(lab);
                }
            }
        }

        /// <summary>
        /// 单击渲染方法调用
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Lab_Click(object sender, EventArgs e)
        {
            pnlPara.Controls.Clear();
            Label lab = sender as Label;
            SoapMethodInfo method=lab.Tag as SoapMethodInfo;
            
            if (method.ParaList!=null&& method.ParaList.Count>0)
            {
                int top = 10;
                Label labp = new Label();
                labp.Left = 20;
                labp.Top = top;
                labp.ForeColor = Color.Red;
                labp.Width = pnlPara.Width - 170;
                labp.Text = method.MethodName+"调用测试"+ ":参数本身是xml的用<![CDATA[参数]]>";
                pnlPara.Controls.Add(labp);
                top += 30;
                foreach (string s in method.ParaList)
                {
                    labp = new Label();
                    labp.Left = 20;
                    labp.Top = top;
                    labp.ForeColor = Color.Black;
                    labp.Text = s;
                    labp.Width = 130;
                    pnlPara.Controls.Add(labp);
                    TextBox txt = new TextBox();
                    txt.Width = pnlPara.Width - 170;
                    txt.Left = 150;
                    txt.Top = top;
                    txt.Tag = s;
                    pnlPara.Controls.Add(txt);
                    top += 30;
                }
                Button btn= new Button();
                btn.Left = 150;
                btn.Top = top;
                btn.Text = "调用";
                btn.Click += Btn_Click;
                btn.Tag = method;
                pnlPara.Controls.Add(btn);
                top += 30;

                retTxt.Text = "";
                retTxt.Left = 150;
                retTxt.Top = top;
                retTxt.Width = pnlPara.Width - 170;
                retTxt.Height = pnlPara.Height-top-20;
                pnlPara.Controls.Add(retTxt);

            }
        }
        /// <summary>
        /// 调用方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Btn_Click(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            SoapMethodInfo method = btn.Tag as SoapMethodInfo;
            SoapInfo soap = pnlMethod.Tag as SoapInfo;
            Dictionary<string, string> dicPara = new Dictionary<string, string>();
            foreach(Control c in pnlPara.Controls)
            {
                if(c is TextBox)
                {
                    TextBox txt = c as TextBox;
                    string paraName = txt.Tag.ToString();
                    string val = txt.Text;
                    dicPara.Add(paraName,val);
                }
            }
            string xml = SoapCallUtil.GetSoapXml(soap.Url, method, dicPara, soap.UserName, soap.Password);
            string ret=SoapCallUtil.InVokeSoapMethod(soap.Url, method.SoapAction,xml);
            retTxt.Text = ret;
        }
    }
}

更多细节,自己对代码和Webservice协议,这里可以把发送HTTP请求 逻辑不用微软的HttpClient,自己用TCP实现HttpClient的发送部分,下一个实现己用TCP发送Http请求,替换Webservice调用的发送部分。

也可以轻松包装成动态调用Webservice的Web页面-这代码就不对外了
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小乌鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值