WebService知识记要

来源:互联网 发布:人工蜂群算法的流程图 编辑:程序博客网 时间:2024/05/22 09:44

WebService各大编程语言都有成熟的模块支持,在平时开发时使用默认的向导就可以使用WebService功能,但有些设置细节或性能优化的知识点还是需要一些操作技巧和概念深入理解,谨在此记录自己在网上收集的知识点,需要的时候可以拾来一用。

Web Service基本知识

一、Web Service基本概念

Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术。是:通过SOAP在Web上提供的软件服务,使用WSDL文件进行说明,并通过UDDI进行注册。

XML:(Extensible Markup Language)扩展型可标记语言。

面向短期的临时数据处理、面向万维网络,是Soap的基础。

Soap:(Simple Object Access Protocol)简单对象存取协议。

是XML Web Service 的通信协议。当用户通过UDDI找到你的WSDL描述文档后,他可以通过SOAP调用你建立的Web服务中的一个或多个操作。SOAP是XML文档形式的调用方法的规范,它可以支持不同的底层接口,像HTTP(S)或者SMTP。

SOAP的框架非常像HTTP协议,都包含的消息的Header和消息的Body,只不过SOAP是Web Service数据交换的专用协议。SOAP是HTTP的上层协议,最终还是通过HTTP来传输数据。 SOAP消息的根元素为<Envelope>,除了组织结构,其他非常类似于HTTP的Request和Response。

SOAP Reqeust Example

<?xml version='1.0' ?>  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">       <soap:Header>            ...       </soap:Header>       <soap:Body>            <getCustomerAddress xmlns="http://soa-in-practice.com/xsd">                 <customerID>12345678</customerID>            </getCustomerAddress >       </soap:Body>  </soap:Envelope>  <?xml version='1.0' ?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">     <soap:Header>          ...     </soap:Header>     <soap:Body>          <getCustomerAddress xmlns="http://soa-in-practice.com/xsd">               <customerID>12345678</customerID>          </getCustomerAddress >     </soap:Body></soap:Envelope>

SOAP Response Example

<?xml version='1.0' ?>  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">       <soap:Header>            ...       </soap:Header>       <soap:Body>            <getCustomerAddressResponse xmlns="http://soa-in-practice.com/xsd">                 <address>                      <street>Gaussstr. 29</street>                      <city>Braunschweig</city>                      <zipCode>D-38106</zipCode>                 </address>            </getCustomerAddressResponse>       </soap:Body>  </soap:Envelope>  

 

WSDL:(Web Services Description Language) WSDL 文件是一个 XML 文档,用于说明一组 SOAP 消息以及如何交换这些消息。

大多数情况下由软件自动生成和使用。WSDL通常是框架来生成的,并不是手工写的,比如Java可以使用wsgen 生成webservice,.Net框架也有自己方法,都可以通过自身的框架把接口发布称WSDL文件。
一个WSDL的简单示例。这个WSDL文件定义了一个被称为CustomerService的服务,该服务提供了一个被称为getCustomerAdress()的操作。这个操作的输入参数为一个类型为long的客户ID,输出为一个包含3个string属性-街道、城市和邮编的结构。(示例来自于《SOA实践指南》)

<?xml version="1.0" encoding="utf-8" ?>  <definitions name="CustomerService"       targetNamespace="http://soa-in-practice.com/wsdl"       xmlns:tns="http://soa-in-practice.com/wsdl"       xmlns:xsd1="http://soa-in-practice.com/xsd"       xmlns:xsd="http://www.w3.org/2001/XMLSchema"       xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"       xmlns="http://schemas.xmlsoap.org/wsdl/">         <types>            <xsd:schema                 targetNamespace="http://soa-in-practice.com/xsd"                 xmlns="http://soa-in-practice.com/xsd">                   <xsd:element name="getCustomerAddress">                      <xsd:complexType>                           <xsd:sequence>                                <xsd:element name="customerID" type="xsd:long"/>                           </xsd:sequence>                      </xsd:complexType>                 </xsd:element>                   <xsd:element name="getCustomerAddressResponse" type="Address"/>                 <xsd:complexType name="Address">                      <xsd:sequence>                           <xsd:element name="street" type="xsd:string"/>                           <xsd:element name="city" type="xsd:string"/>                           <xsd:element name="zipCode" type="xsd:string"/>                      </xsd:sequence>                 </xsd:complexType>              </xsd:schema>       </types>         <message name="getCustomerAddressInput">            <part name="params" element="xsd1:getCustomerAddress"/>       </message>       <message name="getCustomerAddressOutput">            <part name="params" element="xsd1:getCustomerAddressResponse"/>       </message>         <portType name="CustomerInterface" >            <operation name="getCustomerAddress">                 <input message="tns:getCustomerAddressInput" />                 <output message="tns:getCustomerAddressOutput" />            </operation>       </portType>         <binding name="CustomerSOAPBinding"            type="tns:CustomerInterface" >            <soap:binding style="document"            transport="http://schemas.xmlsoap.org/soap/http" />            <operation name="getCustomerAddress">                 <soap:operation                 soapAction="http://soa-in-practice.com/getCustomerAddress" />                 <input>                      <soap:body use="literal" />                 </input>                 <output>                      <soap:body use="literal" />                 </output>            </operation>       </binding>         <service name="CustomerService" >            <port name="CustomerPort"                 binding="tns:CustomerSOAPBinding">                 <soap:address                 location="http://soa-in-practice.com/customer11"/>            </port>       </service>    </definitions>  

