WebService原理

WebService原理

WebService是一种跨编程语言和跨操作系统平台的远程调用技术

Web Services 是通过一系列标准和协议来保证程序之间的动态连接。其中最基本的协议包括:SOAP, WSDL, UDDI

所谓远程调用,就是一台计算机a上的一个程序可以调用到另外一台计算机b上的一个对象的方法,譬如,银联提供给商场的pos刷卡系统(采用交互提问的方式来加深大家对此技术的理解)。

远程调用技术有什么用呢?商场的POS机转账调用的转账方法的代码是在银行服务器上,还是在商场的pos机上呢?

什么情况下可能用到远程调用技术呢?例如,amazon,天气预报系统,淘宝网,校内网,百度等把自己的系统服务以webservice服务的形式暴露出来,让第三方网站和程序可以调用这些服务功能,这样扩展了自己系统的市场占有率,往大的概念上吹,就是所谓的SOA应用。

所谓跨编程语言和跨操作平台,就是说服务端程序采用java编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上运行。

除了WebService外,常见的远程调用技术还有RMI(Remote method invoke)和CORBA,由于WebService的跨平台和跨编程语言特点,因此比其他两种技术应用更为广泛,但性能略低。

 

 

WebService使用SOAP协议实现跨编程语言和跨操作系统平台

WebService采用HTTP协议传输数据,采用XML格式封装数据(即XML中说明调用远程服务对象的哪个方法,传递的参数是什么,以及服务对象的返回结果是什么)。WebService通过HTTP协议发送请求和接收结果时,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议(simple object access protocol,简单对象访问协议) 。

SOAP协议= HTTP协议+ XML数据格式

SOAP协议是基于HTTP协议的,两者的关系就好比高速公路是基于普通公路改造的,在一条公路上加上隔离栏后就成了高速公路。

商店的服务员只要收到了钱就给客户提供货物,商店服务员不用关心客户是什么性质的人,客户也不用关心商店服务员是什么性质的人。同样,WebService客户端只要能使用HTTP协议把遵循某种格式的XML请求数据发送给WebService服务器,WebService服务器再通过HTTP协议返回遵循某种格式的XML结果数据就可以了,WebService客户端与服务器端不用关心对方使用的是什么编程语言。

HTTP协议和XML是被广泛使用的通用技术,各种编程语言对HTTP协议和XML这两种技术都提供了很好的支持,WebService客户端与服务器端使用什么编程语言都可以完成SOAP的功能,所以,WebService很容易实现跨编程语言,跨编程语言自然也就跨了操作系统平台

 

 

WSDL文件

 好比我们去商店买东西,首先要知道商店里有什么东西可买,然后再来购买,商家的做法就是张贴广告海报。WebService客户端要调用一个WebService服务,首先要有知道这个服务的地址在哪,以及这个服务里有什么方法可以调用,所以,WebService务器端首先要通过一个WSDL文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受的参数是什么,返回值是什么),服务的网络地址用哪个url地址表示,服务通过什么方式来调用。

 WSDL(webservicedescription language)是基于XML格式的,它是WebService客户端和服务器端都能理解的标准格式,其中描述的信息可以分为what,where,how等部分!

 WSDL文件保存在Web服务器上,通过一个url地址就可以访问到它。客户端要调用一个WebService服务之前,要知道该服务的WSDL文件的地址。WebService服务提供商可以通过两种方式来暴露它的WSDL文件地址:

1.注册到UDDI服务器,以便被人查找

2.直接告诉给客户端调用者,例如,在自己网站给出信息或邮件告诉。

 

说明:

 我们平常写的程序都是在同一个程序中调用其他对象的方法,而webservice是怎样实现调用其它计算机中的对象的方法的呢?

其基本原理是在客户端程序的本地有一个代理对象,这个代理对象和远程对象具有相同的方法声明,这个代理程序不用我们自己编写,工具可以帮我们生成。客户端程序调用本地代理对象的方法时,代理对象会通过某种网络传送技术去联系远程的程序,远程程序再调用代理对象请求的相应本地对象的方法,然后将方法执行的结果返回给客户端代理程序。远程调用技术就是要解决客户端如何将请求信息传送给远程的程序,远程的程序如何将结果回应给客户端,并且其实现技术对开发人员来说是透明的,即不需要开发人员自己的考虑和实现这些细节。

