C# 动态调用WebService

本文介绍了一种在C#中动态调用WebService的方法,通过反射生成WebService客户端代理类,并提供了具体的实现代码。该方法适用于开发环境中无法直接访问WebService的情况。

 创建方法

.net Framework

using Microsoft.CSharp;
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Web.Services.Description;
using System.Web.Services.Protocols;

namespace HealthyCardPostApp.Helper
{
    public class WebServiceHelper
    {
        // 用Dictionary保存已经反射生成过的WebService和Method,省去每次调用都重新生成实例
        #region 动态调用WebService

        /// <summary>
        /// WebService服务列表
        /// </summary>
        private static ConcurrentDictionary<string, object> ServiceDictionary = new ConcurrentDictionary<string, object>();

        /// <summary>
        /// WebService Method方法列表
        /// </summary>
        private static ConcurrentDictionary<string, System.Reflection.MethodInfo> MethodDictionary = new ConcurrentDictionary<string, System.Reflection.MethodInfo>();


        /// <summary>
        /// 动态调用WebService(开发环境ping不通需要调用的WebService地址时使用)
        /// </summary>
        /// <param name="url">webservice地址</param>
        /// <param name="methodName">需要调用的方法名称</param>
        /// <param name="param">调用上方法时传入的参数</param>
        /// <returns>执行方法后返回的数据</returns>
        public static async Task<object> InvokeWebService(string url, string methodName, string[] param)
        {
            try
            {
                if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(methodName))
                {
                    return null;
                }
                url = url.ToLower().EndsWith("wsdl") ? url : (url + "?WSDL");
                // 检查是否已经存在WebService服务 和 方法
                string key = url + methodName;
                if (!ServiceDictionary.ContainsKey(key) || !MethodDictionary.ContainsKey(key))
                {
                    //客户端代理服务命名空间,可以设置成需要的值。
                    string space = string.Format("ProxyServiceReference");

                    //获取WSDL
                    WebClient webClient = new WebClient();
                    Stream stream = webClient.OpenRead(url);
                    ServiceDescription description = ServiceDescription.Read(stream);//服务的描述信息都可以通过ServiceDescription获取
                    string classname = description.Services[0].Name;

                    ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
                    descriptionImporter.AddServiceDescription(description, "", "");
                    CodeNamespace codeNamespace = new CodeNamespace(space);

                    //生成客户端代理类代码
                    CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
                    codeCompileUnit.Namespaces.Add(codeNamespace);
                    descriptionImporter.Import(codeNamespace, codeCompileUnit);
                    CSharpCodeProvider provider = new CSharpCodeProvider();

                    //设定编译参数
                    CompilerParameters comilerParameters = new CompilerParameters();
                    comilerParameters.GenerateExecutable = false;
                    comilerParameters.GenerateInMemory = true;
                    comilerParameters.ReferencedAssemblies.Add("System.dll");
                    comilerParameters.ReferencedAssemblies.Add("System.XML.dll");
                    comilerParameters.ReferencedAssemblies.Add("System.Web.Services.dll");
                    comilerParameters.ReferencedAssemblies.Add("System.Data.dll");

                    //编译代理类
                    CompilerResults compilerResult = provider.CompileAssemblyFromDom(comilerParameters, codeCompileUnit);
                    if (compilerResult.Errors.HasErrors == true)
                    {
                        System.Text.StringBuilder sb = new System.Text.StringBuilder();
                        foreach (System.CodeDom.Compiler.CompilerError ce in compilerResult.Errors)
                        {
                            sb.Append(ce.ToString());
                            sb.Append(System.Environment.NewLine);
                        }
                        throw new Exception(sb.ToString());
                    }

                    //生成代理实例,并调用方法
                    System.Reflection.Assembly assembly = compilerResult.CompiledAssembly;
                    Type service = assembly.GetType(space + "." + classname, true, true);

                    if (!ServiceDictionary.ContainsKey(key))
                    {
                        object wsInstance = Activator.CreateInstance(service);
                        //((SoapHttpClientProtocol)wsInstance).Timeout = int.MaxValue;
                        //部分url会改变,重新赋值
                        //((SoapHttpClientProtocol)wsInstance).Url = url.Replace("?wsdl", "");
                        ServiceDictionary.TryAdd(key, wsInstance);
                    }

                    System.Reflection.MethodInfo SSOServiceMethod = service.GetMethod(methodName);
                    MethodDictionary.TryAdd(key, SSOServiceMethod);
                }

                return await Task.Run(() => { 
                    return MethodDictionary[key].Invoke(ServiceDictionary[key], param); 
                });
            }
            catch (Exception ex)
            {
                LogHelper.LogError<WebServiceHelper>($"调用服务时发生错误:{url} {methodName} {string.Join(",", param)}\n{ex}");
                return null;
            }

        }