WSDL文件的解读
阅读一个WSDL,需要从下往上看:
最后的<service>节点定义了这个服务的名称为CustomerService,并且该服务可以在http://soa-in-practice.com/customer11找到。
     <service name="CustomerService" >
          <port name="CustomerPort"
               binding="tns:CustomerSOAPBinding">
               <soap:address
               location="http://soa-in-practice.com/customer11"/>
          </port>
     </service>


<binding>节点定义了用来提供Webservice的协议和格式。CustomerSOABiding是Binding的名称,并指出Binding要从哪个接口开始(这里是从CustomerInterface这个接口开始)
     <binding name="CustomerSOAPBinding"
          type="tns:CustomerInterface" >
          <soap:binding style="document"
          transport="http://schemas.xmlsoap.org/soap/http" />
          <operation name="getCustomerAddress">
               <soap:operation
               soapAction="http://soa-in-practice.com/getCustomerAddress" />
               <input>
                    <soap:body use="literal" />
               </input>
               <output>
                    <soap:body use="literal" />
               </output>
          </operation>
     </binding>


<portType>描述了CustomerInterface这个接口,其中接口包含一个叫getCustomerAddress的Operation。在Operation下边,getCustomerAddressInput和getCustomerAddressOutput是这个Operation的输入消息和输出消息。
     <portType name="CustomerInterface" >
          <operation name="getCustomerAddress">
               <input message="tns:getCustomerAddressInput" />
               <output message="tns:getCustomerAddressOutput" />
          </operation>
     </portType>


<message>节点定义了各个消息,使用的是<portType>节点引用的标识符。
     <message name="getCustomerAddressInput">
          <part name="params" element="xsd1:getCustomerAddress"/>
     </message>
     <message name="getCustomerAddressOutput">
          <part name="params" element="xsd1:getCustomerAddressResponse"/>
     </message>


<type>节点定义了将会使用到的数据类型:输入参数customerID的类型为long,输出参数address的类型是有3个字符串属性的结构/记录。所有类型在自己的命名空间xsd1中。
     <types>
          <xsd:schema
               targetNamespace="http://soa-in-practice.com/xsd"
               xmlns="http://soa-in-practice.com/xsd">

               <xsd:element name="getCustomerAddress">
                    <xsd:complexType>
                         <xsd:sequence>
                              <xsd:element name="customerID" type="xsd:long"/>
                         </xsd:sequence>
                    </xsd:complexType>
               </xsd:element>

               <xsd:element name="getCustomerAddressResponse" type="Address"/>
               <xsd:complexType name="Address">
                    <xsd:sequence>
                         <xsd:element name="street" type="xsd:string"/>
                         <xsd:element name="city" type="xsd:string"/>
                         <xsd:element name="zipCode" type="xsd:string"/>
                    </xsd:sequence>
               </xsd:complexType>

          </xsd:schema>
     </types>

UDDI (Universal Description, Discovery, and Integration) 是一个主要针对Web服务供应商和使用者的新项目。

在用户能够调用Web服务之前,必须确定这个服务内包含哪些商务方法,找到被调用的接口定义,还要在服务端来编制软件,UDDI是一种根据描述文档来引导系统查找相应服务的机制。UDDI利用SOAP消息机制(标准的XML/HTTP)来发布,编辑,浏览以及查找注册信息。它采用XML格式来封装各种不同类型的数据,并且发送到注册中心或者由注册中心来返回需要的数据。

二、XML Web Service的特点

Web Service的主要目标是跨平台的可互操作性。为了实现这一目标,Web Service 完全基于XML(可扩展标记语言)、XSD(XML Schema)等独立于平台、独立于软件供应商的标准,是创建可互操作的、分布式应用程序的新平台。因此使用Web Service有许多优点:

1、跨防火墙的通信

如果应用程序有成千上万的用户,而且分布在世界各地,那么客户端和服务器之间的通信将是一个棘手的问题。因为客户端和服务器之间通常会有防火墙或者代理服务器。传统的做法是,选择用浏览器作为客户端,写下一大堆ASP页面,把应用程序的中间层暴露给最终用户。这样做的结果是开发难度大,程序很难维护。 要是客户端代码不再如此依赖于HTML表单,客户端的编程就简单多了。如果中间层组件换成Web Service的话,就可以从用户界面直接调用中间层组件,从而省掉建立ASP页面的那一步。要调用Web Service,可以直接使用Microsoft SOAP Toolkit或.net这样的SOAP客户端,也可以使用自己开发的SOAP客户端,然后把它和应用程序连接起来。不仅缩短了开发周期,还减少了代码复杂度,并能够增强应用程序的可维护性。同时,应用程序也不再需要在每次调用中间层组件时,都跳转到相应的"结果页"。

2、应用程序集成

企业级的应用程序开发者都知道,企业里经常都要把用不同语言写成的、在不同平台上运行的各种程序集成起来,而这种集成将花费很大的开发力量。应用程序经常需要从运行的一台主机上的程序中获取数据;或者把数据发送到主机或其它平台应用程序中去。即使在同一个平台上,不同软件厂商生产的各种软件也常常需要集成起来。通过Web Service,应用程序可以用标准的方法把功能和数据"暴露"出来,供其它应用程序使用。

