前言

  站在开发者的角度,WebService 技术确实是不再“时髦”。甚至很多人会说,我们不再用它。当然,为了使软件可以更简洁,更有层次,更易于实现缓存等机制,我是非常建议将 SOAP 转为 RESTful 架构风格的。但到目前为止,WebService 在一些Public Institution 中使用还是十分广泛的。

  这里主要讨论一下关于WebService的调用问题。关于WebService 的调用分为静态调用和动态调用两种。

静态调用

  静态调用的方式是通过“Add Service Reference...”创建客户端代理类。这种方式让VS.NET环境来为我们生成服务代理,然后调用对应的Web服务。这样是使工作简单了,但是却将提供Web服务的URL、方法名、参数绑定在一起了,这是VS.NET自动为我们生成Web服务代理的限制。如果发布Web服务的URL改变了,则我们需要重新让VS.NET生成代理,并重新编译。很常见的一个场景,某银行Web服务,因为部署的URL更改,而不得不去重新编译生成代理,这将会带来很多不必要的工作量。如果我们使用动态调用就可以避免这种情况。关于静态调用,不是这篇文章的重点,故不作详细介绍。

动态调用

  在某些情况下我们需要在程序运行期间动态调用一个服务。在 .NET Framework 的 System.Web.Services.Description 命名空间中有我们需要的东西。动态调用有动态调用 WebService、生成客户端代理程序集文件、生成客户端代理类源代码3种方式。

动态调用的具体步骤为:

1)从目标 URL 下载 WSDL 数据;

2)使用 ServiceDescription 创建和格式化 WSDL 文档文件;

3)使用 ServiceDescriptionImporter 创建客户端代理类;

4)使用 CodeDom 动态创建客户端代理类程序集;