        #endregion

    }
}

.net6

using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Linq;

public static class WebServiceHelper
{
    /// <summary>
    /// 动态调用 ASMX WebService
    /// </summary>
    /// <param name="url">asmx 地址</param>
    /// <param name="methodName">方法名</param>
    /// <param name="parameters">参数名=值</param>
    /// <param name="muti">多个结果值</param>
    public static async Task<object> InvokeAsync(string url, string methodName, Dictionary<string, string>? parameters = null, bool muti = false)
    {
        if (string.IsNullOrWhiteSpace(url))
            throw new ArgumentNullException(nameof(url));

        if (string.IsNullOrWhiteSpace(methodName))
            throw new ArgumentNullException(nameof(methodName));

        // 1. 构造 SOAP 请求
        string soapXml = BuildSoapXml(methodName, parameters);

        // 2. 发送请求
        using var client = new HttpClient();
        var content = new StringContent(soapXml, Encoding.UTF8, "text/xml");

        var response = await client.PostAsync(url, content);

        response.EnsureSuccessStatusCode();

        // 3. 解析 SOAP 响应
        string xml = await response.Content.ReadAsStringAsync();
        return ExtractResult(xml, methodName, muti);
    }

    public static object InvokeAsync(string v1, string v2, Dictionary<string, string> dictionary, object[] objects)
    {
        throw new NotImplementedException();
    }

    #region Private Methods

    private static string BuildSoapXml(string method, Dictionary<string, string>? parameters)
    {
        var sb = new StringBuilder();
        sb.AppendLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
        sb.AppendLine(@"<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" ");
        sb.AppendLine(@"xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" ");
        sb.AppendLine(@"xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">");
        sb.AppendLine("<soap:Body>");

        sb.AppendLine($"<{method} xmlns=\"http://tempuri.org/\">");

        if (parameters != null)
        {
            foreach (var p in parameters)
            {
                sb.AppendLine($"<{p.Key}>{p.Value}</{p.Key}>");
            }
        }

        sb.AppendLine($"</{method}>");
        sb.AppendLine("</soap:Body>");
        sb.AppendLine("</soap:Envelope>");

        return sb.ToString();
    }

    private static object ExtractResult(string soapXml, string methodName, bool muti)
    {
        var doc = new XmlDocument();
        doc.LoadXml(soapXml);

        var ns = new XmlNamespaceManager(doc.NameTable);
        ns.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

        if (muti)
        {
            var nodes = doc.SelectNodes($"//soap:Body/*[local-name()='{methodName}Response']/*[local-name()='{methodName}Result']", ns);
            List<string> result = new List<string>();
            foreach (XmlNode node in nodes)
            {
                result.Add(node?.InnerText ?? string.Empty);
            }
            return result;
        }
        else
        {
            var node = doc.SelectSingleNode($"//soap:Body/*[local-name()='{methodName}Response']/*[local-name()='{methodName}Result']", ns);
            return node?.InnerText ?? string.Empty;
        }
        
    }

    #endregion
}

方法使用

使用InvokeWebService 方法时注意事项:

1、入参只需要传数据值,不需要带Web Service的入参名称

2、如果Web Service的有多个入参,需要注意入参顺序

3、如果入参类型为xml,考虑用<![CDATA[]]>封装(如果CDATA封装后报错xml格式错误,则不需要使用CDATA封装)

WebServiceHelper.InvokeWebService("http://xxxxx:xx/xxxWebServices.asmx", 
    "方法名", 
    new object[] { "字符串入参1", "<![CDATA[xml入参2]]>" });

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值