XML Web services 提供了在松耦合环境中使用标准协议(HTTP、XML、SOAP 和 WSDL)交换消息的能力。消息可以是结构化的、带类型的,也可以是松散定义的。

3、B2B的集成

B2B 指的是Business to Business,as in businesses doing business with other businesses,商家(泛指企业)对商家的电子商务,即企业与企业之间通过互联网进行产品、服务及信息的交换。通俗的说法是指进行电子商务交易的供需双方都是商家(或企业、公司),她们使用了Internet的技术或各种商务网络平台,完成商务交易的过程。

Web Service是B2B集成成功的关键。通过Web Service,公司可以只需把关键的商务应用"暴露"给指定的供应商和客户,就可以了,Web Service运行在Internet上,在世界任何地方都可轻易实现,其运行成本就相对较低。Web Service只是B2B集成的一个关键部分,还需要许多其它的部分才能实现集成。 用Web Service来实现B2B集成的最大好处在于可以轻易实现互操作性。只要把商务逻辑"暴露"出来,成为Web Service,就可以让任何指定的合作伙伴调用这些商务逻辑,而不管他们的系统在什么平台上运行,使用什么开发语言。这样就大大减少了花在B2B集成上的时间和成本。

4、软件和数据重用

Web Service在允许重用代码的同时,可以重用代码背后的数据。使用Web Service,再也不必像以前那样,要先从第三方购买、安装软件组件,再从应用程序中调用这些组件;只需要直接调用远端的Web Service就可以了。另一种软件重用的情况是,把好几个应用程序的功能集成起来,通过Web Service "暴露"出来,就可以非常容易地把所有这些功能都集成到你的门户站点中,为用户提供一个统一的、友好的界面。 可以在应用程序中使用第三方的Web Service 提供的功能,也可以把自己的应用程序功能通过Web Service 提供给别人。两种情况下,都可以重用代码和代码背后的数据。

从以上论述可以看出,Web Service 在通过Web进行互操作或远程调用的时候是最有用的。不过,也有一些情况,Web Service根本不能带来任何好处,Web Service有以下缺点:

1、 单机应用程序

目前,企业和个人还使用着很多桌面应用程序。其中一些只需要与本机上的其它程序通信。在这种情况下,最好就不要用Web Service,只要用本地的API就可以了。COM非常适合于在这种情况下工作,因为它既小又快。运行在同一台服务器上的服务器软件也是这样。当然Web Service 也能用在这些场合,但那样不仅消耗太大,而且不会带来任何好处。

2、 局域网的一些应用程序

在许多应用中,所有的程序都是在Windows平台下使用COM,都运行在同一个局域网上。在这些程序里,使用DCOM会比SOAP/HTTP有效得多。与此相类似,如果一个.net程序要连接到局域网上的另一个.net程序,应该使用.net Remoting。其实在.net Remoting中,也可以指定使用SOAP/HTTP来进行Web Service 调用。不过最好还是直接通过TCP进行RPC调用,那样会有效得多。

三、XML Web Service的应用

1.最初的 XML Web Service 通常是可以方便地并入应用程序的信息来源,如股票价格、天气预报、体育成绩等等。

2.以 XML Web Service 方式提供现有应用程序,可以构建新的、更强大的应用程序,并利用 XML Web Service 作为构造块。

例如,用户可以开发一个采购应用程序,以自动获取来自不同供应商的价格信息,从而使用户可以选择供应商,提交订单,然后跟踪货物的运输,直至收到货物。而供应商的应用程序除了在Web上提供服务外,还可以使用XML Web Service检查客户的信用、收取货款,并与货运公司办理货运手续。

WebService开发

.net平台内建了对Web Service的支持,包括Web Service的构建和使用。与其它开发平台不同,使用.net平台,你不需要其他的工具或者SDK就可以完成Web Service的开发了。.net Framework本身就全面支持Web Service,包括服务器端的请求处理器和对客户端发送和接受SOAP消息的支持。

一、WebService中常用的属性(Attributes)

1. Web Service(Web服务)提供以下三个属性。

    Namespace:此属性的值包含 XML Web Service的默认命名空间。XML命名空间提供了一种在XML文档中创建名称的方法,该名称可由统一资源标识符(URI)标识。如果不指定命名空间,则使用默认命名空间http://tempuri.org/。
    Name:此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称。
    Description:此属性的值包含描述性消息,此消息将在XML Web Service的说明文件(例如服务说明和服务帮助页)生成后显示给XML Web Service的潜在用户。
   
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        //......
    }