5)利用反射调用相关 WebService 方法。

  第一种方式通过在内存中创建动态程序集的方式完成了动态调用过程;第二种方式将客户端代理类生成程序集文件保存到硬盘,然后可以通过 Assembly.LoadFrom() 载入并进行反射调用。对于需要多次调用的系统,要比每次生成动态程序集效率高出很多;第三种方式是保存源码文件到硬盘中,然后再进行反射调用。

  这里将只讨论第二种方式,这种方式也是我们在实际应用中最常用的。这种方式只下载 一次 WSDL 信息并创建代理类的程序集。往后程序每次启动都会反射之前创建好的程序集。如果是 Web服务 URL 变更,只需要修改 App.config 中的 WebServiceUrl 和 ProxyClassName 配置项,并将程序根目录下生成的程序集删除即可。下次程序启动又会重新下载WSDL信息并创建代理类的程序集。

  App.config文件。

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<!--WebService地址-->
<add key="WebServiceUrl" value="http://localhost:25060/testService/" />
<!--WebService输出dll文件名称-->
<add key="OutputDllFilename" value="TestWebService.dll" />
<!--WebService代理类名称-->
<add key="ProxyClassName" value="TestService" />
</appSettings>
</configuration>

  创建代理类。

     public class WSHelper
{
/// <summary>
/// 输出的dll文件名称
/// </summary>
private static string m_OutputDllFilename; /// <summary>
/// WebService代理类名称
/// </summary>
private static string m_ProxyClassName; /// <summary>
/// WebService代理类实例
/// </summary>
private static object m_ObjInvoke; /// <summary>
/// 接口方法字典
/// </summary>
private static Dictionary<EMethod, MethodInfo> m_MethodDic = new Dictionary<EMethod, MethodInfo>(); /// <summary>
/// 创建WebService,生成客户端代理程序集文件
/// </summary>
/// <param name="error">错误信息</param>
/// <returns>返回:true或false</returns>
public static bool CreateWebService(out string error)
{
try
{
error = string.Empty;
m_OutputDllFilename = ConfigurationManager.AppSettings["OutputDllFilename"];
m_ProxyClassName = ConfigurationManager.AppSettings["ProxyClassName"];
string webServiceUrl = ConfigurationManager.AppSettings["WebServiceUrl"];
webServiceUrl += "?WSDL"; // 如果程序集已存在,直接使用
if (File.Exists(Path.Combine(Environment.CurrentDirectory, m_OutputDllFilename)))
{
BuildMethods();
return true;
} //使用 WebClient 下载 WSDL 信息。
WebClient web = new WebClient();
Stream stream = web.OpenRead(webServiceUrl); //创建和格式化 WSDL 文档。
if (stream != null)
{
// 格式化WSDL
ServiceDescription description = ServiceDescription.Read(stream); // 创建客户端代理类。
ServiceDescriptionImporter importer = new ServiceDescriptionImporter
{
ProtocolName = "Soap",
Style = ServiceDescriptionImportStyle.Client,
CodeGenerationOptions =
CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync
}; // 添加 WSDL 文档。
importer.AddServiceDescription(description, null, null); //使用 CodeDom 编译客户端代理类。
CodeNamespace nmspace = new CodeNamespace();
CodeCompileUnit unit = new CodeCompileUnit();
unit.Namespaces.Add(nmspace); ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters parameter = new CompilerParameters
{
GenerateExecutable = false,
// 指定输出dll文件名。
OutputAssembly = m_OutputDllFilename
}; parameter.ReferencedAssemblies.Add("System.dll");
parameter.ReferencedAssemblies.Add("System.XML.dll");
parameter.ReferencedAssemblies.Add("System.Web.Services.dll");
parameter.ReferencedAssemblies.Add("System.Data.dll"); // 编译输出程序集
CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit); // 使用 Reflection 调用 WebService。
if (!result.Errors.HasErrors)
{
BuildMethods();
return true;
}
else
{
error = "反射生成dll文件时异常";
}
stream.Close();
stream.Dispose();
}
else
{
error = "打开WebServiceUrl失败";
}
}
catch (Exception ex)
{
error = ex.Message;
}
return false;
} /// <summary>
/// 反射构建Methods
/// </summary>
private static void BuildMethods()
{
Assembly asm = Assembly.LoadFrom(m_OutputDllFilename);
//var types = asm.GetTypes();
Type asmType = asm.GetType(m_ProxyClassName);
m_ObjInvoke = Activator.CreateInstance(asmType); //var methods = asmType.GetMethods();
var methods = Enum.GetNames(typeof(EMethod)).ToList();
foreach (var item in methods)
{
var methodInfo = asmType.GetMethod(item);
if (methodInfo != null)
{
var method = (EMethod)Enum.Parse(typeof(EMethod), item);
m_MethodDic.Add(method, methodInfo);
}
}
} /// <summary>
/// 获取请求响应
/// </summary>
/// <param name="method">方法</param>
/// <param name="para">参数</param>
/// <returns>返回:Json串</returns>
public static string GetResponseString(EMethod method, params object[] para)
{
string result = null;
if (m_MethodDic.ContainsKey(method))
{
var temp = m_MethodDic[method].Invoke(m_ObjInvoke, para);
if (temp != null)
{
result = temp.ToString();
}
}
return result;
}
}

  调用接口。

        // SOAP 请求响应方式
TextBox3.Text = WSHelper.GetResponseString(EMethod.Add, Convert.ToInt32(TextBox1.Text), Convert.ToInt32(TextBox2.Text));

Http请求 

  除了静态调用和动态调用,我们还可以发送HttpPost请求来调用WebService的方法。Soap请求就是HTTP POST的一个专用版本,遵循一种特殊的xml消息格式。使用HttpPost请求,对返回结果我们可以手动解析。下面的实现其实和调用WebAPI是完全一样的。

     /// <summary>
