2021-01-21 乐帮网
c# webservice
使用Web Service服务在C#中非常方便,使用静态调用尤其简单。但是静态调也有它的弊端,就是当接口改动时不管你用没有用到新方法新实体,都需要重新生成然后再编译再部署。这个过程不容易掌控。所以就有了动态调用这一说。这篇文章只讨论动态调用。目前WCF也算 比较特殊的Web Service,这里也包含在内。
Web Service的动态调用也有两种常用方法,
方法一:通过C#内部工具动态生成dll类库,然后利用反射调用(推荐)
方法二:可以直接使用http 请求通过合成符合soap协议的消息体来调用方法。
两种方法各有利弊,如何就是在C#平台开发那建议你使用方法一,如何是在其它平台非.net平台,那么建议你使用方法二。
下面开始介绍方法一,在这里我们不仅实现调用,还要把服务中所有的方法以及参数描述信息保存在类中,以方便查看。到最后会提供示例源码下载。
(1)首先建两个测试用的Web Service,一个是asmx一个是wcf方式。我们分别是添加代码如下:
Models
[DataContract]
public class WxBase
{
[DataMember]
public int errcode { get; set; }
[DataMember]
public string errmsg { get; set; }
}
[DataContract]
public class WxResUser : WxBase
{
[DataMember]
public string access_token { get; set; }
[DataMember]
public int expires_in { get; set; }
[DataMember]
public string refresh_token { get; set; }
[DataMember]
public string openid { get; set; }
[DataMember]
public string scope { get; set; }
}
WxTest.asmx
/// <summary>
/// WxTest 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。
// [System.Web.Script.Services.ScriptService]
public class WxTest : System.Web.Services.WebService
{
[WebMethod(Description = "获取用户")]
public WxResUser GetUser(string openid)
{
return new WxResUser()
{
openid = openid,
access_token = "202101171203ef76ab",
expires_in = 7200,
scope = "lebang2020.cn",
refresh_token=string.Empty
};
}
}
WxTest.svc
public class WxTest : IWxTest
{
[OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
public WxResUser GetUser(string openid)
{
return new WxResUser()
{
openid = openid,
access_token="202101171203ef76ab",
expires_in=7200,
scope="lebang2020.cn",
refresh_token="roodke334dswssqwz"
};
}
}
IWxTest.cs
[ServiceContract]
//[WebService(Namespace = "vkt.soap.demo.wcf.wxtest")]
public interface IWxTest
{
[OperationContract]
WxResUser GetUser(string openid);
}
(2)下面写我们的主要实现逻辑,我把核心代码粘贴到这里。
public class WebServiceInfo
{
const string _serviceNamespace = "vkt.test";
WebMethodInfoCollection _webMethods = new WebMethodInfoCollection();
Uri _url;
static Dictionary<string, WebServiceInfo> _webServiceInfos =
new Dictionary<string, WebServiceInfo>();
Assembly _assembly;
string _className;
object _service;
/// <summary>
/// Constructor creates the web service info from the given url.
/// </summary>
/// <param name="url">
private WebServiceInfo(Uri url)
{
if (url == null)
throw new ArgumentNullException("url");
_url = url;
_webMethods = GetWebServiceDescription(url);
}
/// <summary>
/// Factory method for WebServiceInfo. Maintains a hashtable WebServiceInfo objects
/// keyed by url in order to cache previously accessed wsdl files.
/// </summary>
/// <param name="url">
/// <returns></returns>
public static WebServiceInfo OpenWsdl(Uri url)
{
WebServiceInfo webServiceInfo;
if (!_webServiceInfos.TryGetValue(url.ToString(), out webServiceInfo))
{
webServiceInfo = new WebServiceInfo(url);
_webServiceInfos.Add(url.ToString(), webServiceInfo);
}
return webServiceInfo;
}
/// <summary>
/// Convenience overload that takes a string url
/// </summary>
/// <param name="url">
/// <returns></returns>
public static WebServiceInfo OpenWsdl(string url)
{
Uri uri = new Uri(url);
return OpenWsdl(uri);
}
private void BuildLib(ServiceDescription description)
{
try
{
_className = description.Services[0].Name;
//ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter()
{
ProtocolName = "Soap",
Style = ServiceDescriptionImportStyle.Client,
CodeGenerationOptions =
CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync
};
descriptionImporter.AddServiceDescription(description, string.Empty, string.Empty);
CodeNamespace nameSpace = new CodeNamespace(_serviceNamespace);
CodeCompileUnit compile = new CodeCompileUnit();
compile.Namespaces.Add(nameSpace);
descriptionImporter.Import(nameSpace, compile);
CompilerParameters paras = new CompilerParameters();
paras.GenerateExecutable = false;
paras.GenerateInMemory = true;
paras.ReferencedAssemblies.Add("System.dll");
paras.ReferencedAssemblies.Add("System.XML.dll");
paras.ReferencedAssemblies.Add("System.Web.Services.dll");
paras.ReferencedAssemblies.Add("System.Data.dll");
using (CSharpCodeProvider codeProvider = new CSharpCodeProvider())
{
CompilerResults compilerResults = codeProvider.CompileAssemblyFromDom(paras, compile);
if (true == compilerResults.Errors.HasErrors)
{
throw new Exception(compilerResults.Errors[0].ErrorText);
}
_assembly = compilerResults.CompiledAssembly;
if (_assembly != null)
{
_service = _assembly.CreateInstance($"{ _serviceNamespace}.{_className}", true);
}
}
}
catch(Exception ex)
{
throw ex;
}
}
/// <summary>
/// Load the WSDL file from the given url.
/// Use the ServiceDescription class to walk the wsdl and create the WebServiceInfo
/// instance.
/// </summary>
/// <param name="url">
private WebMethodInfoCollection GetWebServiceDescription(Uri url)
{
UriBuilder uriBuilder = new UriBuilder(url);
//uriBuilder.Query = "WSDL";
WebMethodInfoCollection webMethodInfos = new WebMethodInfoCollection();
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uriBuilder.Uri);
//webRequest.ContentType = "text/xml;charset=\"utf-8\"";
webRequest.ContentType = "application/soap+xml;charset=\"utf-8\"";
webRequest.Method = "GET";
webRequest.Accept = "text/xml";
ServiceDescription serviceDescription;
using (System.Net.WebResponse response = webRequest.GetResponse())
using (System.IO.Stream stream = response.GetResponseStream())
{
serviceDescription = ServiceDescription.Read(stream);
}
if(serviceDescription !=null)
{
BuildLib(serviceDescription);
}
foreach (PortType portType in serviceDescription.PortTypes)
{
foreach (Operation operation in portType.Operations)
{
string operationName = operation.Name;
string inputMessageName = operation.Messages.Input.Message.Name;
string outputMessageName = operation.Messages.Output.Message.Name;
// get the message part
string inputMessagePartName =
serviceDescription.Messages[inputMessageName].Parts[0].Element.Name;
string outputMessagePartName =
serviceDescription.Messages[outputMessageName].Parts[0].Element.Name;
// get the parameter name and type
Parameter[] inputParameters = GetParameters(serviceDescription, inputMessagePartName);
Parameter[] outputParameters = GetParameters(serviceDescription, outputMessagePartName);
WebMethodInfo webMethodInfo = new WebMethodInfo(
operation.Name, inputParameters, outputParameters);
webMethodInfos.Add(webMethodInfo);
}
}
return webMethodInfos;
}
/// <summary>
/// Walk the schema definition to find the parameters of the given message.
/// </summary>
/// <param name="serviceDescription">
/// <param name="messagePartName">
/// <returns></returns>
private static Parameter[] GetParameters(ServiceDescription serviceDescription, string messagePartName)
{
List<Parameter> parameters = new List<Parameter>();
Types types = serviceDescription.Types;
System.Xml.Schema.XmlSchema xmlSchema = types.Schemas[0];
foreach (object item in xmlSchema.Items)
{
System.Xml.Schema.XmlSchemaElement schemaElement = item as System.Xml.Schema.XmlSchemaElement;
if (schemaElement != null)
{
if (schemaElement.Name == messagePartName)
{
System.Xml.Schema.XmlSchemaType schemaType = schemaElement.SchemaType;
System.Xml.Schema.XmlSchemaComplexType complexType = schemaType as System.Xml.Schema.XmlSchemaComplexType;
if (complexType != null)
{
System.Xml.Schema.XmlSchemaParticle particle = complexType.Particle;
System.Xml.Schema.XmlSchemaSequence sequence = particle as System.Xml.Schema.XmlSchemaSequence;
if (sequence != null)
{
foreach (System.Xml.Schema.XmlSchemaElement childElement in sequence.Items)
{
string parameterName = childElement.Name;
string parameterType = childElement.SchemaTypeName.Name;
parameters.Add(new Parameter(parameterName, parameterType));
}
}
}
}
}
}
return parameters.ToArray();
}
public string Invoke(string methodName,params object[] args)
{
object result = null;
if (_service != null)
{
if (_webMethods.Contains(methodName))
{
IList<Type> types = new List<Type>();
foreach( var p in args)
{
types.Add(p.GetType());
}
MethodInfo mi = _service.GetType().GetMethod(methodName, types.ToArray());
if (mi == null)
throw new Exception("服务中未找到方法" + methodName);
result = mi.Invoke(_service, args);
//类型的特殊处理可写到这里
if (result is string)
return result as string;
}
}
return JsonConvert.SerializeObject(result);
}
/// <summary>
/// WebMethodInfo
/// </summary>
public WebMethodInfoCollection WebMethods
{
get { return _webMethods; }
}
/// <summary>
/// Url
/// </summary>
public Uri Url
{
get { return _url; }
set { _url = value; }
}
}
(3)使用方法如下 :
[TestMethod]
public void TestMethod1()
{
string wsdlAddress = "http://localhost/wcftest2/WxTest.asmx?wsdl";
WebServiceInfo webServiceInfo = WebServiceInfo.OpenWsdl(wsdlAddress);
var result = webServiceInfo.Invoke("GetUser", "9887445dfe");
}
写在最后,以上的方法是返回的实体序列化的Json格式,当然你可以优化成实体。核心思想就是动态下载服务描述,根据描述生成dll然后再加载。通过反射调用WebService中的方法。对于wcf和asmx的服务都通用。其中wcf是指绑定方式是basicHttpBinding的配置。
下一篇将会带来通过Http方式调用的代码。
可以下载我的示例代码链接:https://pan.baidu.com/s/1fyQzqQtZMfb00zCT9Ak-WQ
提取码请点击下方的捐赠按钮获取。
包含两种调用方式的示例代码,以及webservice 和 wcf接口示例。
参考:
Dynamic call of SOAP base Web Service
Calling Web Service Using SOAP Request In Console Application
How to send/receive SOAP request and response using C# in Windows Phone 8
关注我的微信公众号
在公众号里留言交流
投稿邮箱:1052839972@qq.com
庭院深深深几许?杨柳堆烟,帘幕无重数。
玉勒雕鞍游冶处,楼高不见章台路。
雨横风狂三月暮。门掩黄昏,无计留春住。
泪眼问花花不语,乱红飞过秋千去。
如果感觉对您有帮助
欢迎向作者提供捐赠
这将是创作的最大动力