2. WebMethod(Web服务方法)有以下4个常用属性。

     Description:是对Web Service方法的描述信息。就像Web Service方法的功能注释,可以让调用者看见的注释。
   
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod(Description = "显示所有人员的信息")]
        public InfoData[] GetInfos(out string emsg)
        {
            //...
        }
    }

 

   EnableSession:指示Web Service是否启动Session标志,主要通过Cookie完成,默认为false。
    默认情况下WebService的方法中不能使用Session,如果使用会产生异常。
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod(Description = "显示所有人员的信息")]
        public InfoData[] GetInfos(out string emsg)
        {
            Session["test"] = DateTime.Now.ToString();
            return new InfoDA().Select().ToArray();
        }
    }
    要在WebService中使用Session需要把上面的代码进行如下修改:
   
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod(Description = "显示所有人员的信息", EnableSession = true)]
        public InfoData[] GetInfos(out string emsg)
        {
            Session["test"] = DateTime.Now.ToString();
            return new InfoDA().Select().ToArray();
        }
    }

 

    TransactionOption:指示Web Service方法的事务支持。
    要在WebService中使用事务时,不需要编写很多的代码,只需要在WebMethod属性中加上启用事务的声明即可
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod(TransactionOption= TransactionOption.RequiresNew)]
        public void AddInfo(InfoData data)
        {
            new InfoDA().Insert(data);
            new InfoDA().Insert(data);
        }
    }
    在上面的代码中向数据库中插入了两次数据,在第二次插入数据时会产生主键重复的异常。如果查看数据库时我们会发现第一次Insert也没有插入成功。这就是因为我们在该方法上加入了事务的功能。

 

    CacheDuration:设置响应应在缓存中保留的秒数。这样Web Service就不需要重复执行多遍,可以提高访问效率,而CacheDuration就是指定缓存时间的属性。
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod( EnableSession=true, CacheDuration=10)]
        public string GetCNDateTime()
        {
            return DateTime.Now.ToString("yyyy年MM月dd日hh时mm分ss秒ms毫秒");
        }
    }
    上面的服务方法缓存了10秒钟。
    在我们运行该服务,手动调用方法的时候会发现好像缓存并没有起作用,每次调用的显示的时间总会变化。其实该缓存的效果在直接运行的时候并不会起作用,当我们编写客户端代码调用该服务的时候会发现缓存的确是起作用了。
    客户端代码如下:
    public static void Main(string[] args)
    {
        Services.MyServiceSoapClient client = new Client.Services.MyServiceSoapClient();
        string str = client.GetCNDateTime();
        Console.WriteLine(str);
    }
    在运行该客户端代码的时候,在10秒钟之内的两次运行显示的时间是不变的。
 

二、WebService的调用。

我们可以使用ASP.NET Web程序、WinForm程序、控制台程序和javascript来调用WebService。
ASP.NET、WinForm、控制台调用WebService方法基本一样也很简单,主要是添加Web引用,调用代理类来实现的,这里我们不再举例。
下面我们主要看一下如何使用JavaScript来调用WebService

WebService代码如下:
[WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
    [WebMethod]
    public string GetCNDateTime()
    {
        return DateTime.Now.ToString("yyyy年MM月dd日hh时mm分ss秒ms毫秒");
    }
}

客户端JS调用代码如下:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <div id="dd"></div>
    </div>
    </form>
</body>
</html>
<script language="javascript">
    function showTime() {
        //下面我们使用XMLDOM对象对WebService访问,因为WebService是以XML方式传输数据的,所以我们可以使用XMLDOM对象来解析WebService返回的数据
        var obj = new ActiveXObject("Microsoft.XMLDOM");
        //设定读取方式是同步方式
        obj.async = false;
        //指定XMLDOM对象要加载的WebService方法返回的XML数据
        //如果使用开发服务器的运行WebService的话,那需要选运行起这个WebService。
        obj.load("http://localhost/TestWS/Service.asmx/GetCNDateTime");
        //取得返回的XML数据中的文本
        var res = obj.documentElement.text;
        dd.innerHTML = res;
    }
    window.setInterval(showTime, 1000);
</script>

这样运行并不成功

这是因为WebService并不接收JavaScript提交的方式。要完善这个问题我们需要在WebService的web.config配置文件中加入下面的配置
<system.web>
        <webServices>
            <protocols>
                <add name="HttpPost"/>
                <add name="HttpGet"/>
            </protocols>
        </webServices>
        .........
</system.web>


三、实现异步调用Web Service

普通调用Web Service的方法,在Web Service执行期间客户端会一直等待服务执行完毕才能响应。从而造成客户端UI线程的阻塞假死现象。这时候,异步调用就显得很有用,它可以让客户端在调用Web Service时,不至于阻塞客户端的UI线程导致假死,还可以在调用Web Service的同时做些其他的处理。
第一种方法,是通过利用Backgroundworker对象实现。
BackgroundWorker 类允许你在单独的专用线程上运行操作。耗时的操作(如下载和数据库事务)在长时间运行时可能会导致用户界面(UI)似乎处于停止响应状态。如果你需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用 BackgroundWorker 类方便地解决问题。
class Program
{
    public static void Main(string[] args)
    {
        //实现异步调用
        BackgroundWorker bgw = new BackgroundWorker();
        //DoWork事件是在BackgroundWorker对象调用RunWorkerAsync()方法后要触发的事件
        bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);  
        //RunWorkerCompleted事件是DoWork指向的方法执行完成的回调事件
        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
        //启运异步调用
        bgw.RunWorkerAsync();
        Debug.WriteLine("Main Function is Over....");
        Console.ReadLine();
    }

    static void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Debug.WriteLine("Work is over...");
    }

    static void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        Debug.WriteLine("Work is Processing...");
    }
}

打印的结果应当是:
Main Function is Over....
Work is Processing...
Work is over...

其中的Main函数线程和bgw_DoWork线程同时运行的。