/// 请求信息帮助
/// </summary>
public partial class HttpHelper
{
private static HttpHelper m_Helper;
/// <summary>
/// 单例
/// </summary>
public static HttpHelper Helper
{
get { return m_Helper ?? (m_Helper = new HttpHelper()); }
} /// <summary>
/// 获取请求的数据
/// </summary>
/// <param name="strUrl">请求地址</param>
/// <param name="requestMode">请求方式</param>
/// <param name="parameters">参数</param>
/// <param name="requestCoding">请求编码</param>
/// <param name="responseCoding">响应编码</param>
/// <param name="timeout">请求超时时间(毫秒)</param>
/// <returns>返回:请求成功响应信息,失败返回null</returns>
public string GetResponseString(string strUrl, ERequestMode requestMode, Dictionary<string, string> parameters, Encoding requestCoding, Encoding responseCoding, int timeout = )
{
string url = VerifyUrl(strUrl);
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(url)); HttpWebResponse webResponse = null;
switch (requestMode)
{
case ERequestMode.Get:
webResponse = GetRequest(webRequest, timeout);
break;
case ERequestMode.Post:
webResponse = PostRequest(webRequest, parameters, timeout, requestCoding);
break;
} if (webResponse != null && webResponse.StatusCode == HttpStatusCode.OK)
{
using (Stream newStream = webResponse.GetResponseStream())
{
if (newStream != null)
using (StreamReader reader = new StreamReader(newStream, responseCoding))
{
string result = reader.ReadToEnd();
return result;
}
}
}
return null;
} /// <summary>
/// get 请求指定地址返回响应数据
/// </summary>
/// <param name="webRequest">请求</param>
/// <param name="timeout">请求超时时间(毫秒)</param>
/// <returns>返回:响应信息</returns>
private HttpWebResponse GetRequest(HttpWebRequest webRequest, int timeout)
{
try
{
webRequest.Accept = "text/html, application/xhtml+xml, application/json, text/javascript, */*; q=0.01";
webRequest.Headers.Add("Accept-Language", "zh-cn,en-US,en;q=0.5");
webRequest.Headers.Add("Cache-Control", "no-cache");
webRequest.UserAgent = "DefaultUserAgent";
webRequest.Timeout = timeout;
webRequest.Method = "GET"; // 接收返回信息
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
return webResponse;
}
catch (Exception ex)
{
return null;
}
} /// <summary>
/// post 请求指定地址返回响应数据
/// </summary>
/// <param name="webRequest">请求</param>
/// <param name="parameters">传入参数</param>
/// <param name="timeout">请求超时时间(毫秒)</param>
/// <param name="requestCoding">请求编码</param>
/// <returns>返回:响应信息</returns>
private HttpWebResponse PostRequest(HttpWebRequest webRequest, Dictionary<string, string> parameters, int timeout, Encoding requestCoding)
{
try
{
// 拼接参数
string postStr = string.Empty;
if (parameters != null)
{
parameters.All(o =>
{
if (string.IsNullOrEmpty(postStr))
postStr = string.Format("{0}={1}", o.Key, o.Value);
else
postStr += string.Format("&{0}={1}", o.Key, o.Value); return true;
});
} byte[] byteArray = requestCoding.GetBytes(postStr);
webRequest.Accept = "text/html, application/xhtml+xml, application/json, text/javascript, */*; q=0.01";
webRequest.Headers.Add("Accept-Language", "zh-cn,en-US,en;q=0.5");
webRequest.Headers.Add("Cache-Control", "no-cache");
webRequest.UserAgent = "DefaultUserAgent";
webRequest.Timeout = timeout;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = byteArray.Length;
webRequest.Method = "POST"; // 将参数写入流
using (Stream newStream = webRequest.GetRequestStream())
{
newStream.Write(byteArray, , byteArray.Length);
newStream.Close();
} // 接收返回信息
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
return webResponse;
}
catch (Exception ex)
{
return null;
}
} /// <summary>
/// 验证URL
/// </summary>
/// <param name="url">待验证 URL</param>
/// <returns></returns>
private string VerifyUrl(string url)
{
if (string.IsNullOrEmpty(url))
throw new Exception("URL 地址不可以为空!"); if (url.StartsWith("http://", StringComparison.CurrentCultureIgnoreCase))
return url; return string.Format("http://{0}", url);
}
}

  HttpPost 请求响应方式调用接口。

             // Http Post 请求响应方式
string url = m_WebServiceUrl + EMethod.Add.ToString(); //@"http://localhost:25060/testService.asmx/Add";
Dictionary<string, string> parameters = new Dictionary<string, string> { { "parameter1", TextBox1.Text }, { "parameter2", TextBox2.Text } };
string result = HttpHelper.Helper.GetResponseString(url, ERequestMode.Post, parameters, Encoding.Default, Encoding.UTF8);
XElement root = XElement.Parse(result);
TextBox3.Text = root.Value;

关于SOAP和REST

  我们都知道REST相比SOAP建议的标准更轻量级,甚到用Javascript都可以调用,使用方更方便、高效、简单。但并不是说REST就是SOAP的替代者。他们都只是实现Web Service(web服务)的两种不同的架构风格。就安全性等方面来说,SOAP还是更好的。

源码下载

扩展阅读

http://www.ruanyifeng.com/blog/2011/09/restful

http://www.cnblogs.com/lema/archive/2012/02/23/2364365.html

http://stevenjohn.iteye.com/blog/1442776

http://blog.linuxphp.org/archives/1505/

http://blog.csdn.net/superjj01/article/details/5270227

