C# WebService动态调用
前言
站在开发者的角度,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动态调用的更多相关文章
- SOA 下实现分布式 调用 cxf+ webService +动态调用
近期项目间隙 自学了 webservice 一下 是我写的 一个demo 首先我们在web.xml 里配置如下 <servlet> <servlet-name>CXFS ...
- webService动态调用及返回至处理
http://www.cnblogs.com/xffy1028/archive/2012/05/07/2487595.html using System; using System.Collectio ...
- WEBService动态调用代码
BasicHttpBinding bind = new BasicHttpBinding(); bind.MaxReceivedMessageSize = int.MaxValue; Endpoint ...
- .Net webservice动态调用
直接贴代码吧 public class PmsService { /// <summary> /// pms接口 /// </summary> /// <param na ...
- WebService – 2.动态调用WebService
在本节课程中,将演示如何通过程序动态添加.调用.编译.执行WebService并返回结果. WebService动态调用示意图 WebService相关知识 代码文档对象模型CodeDom的使用 编程 ...
- 调用webservice服务(通过反射的方式动态调用)
调用 ";//系统类别 var jkxlh = "";//接口序列号 var jkid = "68W05";//接口id string WriteXm ...
- c# 动态调用.net编写的webservices接口
创建类WebServiceHelper: public class WebServiceHelper { #region 动态调用WebService动态调用地址 /// < summary&g ...
- .net WebServer示例及调用(接口WSDL动态调用 JAVA)
新建.asmx页面 using System; using System.Collections.Generic; using System.Linq; using System.Web; using ...
- 动态调用WebService
WebService内容 using Microsoft.CSharp;using System;using System.CodeDom;using System.CodeDom.Compiler; ...
随机推荐
- 决战大数据之二:CentOS 7 最新JDK 8安装
决战大数据之二:CentOS 7 最新JDK 8安装 [TOC] 修改hostname # hostnamectl set-hostname node1 --static # reboot now 重 ...
- angular学习的一些小笔记(中)之ng-init
ng-init是给angular执行给定的表达式,初始化变量的值 <!DOCTYPE html> <html> <head> <meta charset='U ...
- jQuery Transit 过渡效果
jQuery Transit 使用 CSS3 的新特性来实现过渡效果,比默认的.animate方法要顺畅得多. 因为使用 CSS3 进行过渡效果,所以对不支持 CSS3 的浏览器效果有所下降. 语法和 ...
- [deviceone开发]-天气demo
一.简介 该demo主要实现定位功能,读取天气信息,语音播报功能.其中定位需要配置key,调试二维码请到论坛中下载! 二.效果图 三.相关讨论 http://bbs.deviceone.net/for ...
- sharepoint2010问卷调查(3)-实现问卷的开始和结束时间(采用自定义字段类型)
接着上面的图片调查,sharepoint自带的问卷调查是没有开始和结束时间的.这个在项目过程不太实用.问卷一般有开始和结束时间的.因此需要自己 动手开发一个自定义字段类型字段.如下图: 开发添加栏目会 ...
- DirectX标准规定 DirectX和OpenGL的不同
DirectX标准规定 DirectX使用左手坐标系. X轴正向指向右,Y轴正向指向上,Z轴正向垂直纸面向内. 编写Direct3D应用程序时,通常只使用4×4的矩阵和1×4的行向量,相乘时行向量在前 ...
- Failed to apply plugin [id 'com.android.application'] 和 Could not find com.android.tools.build:gradle:2.XX的最正确的解决方法
发现android studio是真的可爱啊,上一秒还没问题可以build运行,下一秒就出错...好,你任性,你牛逼.. 说下今天又遇到的两个问题:Failed to apply plugin [id ...
- NSURLSession从网络上下载资源,此程序下载的是视频
#import "ViewController.h" @interface ViewController ()<NSURLSessionDelegate, NSURLSess ...
- html 常用的标签
1.html 的基本格式 <html> <head> <meta charset="UTF-8"> <title>HTML5的标题& ...
- 安装pods 遇到的坑
1. ERROR: While executing gem ... (Errno::EPERM) Operation not permitted - /usr/bin/pod 苹果系统升级 OS ...