第二种方法,是调用Web Service的WebMethod中的Async方法实现。
当添加完Web Service的引用以后,会在本地生成代理类,其中,会有一个和原Web Service方法名字相同而后缀是Async的方法。
在.NET FrameWork3.5中并不会自动产生这种异步方法。需要我们来生成一下。
在“Web引用”上右击,选择“配置服务引用...”
在服务引用设置窗口中把“生成异步操作”复选框选中点击确定。

在客户端编写代码的时候,我们会看到代理类中的每个方法都多了个异步操作的方法

客户端完整代码如下:
public static void Main(string[] args)
{
    Services.MyServiceSoapClient client = new Client.Services.MyServiceSoapClient();
    client.GetCNDateTimeCompleted += new EventHandler<Client.Services.GetCNDateTimeCompletedEventArgs>(client_GetCNDateTimeCompleted);
    client.GetCNDateTimeAsync();
    Debug.WriteLine("Doing other things");
    Console.ReadLine();
}

四、保证Web Service的安全

要以安全的方式访问Web服务方法,可以考虑以下安全措施:
    是谁调用?——SoapHeader身份认证。
    来自哪里?——访问IP认证。
    加密传输 ——SSL安全访问。
在这里我们主要来讨论前两种措施,至于SSL安全访问需要对操作系统进行配置,这里不再赘述。

(一)SoapHeader身份认证。

1.在Web Service中定义自己的SoapHeader派生类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services.Protocols;

public class MySoapHeader:SoapHeader
{
    private string _UserName;

    public string UserName
    {
        get { return _UserName; }
        set { _UserName = value; }
    }
    private string _Password;

    public string Password
    {
        get { return _Password; }
        set { _Password = value; }
    }
    public MySoapHeader() { }
    public MySoapHeader(string uid,string pwd)
    {
        _UserName = uid;
        _Password = pwd;
    }
    public bool IsValid(out string errorMsg)
    {
        errorMsg = "";
        if (_UserName == "admin" && _Password == "123")
        {
            return true;
        }
        else
        {
            errorMsg = "您无权调用该服务";
            return false;
        }
    }
}

2.在Web Service中添加基于SoapHeader验证的Web Service接口方法:

[WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
    public MySoapHeader SoapHeader = new MySoapHeader();
   [SoapHeader("SoapHeader")]
    [WebMethod(Description = "显示所有人员的信息")]
    public InfoData[] GetInfos(out string emsg)
    {
        if (!SoapHeader.IsValid(out emsg))
            return null;
        return new InfoDA().Select().ToArray();
    }
}

3.在客户端调用具有SoapHeader的Web Service

Services.MyServiceSoapClient client = new Client.Services.MyServiceSoapClient();
Services.MySoapHeader header = new Client.Services.MySoapHeader();
header.UserName = "admin";
header.Password = "asasas";
e.Result = client.GetInfos(header,out outmsg);
Console.WriteLine(outmsg);

(二)访问IP认证。

对来源IP的检查来进行验证,我们只允许指定IP的服务器来访问,保证点对点的安全。
bool ValidateIP(int UserID, out string exceptionInfo)
{
    exceptionInfo = "";
    string uip = HttpContext.Current.Request.UserHostAddress;
    Common dal = new Common();
    List<string> ips = dal.GetPermitIp(UserID);//得到该用户ID所允许的IP列表
    if (ips == null || ips.Count == 0)
    {
        exceptionInfo = "调用Web服务的客户端IP未被允许,无法访问!";
        return false;
    }
    if (ips.Contains(uip)) //允许IP列表中包含该IP
    {
        return true;
    }
    exceptionInfo = "调用Web服务的客户端IP未被允许,无法访问!";
    return false;
}
在具体Web方法里调用该方法检查用户访问者是否是以我们允许的IP进行访问的,以确保安全。
    优点:简单,防止非指定客户机器访问。
    缺点:IP是可以伪造的;维护IP地址表比较烦琐。且只适合于固定IP访问者的情况。