http://www.csdn.net/article/2013-06-13/2815744-RESTful-API

http://www.haodaima.net/art/2003909

http://my.oschina.net/lilw/blog/170518

C# WebService动态调用的更多相关文章

  1. SOA 下实现分布式 调用 cxf+ webService +动态调用

    近期项目间隙 自学了  webservice   一下 是我写的  一个demo 首先我们在web.xml 里配置如下 <servlet> <servlet-name>CXFS ...

  2. webService动态调用及返回至处理

    http://www.cnblogs.com/xffy1028/archive/2012/05/07/2487595.html using System; using System.Collectio ...

  3. WEBService动态调用代码

    BasicHttpBinding bind = new BasicHttpBinding(); bind.MaxReceivedMessageSize = int.MaxValue; Endpoint ...

  4. .Net webservice动态调用

    直接贴代码吧 public class PmsService { /// <summary> /// pms接口 /// </summary> /// <param na ...

  5. WebService – 2.动态调用WebService

    在本节课程中,将演示如何通过程序动态添加.调用.编译.执行WebService并返回结果. WebService动态调用示意图 WebService相关知识 代码文档对象模型CodeDom的使用 编程 ...

  6. 调用webservice服务(通过反射的方式动态调用)

    调用 ";//系统类别 var jkxlh = "";//接口序列号 var jkid = "68W05";//接口id string WriteXm ...

  7. c# 动态调用.net编写的webservices接口

    创建类WebServiceHelper: public class WebServiceHelper { #region 动态调用WebService动态调用地址 /// < summary&g ...

  8. .net WebServer示例及调用(接口WSDL动态调用 JAVA)

    新建.asmx页面 using System; using System.Collections.Generic; using System.Linq; using System.Web; using ...

  9. 动态调用WebService

    WebService内容 using Microsoft.CSharp;using System;using System.CodeDom;using System.CodeDom.Compiler; ...

随机推荐

  1. bootbox.js

    bootbox:一个弹出框插件,官网看一下例子就好了:http://bootboxjs.com/examples.html 目前来说应该只要调用bootbox.js就可以了,没有css的问题 1.有最 ...

  2. Intercooler.js – 让 AJAX 像锚标签一样简单

    使用 Intercooler,你可以添加 Ajax 到你的应用程序,而无需使用客户端模式的路由,认证,渲染,工厂或依赖注入.事实上,你不需要写任何的 JavaScript 代码.Intercooler ...

  3. Titanium中调用ios组件时语言不是本地化的解决方法

    用Titanium开发的ios应用中,当调用系统组件时,尽管手机已经设置了系统语言为中文,但那些组件的界面却仍为英文.比如调用iphone中的相册组件,其界面为: 那么怎么让它跟系统语言保持一致呢? ...

  4. asp.net正则模板引擎代码

    我们申明一个数组 ]; 接下来关键的正则表达式: RegexOptions options = RegexOptions.None; //嵌套模板标签(兼容) r[] = new Regex(@&qu ...

  5. 教你写能被舒服舒服又舒服地调用的iOS库

    目录 前言 脑洞开一开 分析 整容 结语 前言 2014年过的那么快,过年又那么块,2015年又是飞快地节奏,真尼玛感觉上帝是不是无聊使用了变速外挂开启了加速模式~到现在博主都无法接受已经上班的事实… ...

  6. openlayers方法总结

    openlayers中的一些方法:OpenLayers.Layer::initialize:创建层Div,注册事件:destroy:注销:clone:克隆当前层:setName:设置层name:add ...

  7. [SharePoint] SharePoint 错误集 1

    1. Delete a site collection · Run command : Remove-SPSite –Identity http://ent132.sharepoint.hp.com/ ...

  8. Oracle function real_st_astext,解决ArcSDE中st_astext函数返回字符串结构异常问题

    项目过程中发现在Oracle中调用ArcSDE的st_astext函数返回ST_Geometry类型字段的WKT文本有时空间类型前缀没有返回,例如一个点的经度为113.4,纬度为30.6,调用st_a ...

  9. iOS 获取ssid

    - (NSString *)fetchSSIDInfo { NSString *ssid = nil; NSArray *ifs = (__bridge_transfer id)CNCopySuppo ...

  10. CAGradientLayer的一些属性解析

    CAGradientLayer的一些属性解析 iOS中Layer的坐标系统: 效果: - (void)viewDidLoad { [super viewDidLoad]; CAGradientLaye ...