1.WSDL(网络服务描述语言,WebServices Description Language)是一门基于 XML 的语言,用于描述 Web Services 以及如何对它们进行访问。

2.UDDI 是一种目录服务,企业可以使用它对 Web services 进行注册和搜索。

UDDI,英文为"Universal Description, Discovery and Integration",可译为“通用描述、发现与集成服务”。




<!--
	Title:WebService 超简单入门教程
	Author:Lovingshu
	Date:2012/09/21
	Description:Teach you how to use WebService with java easily!
-->
写在前面的话:
	当两个人碰面后,产生了好感,如果需要得到双方的信息,那么双方的交流是必不可少的!应用程序也如此,
	各个应用程序之间的交流就需要WebService来作为相互交流的桥梁!

项目目的:
	程序A调用程序B中的方法C...


首先申明:本次需要采用的JDK版本为jdk1.6+,也就是要java6+才能看到效果,java5没试过...我认为,升级一下JDK版本比起安装什么
Axis以及导入一堆jar包和配置N多配置文件来说,java6的webservice无疑是简单的不能再简单!

废话少说少整点,开整:
1,建立一个项目名为Trans,web项目,普通java项目都可以!这里我们就以简单的java应用程序来作为示范吧!
	1.1在建立一个方法属于com.shu.function.Function类:

//别忘了引入webservice
@WebService
public class Function{

	//该方法就是要暴露给其他应用程序调用的方法
	public String transWords(String words){
		String res="";
		for(char ch : words.toCharArray()){
			res+="\t"+ch+"\t";
		}
		return res;
	}

	//这里我们使用main方法来发布我们的service
	public void static main(String[] args){
		Endpoint.publish("http://localhost:9001/Service/Function",new Function());
		System.out.println("Publish Success~");
	}
}

2.在打出"Publish Success~"后,说明我们消息发布成功,不成的话,注意:1.需要JDK1.6+,2.确定服务器端口没有被占用;成功后,
在浏览器地址栏输入:http://localhost:9001/Service/Function?wsdl就可以看到该service的详细信息!

3.建立另外一个项目:GiveMeWords

关键的来了,打开cmd输入以下命令:wsimport -s src的路径 -p 完整包名 -keep webservice的发布地址 然后回车就OK了,
比如说: wsimport -s F:\\WorkBench\\Eclipse\\GiveMeWords\\src  -p com.shu.service -keep http://localhost:9001/Service/Function
(如果wsimport命令失效,请检查是否把jdk配入环境变量!JAVA_HOME:JDK所在位置,CLASSPATH:%JAVA_HOME%\lib\tools.jar)
回车后,会出现:
	parsing WSDL...
	generating code...
	compiling code...
恭喜你,成功了,你可以刷新一下你的GiveMeWords项目后会在com.shu.service包下会有对应的WebService生成的文件!
然后调用如下:
public void static main(String[] args){
	Function fu =new FunctionService().getFunctionPort();
	String str=fu.transWords("Let's Get Heck Out Of Here!");
	//最后str就是在Trans项目下的transWords方法处理后的字符串咯!
}

最后...怎么样,webservice不难吧,全靠jdk的升级呀!


动态调用 WebService
具体步骤:
1. 从目标 URL 下载 WSDL 数据。
2. 使用 ServiceDescription 创建和格式化 WSDL 文档文件。
3. 使用 ServiceDescriptionImporter 创建客户端代理类。
4. 使用 CodeDom 动态创建客户端代理类程序集。
5. 利用反射调用相关 WebService 方法。

OK,看看具体的例子。

我们要调用的目标 WebService,其 URL 是  http://localhost:60436/Learn.WEB/WebService.asmx

HelloWorld.asmx
[WebService(Namespace = "http://www.rainsts.net/", Description="我的Web服务")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebService : System.Web.Services.WebService {

 public WebService () {
 }

  [WebMethod]
  public string HelloWorld()
  {
    return "Hello Wolrd!";
  }
}

1. 动态调用 WebService

客户端动态调用代码
using System.IO;
using System.Net;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using System.Xml.Serialization;

// 1. 使用 WebClient 下载 WSDL 信息。
WebClient web = new WebClient();
Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL");

// 2. 创建和格式化 WSDL 文档。
ServiceDescription description = ServiceDescription.Read(stream);

// 3. 创建客户端代理代理类。
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();

importer.ProtocolName = "Soap"; // 指定访问协议。
importer.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。
importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync; 

importer.AddServiceDescription(description, null, null); // 添加 WSDL 文档。

// 4. 使用 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();
parameter.GenerateExecutable = false;
parameter.GenerateInMemory = true;
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);

