前言

  站在开发者的角度,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. MVC初认

    初探MVC 何谓MVC 简单来说就是开发程序时所使用的一种“架构(框架)”.它就是开发的一种观念,或者说是存在已久的设计规则.我们知道在开发过程中总会伴随着需求.技术.客户.老板等因素的变化,给开发带 ...

  2. 记一次CSR上线及总结

    终于到上线的时候了,可以好好休息了.放松了,但在没有经过用户确认之前,一切皆有可能发生...... 经历: 项目终于完成,上线文档已准备就绪,等待上线时刻. 在上线之前,忘记了解目前环境的部署架构,注 ...

  3. jQuery幸运大转盘_jQuery+PHP抽奖程序的简单实现

    jQuery幸运大转盘_jQuery+PHP抽奖程序的简单实现 在线实例 查看演示 完整代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...

  4. jQuery BreakingNews 间歇滚动

    BreakingNews 是一款基于jQuery的间歇滚动插件.它可以设置标题.标题颜色.标题背景颜色.链接颜色.字体大小.边框.宽度.自动滚动.间歇时间等等,同时它还好提供两种过度方式--淡入淡出( ...

  5. C# 如何在Excel 动态生成PivotTable

    Excel 中的透视表对于数据分析来说,非常的方便,而且很多业务人员对于Excel的操作也是非常熟悉的,因此用Excel作为分析数据的界面,不失为一种很好的选择.那么如何用C#从数据库中抓取数据,并在 ...

  6. Font Combiner – 自定义网页字体和图标生成工具

    Font Combiner 是一个功能丰富的 Web 字体生成工具和字体改进工具,提供字距调整.构造子集.各种提示选项和自定义字体字形组合.您可以生成您自己的自定义字体的格式和文件大小. 另外还有成千 ...

  7. 【追寻javascript高手之路02】变量、作用域知多少?

    前言 本来想把这个与上篇博客写到一起的,但是考虑到是两个知识点还是分开算了,于是我们继续今天的学习吧. 基本类型与引用类型 ECMAScript的的变量有两种类型: 基本类型(值类型):简单数据段 引 ...

  8. 当jquery ajax遇上401请求

    jquery ajax是个很常用接口,而在请求时候,可能存在响应401的情况(身份认证过期或未登录),比较容易出现在混合应用上,如何进行身份认证,重发失败请求,还是值得注意的. ajax请求有两种方式 ...

  9. CSS属性之float学习心得

    全文参考:http://www.linzenews.com/program/net/2331.html 我们来看看CSS重要属性--float. 以下内容分为如下小节: 1:float属性 2:flo ...

  10. Android九宫格界面实现点击每个格点击跳转界面

    刚开始有个任务就是做一个九宫格界面,后来有个任务就是实现点击每个格并跳转界面实现每个格的功能.下面我就介绍一下我是如何实现该功能的 首先写一下我的想法是: 登录成功后显示一个九宫格界面,每个九宫格的每 ...