.Net下采用GET/POST/SOAP方式动态调用WebService的简易灵活方法(C#)

摘录:

在.Net环境下,最常用的方法就是采用代理类来调用WebService,可以通过改变代理类的Url属性来实现动态调用,但当xmlns改变时就会出错,似乎要重新绑定Webservice并重新编译后才能再次运行。还试过网上的一种动态编译并动态调用WebService的方式,这种方法效率低,而且需要有较高的权限,否则编译失败。曾在Sql Server 2005的CLR存储过程中用此方法调用WebService时,浪费了大半天时间,无论怎么试它就是不能成功编译。作者终于写了一个类用于动态调用WebService,只需传入WebService地址、需调用的方法及其参数,就可以随时动态调用了。现分享给大家,代码如下:

using System;using System.Web;using System.Xml;using System.Collections;using System.Net;using System.Text;using System.IO;using System.Xml.Serialization;//By huangz 2008-3-19/**//// <summary>///  利用WebRequest/WebResponse进行WebService调用的类,By 同济黄正 http://hz932.ys168.com 2008-3-19/// </summary>public class WebSvcCaller{    //<webServices>    //  <protocols>    //    <add name="HttpGet"/>    //    <add name="HttpPost"/>    //  </protocols>    //</webServices>    private static Hashtable _xmlNamespaces = new Hashtable();//缓存xmlNamespace,避免重复调用GetNamespace    /**//// <summary>    /// 需要WebService支持Post调用    /// </summary>    public static XmlDocument QueryPostWebService(String URL , String MethodName , Hashtable Pars)    {        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL + "/" + MethodName);        request.Method = "POST";        request.ContentType = "application/x-www-form-urlencoded";        SetWebRequest(request);        byte[] data = EncodePars(Pars);        WriteRequestData(request , data);        return ReadXmlResponse(request.GetResponse());    }    /**//// <summary>    /// 需要WebService支持Get调用    /// </summary>    public static XmlDocument QueryGetWebService(String URL , String MethodName , Hashtable Pars)    {        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL + "/" + MethodName + "?" + ParsToString(Pars));        request.Method = "GET";        request.ContentType = "application/x-www-form-urlencoded";        SetWebRequest(request);        return ReadXmlResponse(request.GetResponse());    }    /**//// <summary>    /// 通用WebService调用(Soap),参数Pars为String类型的参数名、参数值    /// </summary>    public static XmlDocument QuerySoapWebService(String URL , String MethodName , Hashtable Pars)    {        if (_xmlNamespaces.ContainsKey(URL))        {            return QuerySoapWebService(URL , MethodName , Pars , _xmlNamespaces[URL].ToString());        }        else        {            return QuerySoapWebService(URL , MethodName , Pars ,GetNamespace(URL));        }    }    private static XmlDocument QuerySoapWebService(String URL , String MethodName , Hashtable Pars , string XmlNs)    {    //By 同济黄正 http://hz932.ys168.com 2008-3-19        _xmlNamespaces[URL] = XmlNs;//加入缓存,提高效率        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL);        request.Method = "POST";        request.ContentType = "text/xml; charset=utf-8";        request.Headers.Add("SOAPAction" , "/"" + XmlNs + (XmlNs.EndsWith("/") ? "" : "/") + MethodName + "/"");        SetWebRequest(request);        byte[] data = EncodeParsToSoap(Pars , XmlNs , MethodName);        WriteRequestData(request , data);        XmlDocument doc = new XmlDocument() , doc2 = new XmlDocument();        doc = ReadXmlResponse(request.GetResponse());        XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);        mgr.AddNamespace("soap" , "http://schemas.xmlsoap.org/soap/envelope/");        String RetXml = doc.SelectSingleNode("//soap:Body/*/*" , mgr).InnerXml;        doc2.LoadXml("<root>" + RetXml + "</root>");        AddDelaration(doc2);        return doc2;    }    private static string GetNamespace(String URL)    {        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL + "?WSDL");        SetWebRequest(request);        WebResponse response = request.GetResponse();        StreamReader sr = new StreamReader(response.GetResponseStream() , Encoding.UTF8);        XmlDocument doc = new XmlDocument();        doc.LoadXml(sr.ReadToEnd());        sr.Close();        return doc.SelectSingleNode("//@targetNamespace").Value;    }    private static byte[] EncodeParsToSoap(Hashtable Pars , String XmlNs , String MethodName)    {        XmlDocument doc = new XmlDocument();        doc.LoadXml("<soap:Envelope xmlns:xsi=/"http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd=/"http://www.w3.org/2001/XMLSchema/" xmlns:soap=/"http://schemas.xmlsoap.org/soap/envelope//"></soap:Envelope>");        AddDelaration(doc);        XmlElement soapBody = doc.CreateElement("soap" , "Body" , "http://schemas.xmlsoap.org/soap/envelope/");        XmlElement soapMethod = doc.CreateElement(MethodName);        soapMethod.SetAttribute("xmlns" , XmlNs);        foreach (string k in Pars.Keys)        {            XmlElement soapPar = doc.CreateElement(k);            soapPar.InnerXml  = ObjectToSoapXml(Pars[k]);            soapMethod.AppendChild(soapPar);        }        soapBody.AppendChild(soapMethod);        doc.DocumentElement.AppendChild(soapBody);        return Encoding.UTF8.GetBytes(doc.OuterXml);    }    private static string ObjectToSoapXml(object o)    {        XmlSerializer mySerializer = new XmlSerializer(o.GetType());        MemoryStream ms=new MemoryStream();        mySerializer.Serialize(ms,o);        XmlDocument doc=new XmlDocument();        doc.LoadXml(Encoding.UTF8.GetString(ms.ToArray()));        if(doc.DocumentElement !=null)        {            return  doc.DocumentElement.InnerXml ;        }        else        {            return o.ToString();        }    }    private static void SetWebRequest(HttpWebRequest request)    {        request.Credentials = CredentialCache.DefaultCredentials;        request.Timeout = 10000;    }    private static void WriteRequestData(HttpWebRequest request , byte[] data)    {        request.ContentLength = data.Length;        Stream writer = request.GetRequestStream();        writer.Write(data , 0 , data.Length);        writer.Close();    }    private static byte[] EncodePars(Hashtable Pars)    {        return Encoding.UTF8.GetBytes(ParsToString(Pars));    }    private static String ParsToString(Hashtable Pars)    {        StringBuilder sb = new StringBuilder();        foreach (string k in Pars.Keys)        {            if (sb.Length > 0)            {                sb.Append("&");            }            sb.Append(HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(Pars[k].ToString()));        }        return sb.ToString();    }    private static XmlDocument ReadXmlResponse(WebResponse response)    {        StreamReader sr = new StreamReader(response.GetResponseStream() , Encoding.UTF8);        String retXml = sr.ReadToEnd();        sr.Close();        XmlDocument doc = new XmlDocument();        doc.LoadXml(retXml);        return doc;    }    private static void AddDelaration(XmlDocument doc)    {        XmlDeclaration decl = doc.CreateXmlDeclaration("1.0" , "utf-8" , null);        doc.InsertBefore(decl , doc.DocumentElement);    }}

这个类有三个公用的方法:QuerySoapWebService为通用的采用Soap方式调用WebService,QueryGetWebService采用GET方式调用,QueryPostWebService采用POST方式调用,后两个方法需要WebService服务器支持相应的调用方式。三个方法的参数和返回值相同:URL为Webservice的Url地址(以.asmx结尾的);MethodName为要调用的方法名称;Pars为参数表,它的Key为参数名称,Value为要传递的参数的值,Value可为任意对象,前提是这个对象可以被xml序列化。注意方法名称、参数名称、参数个数必须完全匹配才能正确调用。第一次以Soap方式调用时,因为需要查询WSDL获取xmlns,因此需要时间相对长些,第二次调用不用再读WSDL,直接从缓存读取。这三个方法的返回值均为XmlDocument对象,这个返回的对象可以进行各种灵活的操作。最常用的一个SelectSingleNode方法,可以让你一步定位到Xml的任何节点,再读取它的文本或属性。也可以直接调用Save保存到磁盘。采用Soap方式调用时,根结点名称固定为root。

这个类主要是利用了WebRequest/WebResponse来完成各种网络查询操作。为了精简明了,这个类中没有添加错误处理,需要在调用的地方设置异常捕获。

下面是一个调用实例:

    protected void Page_Load(object sender , EventArgs e)    ...{        try        ...{            Hashtable pars = new Hashtable();            String Url = "http://www.260dns.cn/Services/Weather.asmx";            pars["city"] = "上海";            pars["wdate"]="2008-3-19";            XmlDocument doc = WebSvcCaller.QuerySoapWebService(Url , "GetWeather" , pars);            Response.Write(doc.OuterXml);        }        catch (Exception ex)        ...{            Response.Write(ex.Message);        }    }

 

ASP.NET 使用Get方式请求WebService,并得到DataSet结果

注意:如下使用了如下webservices的配置,将导致客户端引用时将无法引用到ArrayOfInt或ArrayOfString参数。

WebService有多种方式进行请求,如Get,Post,Soap,Documentation等等,注意要使用时在web.config中的system.web节点中增加类似如下的配置节点信息

    <webServices>      <protocols>        <add name="HttpGet"/>        <add name="HttpPost"/>        <add name="HttpSoap"/>                <add name="Documentation"/>        <add name="HttpPostLocalhost"/>              </protocols>    </webServices>


在WebService类中实现了如下方法,分别是返回byte[]和XmlDocument对象

[WebMethod(Description = "返回byte数据")]        public byte[] GetKeywordQueueToByteArray(string username, string clientId, string seType, string batchTotal, string maxPage)        {            DataSet ds = InitData();            ds.DataSetName = "KeywordQueue";            byte[] bytes = DataSetToByte(ds);            //返回前可压缩,节省流量            return bytes;        }        [WebMethod(Description = "返回XmlDocument")]        public XmlDocument GetKeywordQueueToXml(string username, string clientId, string seType, int batchTotal, int maxPage)        {            XmlDocument xd = new XmlDocument();            DataSet ds = InitData();            ds.DataSetName = "KeywordQueue";            xd.LoadXml(ds.GetXml());            return xd;        }        private DataSet InitData()        {            DataSet dataset = new DataSet();            DataTable dtKeyword = new DataTable();            dtKeyword.TableName = "keywords";            dtKeyword.Columns.AddRange(new DataColumn[]{                new DataColumn() { ColumnName = "keyword", DataType = typeof(System.String) },                new DataColumn() { ColumnName = "setype", DataType = typeof(System.String) },                new DataColumn() { ColumnName = "maxpage", DataType = typeof(System.Int32) }            });            DataTable dtSeType = new DataTable();            dtSeType.TableName = "se";            dtSeType.Columns.AddRange(new DataColumn[]{                new DataColumn() { ColumnName = "code", DataType = typeof(System.String) },                new DataColumn() { ColumnName = "name", DataType = typeof(System.String) },                new DataColumn() { ColumnName = "url", DataType = typeof(System.String) }            });            dtKeyword.Rows.Add("手机", "BD", 1);            dtKeyword.Rows.Add("网络游戏", "BD", 1);            dtSeType.Rows.Add("BD", "百度", "");            dtSeType.Rows.Add("GG", "GG", "");            dataset.Tables.Add(dtKeyword);            dataset.Tables.Add(dtSeType);            return dataset;        }                /// <summary>        /// 把DateSet转化成byte         /// </summary>        /// <param name="m_ds"></param>        /// <returns></returns>        public byte[] DataSetToByte(DataSet dataset)        {            byte[] result = null;            MemoryStream memStream = new MemoryStream();            IFormatter m_formatter = new BinaryFormatter();            dataset.RemotingFormat = SerializationFormat.Binary;            MemoryStream ms = new MemoryStream();            IFormatter bf = new BinaryFormatter();            bf.Serialize(ms, dataset);            result = ms.ToArray();            ms.Close();            return result;        }

请求WebService的测试方法

public static void Invoke()        {            //string webserviceUrl = "http://localhost:9090/KeywordQueueService.asmx/HelloWorld?content=hello world";            Console.WriteLine("WebService返回XmlDocument,直接解析为DataSet");            GetDataSetFromWebServiceByXmlDocument();            Console.WriteLine("WebService返回byte[],直接解析为DataSet");            GetDataSetFromWebServiceByteArray();        }        /// <summary>        /// WebService返回的是Byte数组        /// </summary>        static void GetDataSetFromWebServiceByteArray()        {            string webserviceUrl = "http://localhost:9090/KeywordQueueService.asmx/GetKeywordQueueToByteArray?username=test1&clientId=dkkad8789adda&seType=BD&batchTotal=61&maxPage=1";            string charSet = "utf-8";            try            {                string pageContent = GetWebServiceContent(webserviceUrl, charSet);                Console.WriteLine(pageContent);                //加载文档                XmlDocument xd = new XmlDocument();                xd.LoadXml(pageContent);                byte[] bytes = Convert.FromBase64String(xd.DocumentElement.InnerText);                //因为WebService方法中序列化过,所以这里需要反序列化                MemoryStream ms = new MemoryStream(bytes);                System.Runtime.Serialization.IFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();                object obj = bf.Deserialize(ms);                DataSet dataset = obj as DataSet;                ms.Close();            }            catch (Exception err)            {                Console.WriteLine(err.ToString());            }        }        /// <summary>        /// WebService返回的是XmlDocument        /// </summary>        static void GetDataSetFromWebServiceByXmlDocument()        {            string webserviceUrl = "http://localhost:9090/KeywordQueueService.asmx/GetKeywordQueueToXml?username=test1&clientId=dkkad8789adda&seType=BD&batchTotal=61&maxPage=1";            string charSet = "utf-8";            try            {                string pageContent = GetWebServiceContent(webserviceUrl, charSet);                Console.WriteLine(pageContent);                XmlDocument xd = new XmlDocument();                xd.LoadXml(pageContent);                DataSet ds = new DataSet();                ds.ReadXml(new XmlNodeReader(xd));            }            catch (Exception err)            {                Console.WriteLine(err.ToString());            }        }        /// <summary>        /// 请求WebService        /// </summary>        /// <param name="webserviceUrl"></param>        /// <param name="charset"></param>        /// <returns></returns>        static string GetWebServiceContent(string webserviceUrl, string charset)        {            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(webserviceUrl);            request.Method = "GET";            request.Timeout = 60000;            HttpWebResponse response = request.GetResponse() as HttpWebResponse;            Stream streamReceive = response.GetResponseStream();            Encoding encoding = Encoding.Default;            if (!string.IsNullOrEmpty(charset) && Encoding.GetEncoding(charset) != Encoding.Default)            {                encoding = Encoding.GetEncoding(charset);            }            StreamReader streamReader = new StreamReader(streamReceive, encoding);            return streamReader.ReadToEnd();        }        /// <summary>        /// 将Byte[]转换为DataSet        /// <para>反序列化了一下</para>        /// </summary>        /// <param name="data"></param>        /// <returns></returns>        static DataSet GetDataSetByUnCompressByte(byte[] data)        {            MemoryStream ms = new MemoryStream(data);            System.Runtime.Serialization.IFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();            object obj = bf.Deserialize(ms);            DataSet dsResult = (DataSet)obj;            ms.Close();            return dsResult;        }


注意,如果数据量极少时,直接返回XmlDocument占用流量更小,如果数据量稍大,则使用压缩的byte[]方式更节省流量!


JAVA开发参考

Smart Framework:轻量级 Java Web 框架

 

Web Service开发中需要注意的问题

WebMethod的名字、参数和返回值应该一看就知道这接口大概是干什么用的。
WebMethod参数要尽量简单。只有一个参数的服务接口,往往不能满足业务需求。但过多的参数也提高了出错的几率
WebMethod要提供对参数和返回值的校验
WebMethod返回值应该是简单的语言无关
WebMethod谨慎地抛出异常
    对Web Service中的任何异常都应该进行相应的处理。可以简单地归纳为以下两种情况。
        第1种情况是返回值是简单类型,比如bool型,就true和false两种情况,不抛出异常怎么办?选择有两种,一是抛出异常,二是改变接口,返回int用1和0对应true和false,用-1对应系统异常。
        第2种情况是返回值是复杂对象,可以通过参数out string exceptionInfo来返回异常信息。

WebMethod禁用HTTP POST/GET协议
    Web服务绑定到3种协议:HTTP/POST、HTTP/GET和SOAP。HTTP/GET的安全性不如SOAP。
WebMethod避免使用ASP.NET会话状态

示例:

免费的天气预报webservice接口 

参考网址:

C#调用WebService(介绍了WebService基本概念,以及C#建立及调用WebService的示例)

WebService常用功能讲解(开发要点)

Web Service的研究(详细介绍WSDL和SOAP结构及示例讲解,图文并茂)

WSDL(官方WSDL解释)

.Net下采用GET/POST/SOAP方式动态调用WebService的简易灵活方法(C#)

ASP.NET 使用Get方式请求WebService,并得到DataSet结果

Smart Framework:轻量级 Java Web 框架(赞)

免费的天气预报webservice接口 

0 0
原创粉丝点击