// 5. 使用 Reflection 调用 WebService。
if (!result.Errors.HasErrors)
{
  Assembly asm = result.CompiledAssembly;
  Type t = asm.GetType("WebService"); // 如果在前面为代理类添加了命名空间,此处需要将命名空间添加到类型前面。

  object o = Activator.CreateInstance(t);
  MethodInfo method = t.GetMethod("HelloWorld");
  Console.WriteLine(method.Invoke(o, null));
}

2. 生成客户端代理程序集文件

上面的代码通过在内存中创建动态程序集的方式完成了动态调用过程。如果我们希望将客户端代理类生成程序集文件保存到硬盘,则可以进行如下修改。生成程序集文件后,我们可以通过 Assembly.LoadFrom() 载入并进行反射调用。对于需要多次调用的系统,要比每次生成动态程序集效率高出很多。
using System.IO;
using System.Net;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using System.Xml.Serialization;

// 1. 使用 WebClient 下载 WSDL 信息。
WebClient web = new WebClient();
Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL");

// 2. 创建和格式化 WSDL 文档。
ServiceDescription description = ServiceDescription.Read(stream);

// 3. 创建客户端代理代理类。
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();

importer.ProtocolName = "Soap"; // 指定访问协议。
importer.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。
importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync; 

importer.AddServiceDescription(description, null, null); // 添加 WSDL 文档。

// 4. 使用 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();
parameter.GenerateExecutable = false;
parameter.OutputAssembly = "test.dll"; // 可以指定你所需的任何文件名。
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);
if (result.Errors.HasErrors)
{
  // 显示编译错误信息
}

调用程序集文件演示
Assembly asm = Assembly.LoadFrom("test.dll");
Type t = asm.GetType("WebService"); 

object o = Activator.CreateInstance(t);
MethodInfo method = t.GetMethod("HelloWorld");
Console.WriteLine(method.Invoke(o, null));

3. 获取客户端代理类源代码

还有一种情形,就是我们需要获得客户端代理类的 C# 源代码。
using System.IO;
using System.Net;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using System.Xml.Serialization;

// 1. 使用 WebClient 下载 WSDL 信息。
WebClient web = new WebClient();
Stream stream = web.OpenRead("http://localhost:60436/Learn.WEB/WebService.asmx?WSDL");

// 2. 创建和格式化 WSDL 文档。
ServiceDescription description = ServiceDescription.Read(stream);

// 3. 创建客户端代理代理类。
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();

importer.ProtocolName = "Soap"; // 指定访问协议。
importer.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。
importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync; 

importer.AddServiceDescription(description, null, null); // 添加 WSDL 文档。

// 4. 使用 CodeDom 编译客户端代理类。
CodeNamespace nmspace = new CodeNamespace(); // 为代理类添加命名空间,缺省为全局空间。
CodeCompileUnit unit = new CodeCompileUnit();
unit.Namespaces.Add(nmspace);

ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit); 
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");

// 5. 保存源代码到文件。当然,你也可以直接保存到内存字符串中。
TextWriter writer = File.CreateText("test.cs"); // 指定你所需的源代码文件名。
provider.GenerateCodeFromCompileUnit(unit, writer, null);
writer.Flush();
writer.Close();

如果你调用时触发 "WebException: 请求因 HTTP 状态 415 失败: Unsupported Media Type。" 这样的异常,那么恭喜你和我一样郁闷 ,赶紧把服务器端的 WSE 关掉吧。在必须使用 WSE 的情况下,需要对客户端进行调整,至于代码需要你自己去写了。呵呵~~~~ 
另外一种方法是:

1. 使用 wsdl.exe 生成客户端代理类型源代码。
2. 使用 csc.exe 生成客户端代理程序集。
3. 使用反射调用。

 

你可能感兴趣的:(WebService原理)