本文转自:

WebService

经常看到这些词汇,也有baidu或google过,但记忆里总是模糊,不确定,以至于别人问及的时候,总说不清楚。开篇随笔记录下。大家有补充或者意见的尽请留文。

What – OData是什么?

1. WebService基本认识

WebService最早是微软提出了一种以XML为载体网络信息传输的规范,现在几乎所有的语言与平台都支持,带有状态机制,不依赖于容器,可以发送一个xml作为其请求内容.

WebService通常是基于http的远程方法调用(RMI),号称是可以返回远程对象,一般来说客户端可以象调用本地方法一样调用WebService的方法。

在各种各样的对 WebService的解释版本中, 足够官方, 却并没有太大帮助,
在我理解, WebService 是一种应用程序组件, 提供一个接口, client端
提供参数, 通过多种方式发起请求,获取到相应的返回值,
可以通过WebService进行网络通信, 同时可以实现不同平台间的数据共享,
仅此而已.

参考链接:WebService的两种方式SOAP和REST比较
(转)

不过需要注意的一点是,WebService有两种方式:

  1. SOAP协议方式, 三要素为 SOAP, WSDL, UDDI
  2. REST方式

本文顺序:

OData – Open Data Protocol,是一个设计和使用RESTful
API的标准。REST本身只是一个构建web服务的思想和理念,其没有规定一个统一的标准来限制开发人员该如何设计RESTful
API。其实我们实际开发中的确也没有遵循某个统一的标准去设计WebAPI。因为大多数场景下,遵循一个统一的标准并不是必要的。但在某些场景下,有这样一个标准却能带来很大的好处。

1.1 SOAP概述

SOAP(Simple Object Access Protocol), 简单对象访问协议.

简单对象访问协议是交换数据的一种协议规范,是一种轻量的、简单的、基于XML(标准通用标记语言下的一个子集)的协议,它被设计成在WEB上交换结构化的和固化的信息。

SOAP的概念理解:

SOAP消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求
/ 应答的模式。所有的 SOAP消息都使用 XML 编码。一条
SOAP消息就是一个包含有一个必需的 SOAP 的封装包,一个可选的 SOAP
标头和一个必需的 SOAP 体块的 XML 文档。

具体表现如下:

<!-- 在SOAP的XML消息中,强制使用的 SOAP 的 Envelope 元素是 SOAP 消息的根元素。 -->
<!-- 且SOAP命名空间的使用中, 值始终应为xmlns:soap=http://www.w3.org/2001/12/soap-envelope-->
<?xml version="1.0"?>

<!-- 下面便是SOAP封装包, 将对应的XML消息封装至 soap 命名空间 -->
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
...
Message information goes here
...
</soap:Envelope>

<!--可选的SOAP头-->
<!--可选的 SOAP Header 元素可包含有关 SOAP 消息的应用程序专用信息(比如认证、支付等)。如果 Header 元素被提供,则它必须是 Envelope 元素的第一个子元素。-->

<soap:Header>
<m:Trans
xmlns:m="http://www.w3school.com.cn/transaction/"
soap:mustUnderstand="1">234</m:Trans>
</soap:Header>

<!--必需的 SOAP Body,可包含打算传送到消息最终端点的实际 SOAP 消息。-->
<soap:Body>
<m:GetPrice xmlns:m="http://www.w3school.com.cn/prices">
    <m:Item>Apples</m:Item>
</m:GetPrice>
</soap:Body>

除此之外, 还有 SOAP Fault元素,用于指示错误消息,

<!--实例-->
<!--SOAP请求-->
POST /InStock HTTP/1.1
Host: www.example.org
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">

<soap:Body xmlns:m="http://www.example.org/stock">
    <m:GetStockPrice>
    <m:StockName>IBM</m:StockName>
    </m:GetStockPrice>
</soap:Body>

</soap:Envelope>

<!--SOAP响应-->
HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">

<soap:Body xmlns:m="http://www.example.org/stock">
    <m:GetStockPriceResponse>
    <m:Price>34.5</m:Price>
    </m:GetStockPriceResponse>
</soap:Body>
</soap:Envelope>

SOAP 通讯协议使用 HTTP 来发送XML 格式的信息,SOAP 把 XML
的使用代码化为请求和响应参数编码模式, 并用HTTP 作传输。

具体地讲, 一个SOAP 方法可以简单地看作遵循SOAP编码规则的HTTP请求和响应,
一个 SOAP终端则可以看作一个基于HTTP 的URL, 它用来识别方法调用的目标。

在我目前的理解中, SOAP可以概括为以下要点:

SOAP消息用XML进行编码, 同时拥有相匹配的编码规则, 便于通信, 解析, 交互;

同时在HTTP上 发送SOAP的语义会被映射到HTTP语义, 且不仅限于HTTP协议,
SOAP也可以绑定至TCP/UDP协议上;

通过HTTP发送 XML格式信息, 服务器端 转换解析相应的 信息, 处理并响应.

HTTP + XML = SOAP

一、Rest(Restful)跟RPC**

OData的理想是, 无论哪个组织构建的RESTful
API,只要其符合OData标准。其他组织就可以按照OData标准中定义的方式去使用这个API获取/修改资源。这个可以类比SQL标准之于RDBMS关系。无论什么关系型数据库,如果其声称支持SQL
标准,任何人就可以使用标准SQL查询语句来查询数据。

SOAP优点

  1. 可扩展的。SOAP 无需中断已有的应用程序, SOAP 客户端、
    服务器和协议自身都能发展。而且SOAP
    能极好地支持中间介质和层次化的体系结构。
  2. 简单的。客户端发送一个请求,调用相应的对象,
    然后服务器返回结果。这些消息是XML 格式的,并且封装成符合HTTP
    协议的消息。因此,它符合任何路由器、 防火墙或代理服务器的要求。
  3. 完全和厂商无关。SOAP可以相对于平台、 操作系统、
    目标模型和编程语言独立实现。另外,传输和语言绑定以及数据编码的参数选择都是由具体的实现决定的。
  4. 与编程语言无关。SOAP 可以使用任何语言来完成,只要客户端发送正确SOAP
    请求( 也就是说, 传递一个合适的参数给一个实际的远端服务器)。SOAP
    没有对象模型,应用程序可以捆绑在任何对象模型中。

个人思考:

如果想要深入了解 SOAP, 需要了解 相应的 XML封装, 同时对
XML扩展文本标记语言有一定程度的了解. 粗浅的使用, 目前足以;

在看完了XML 和 XML schema 之后, 再对文章进行了一些补充更新, 对SOAP
WSDL也有了更清晰的认知. 各种官方的语言 被转化为实际的 代码之后,
理解起来总是更简单的.

二、SOAP啥意思?

标准化的另一个好处:可以将Odata协议实现到一个通用的类库中,通过这个类库去创建和访问RESTful
API可以减少开发人员的工作量。官网上有很多这样的组件。

1.2 WSDL概述

WebService的另一大核心:

WSDL(Web Services Description Language) 网络服务描述语言

如同字面意思所描述的那样,WSDL 是这样一个基于 XML 的语言,用于描述 Web
Service 及其函数、参数和返回值。因为是基于 XML 的,所以 WSDL
既是机器可阅读的,又是人可阅读的.(XML的自描述性)

以下是几个元素定义,有助于更直观的理解 WSDL:

<portType> web service 执行的操作

下面截取了 WeatherWS.xml的一部分, 以便于对 WSDL 有更直观的理解

<wsdl:portType name="WeatherWSSoap">
    <wsdl:operation name="getRegionDataset">
        <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
            <br /><h3>获得中国省份、直辖市、地区;国家名称(国外)和与之对应的ID</h3><p>输入参数:无,返回数据:DataSet。</p><br />
        </wsdl:documentation>
        <wsdl:input message="tns:getRegionDatasetSoapIn"/>
        <wsdl:output message="tns:getRegionDatasetSoapOut"/>
    </wsdl:operation>
    <wsdl:operation name="getRegionProvince">
        <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
            <br /><h3>获得中国省份、直辖市、地区和与之对应的ID</h3><p>输入参数:无,返回数据:一维字符串数组。</p><br />
        </wsdl:documentation>
        <wsdl:input message="tns:getRegionProvinceSoapIn"/>
        <wsdl:output message="tns:getRegionProvinceSoapOut"/>
    </wsdl:operation>
    <wsdl:operation name="getRegionCountry">
        <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
            <br /><h3>获得国外国家名称和与之对应的ID</h3><p>输入参数:无,返回数据:一维字符串数组。</p><br />
        </wsdl:documentation>
        <wsdl:input message="tns:getRegionCountrySoapIn"/>
        <wsdl:output message="tns:getRegionCountrySoapOut"/>
    </wsdl:operation>
    <wsdl:operation name="getSupportCityDataset">
        <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
            <br /><h3>获得支持的城市/地区名称和与之对应的ID</h3><p>输入参数:theRegionCode = 省市、国家ID或名称,返回数据:DataSet。</p><br />
        </wsdl:documentation>
        <wsdl:input message="tns:getSupportCityDatasetSoapIn"/>
        <wsdl:output message="tns:getSupportCityDatasetSoapOut"/>
    </wsdl:operation>
    <wsdl:operation name="getSupportCityString">
        <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
            <br /><h3>获得支持的城市/地区名称和与之对应的ID</h3><p>输入参数:theRegionCode = 省市、国家ID或名称,返回数据:一维字符串数组。</p><br />
        </wsdl:documentation>
        <wsdl:input message="tns:getSupportCityStringSoapIn"/>
        <wsdl:output message="tns:getSupportCityStringSoapOut"/>
    </wsdl:operation>
    <wsdl:operation name="getWeather">
        <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
            <br /><h3>获得天气预报数据</h3><p>输入参数:城市/地区ID或名称,返回数据:一维字符串数组。</p><br />
        </wsdl:documentation>
        <wsdl:input message="tns:getWeatherSoapIn"/>
        <wsdl:output message="tns:getWeatherSoapOut"/>
    </wsdl:operation>
</wsdl:portType>

它可描述一个 web service、可被执行的操作,以及相关的消息。

<message> web service 使用的消息

<types> web service 使用的数据类型

<binding> web service 使用的通信协议

需要了解更多的话, 官方文档更为详细: WSDL
文档

三、实例区分RESTful风格的 Web API与采用RPC风格的SOAP
WebService?

 

1.3 UDDI概述

UDDI(Universal Description Discovery and Integration), UDDI
是一种目录服务,企业可以使用它对 Web services 进行注册和搜索。

UDDI 指的是通用描述、发现与集成服务

UDDI 是一种用于存储有关 web services 的信息的目录。

UDDI 是一种由 WSDL 描述的 web services 界面的目录。

UDDI 经由 SOAP 进行通信

UDDI 被构建入了微软的 .NET 平台

总结来说:

SOAP定义协议层交互, 映射, WSDL用来描述 WebService, UDDI
用来进行WebService 注册 搜索.

 

Who – 谁发布了OData?

1.4 REST

一、REST(REpresentational State Transfer) 翻译为 表现状态转换
 有个老头(Fielding)又称之为“分布式超媒体应用”

该标准由微软发起,前三个版本1.0、2.0、3.0都是微软开放标准。

1.4.1 Rest基本含义

在开始, 仍然是参考链接, 看过后,相信会对 Rest有一个比较全面的了解:

REST简介-Amber-Garden

REST(Representational State Transfer) 表现层状态转移;

REST通常基于使用HTTP,URI,和XML
以及HTML(标准通用标记语言下的一个应用)这些现有的广泛流行的协议和标准。

在阅读上述博文前,可以先了解这样几个概念, 同时也是对Rest的一种描述:

参考:REST无状态的理解

  1. 首先必须清楚的是 Rest是一种软件架构风格而并非是标准;

  2. Rest中并非是向以往的java项目设计那样, 以业务逻辑/功能为考虑出发点,
    而是以 资源 为出发点考虑的, 因此很重要的一个特性为
    面向资源的接口设计。

  3. 资源: 操作实体都作为资源来看待, 如在一个商城项目中, 商品类别,
    具体的商品, 物流, 用户资料
    等都会被看做资源,你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
    而 Client端的操作都是对资源本身的操作。

  4. 表现层:
    指的是资源的表现层,它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对”表现层”的描述。

  5. 状态: 应用状态(Application State)和资源状态(Resource State):

  • 应用状态:指的是与某一特定请求相关的状态信息
  • 资源状态:则反映了某一存储在服务器端资源在某一时刻的特定状态,该状态不会因为用户请求而改变,任何用户在同一时刻对该资源的请求都会获得这一状态的表现(Representation)。

当用户登录后, 一般需要保存对应的 用户信息, 用户信息本身就是一种资源,
特定的用户 对应着用户资源的一种状态,
而在服务器端取出存储在Session中的用户资源时, 实际上也就意味着
服务器保存着用户资源的 状态信息, 但在 Rest架构设计中,
实际上是期望请求方提供的,所以传递Session ID被认为是unRESTful的做法。

深感自己水平有限, 知识储备不足,
因此仅仅是将REST简介-Amber-Garden博文的要点提出来:

除了上述关于资源状态的概念理解之外,还有以下信息:

  1. Rest架构设计有以下几条约束:

  2. 使用客户/服务器模型。客户和服务器之间通过一个统一的接口来互相通讯。

  3. 层次化的系统。在一个REST系统中,客户端并不会固定地与一个服务器打交道。

  4. 无状态。在一个REST系统中,服务端并不会保存有关客户的任何状态。也就是说,客户端自身负责用户状态的维持,并在每次发送请求时都需要提供足够的信息。

  5. 可缓存。REST系统需要能够恰当地缓存请求,以尽量减少服务端和客户端之间的信息传输,以提高性能。

  6. 统一的接口。一个REST系统需要使用一个统一的接口来完成子系统之间以及服务与用户之间的交互。这使得REST系统中的各个子系统可以独自完成演化。

    其中无状态 和 统一接口 又是 较为特别的约束条件:

    1. 无状态: 理解了状态, 也就明白了什么叫无状态, 在我的理解中,
      通俗的来讲就是, 对于一个请求, 请求中已经包含了足够的信息,
      并不需要服务器端自身保存任何资源, 用以进行对请求的处理,
      资源的返回等。

    2. 统一接口:

      1. 每个资源都拥有一个资源标识。每个资源的资源标识可以用来唯一地标明该资源。

      2. 消息的自描述性。在REST系统中所传递的消息需要能够提供自身如何被处理的足够信息。例如该消息所使用的MIME类型,是否可以被缓存等。

      3. 资源的自描述性。一个REST系统所返回的资源需要能够描述自身,并提供足够的用于操作该资源的信息,如如何对资源进行添加,删除以及修改等操作。也就是说,一个典型的REST服务不需要额外的文档对如何操作资源进行说明。

      4. HATEOAS。即客户只可以通过服务端所返回各结果中所包含的信息来得到下一步操作所需要的信息,如到底是向哪个URL发送请求等。也就是说,一个典型的REST服务不需要额外的文档标示通过哪些URL访问特定类型的资源,而是通过服务端返回的响应来标示到底能在该资源上执行什么样的操作。一个REST服务的客户端也不需要知道任何有关哪里有什么样的资源这种信息。

  7. 除了资源的状态, 定位以外, 则是对资源的操作: 基于HTTP, 则有:

    1. GET 读取某个资源。

    2. DELETE 删除某个资源

    3. POST POST动词会在目标URI之下创建一个新的子资源。

    4. PUT则是根据请求创建或修改特定位置的资源。此时向服务端发送的请求的目标URI需要包含所处理资源的ID.(对于put请求仍然不是很理解)

  8. Rest优点:

  9. 优点是基于特性而来的, 最大的特性便是
    无状态。因为无状态原则的特性,让RESTful在分布式系统中得到了广泛的应用,它改善了分布式系统的可见性、可靠性以及可伸缩性,同时有效的降低了Client与Server之间的交互延迟。

  10. 其次,无状态请求有较强的容错性和可伸缩性。如果一台服务器宕机,无状态请求可以透明地交由另一台可用Server来处理,而有状态的请求则会因为存储请求状态信息的Server宕机而承担状态丢失的风险。

至于 Rest架构设计等其他, 在博文中有说明, 且对WebService 影响不大,
在此就不再说明。

  REST不是一个标准,而是一种应用架构风格。

 

2. WebService 与 Java

在对WebService 及 SOAP 和 Rest有了基本的了解后, 接下来就是
WebService在Java中的简单实现.

  WebAPI采用的就是这种风格。

When – 什么时候成为了工业标准?

2.1 SOAP方式

  这是一种面向资源的架构风格。

第四个版本4.0于2014年3月17日在OASIS投票通过成为开放工业标准

2.1.1 使用 JDK 方式开发

参考链接: ;

重点在这样几个地方:

  1. 在 接口类和实现类 均需要 @WebService注解, 同时在接口类的方法上,
    采用@WebMethod接口

  2. 在 main方法中, 用 EndPoint.publish(address, 实现类); 发布即可

  3. 访问的时候通过 address.wsdl 即可访问发布的 WebService;

  4. 打开命令行窗口,切换到src目录,执行”wsimport -keep
    address?wsdl生成客户端代码.

  5. 使用生成的java类创建实现类,调用对应的方法 接口.

  6. 注解有以下几种:

  7. 类级别:

    • @javax.jws.WebService(targetNamespace = “”, name =
      “”,serviceName = “”)

      1. targetNamespace :生成的 WSDL 中使用的名称空间.

      2. name:Web Service 的名称,映射到 WSDL 文件中的

      3. serviceName: Web Service 的服务名,映射到 WSDL 文件

    • @javax.jws.soap.SOAPBinding(parameterStyle =
      javax.jws.soap.SOAPBinding.ParameterStyle.BARE)

      1. parameterStyle
        :确定方法参数是否表示整个消息正文,或者参数是否是包装在以操作命名的顶层元素中的元素。默认值:javax.jws.soap.SOAPBinding.ParameterStyle.WRAPPED
  8. 方法级别

    • @javax.jws.WebResult(name = “”, targetNamespace = “”, partName =
      “”)

      name:指定生成的 WSDL 中的操作结果的名称, 默认名称“return”。

    • @javax.jws.WebMethod(operationName=””)

      operationName: 指定方法公开的公共操作名,映射到 WSDL 文件中的

    • @javax.jws.WebParam(name=””,targetNamespace=””)

      name: 指定输入参数名,而不是该参数的Java;

代码如下:

@WebService
public interface IHelloWorld {

    @WebMethod
    String hello();
}

@WebService
public class HelloWorldImpl implements IHelloWorld {
    @override
    public String hello() {
        return "helloWorld";
    }
}

public class Publish {
    public static void main(String[] args) {

        String address = "http://localhost:80/Server/Test";
        Endpoint.publish(address, new HelloWorldImpl());
        System.out.println("发布成功");
    }
}
//访问地址为:
//http://localhost:80/Server/Test?wsdl

//在命令行中,切换到 Src文件夹下 , wsimport -keep http://localhost:80/Server/Test?wsdl 即可.

public class WebServiceDemo {

    public static void main(String[] args) {

        //创建一个用于产生WebServiceImpl实例的工厂,WebServiceImplService类是wsimport工具生成的
        HelloWorldImplService factory = new HelloWorldImplService();
        //通过工厂生成一个WebServiceImpl实例,WebServiceImpl是wsimport工具生成的
        HelloWorldImpl wsImpl = factory.getHelloWorldImplPort();
        //调用WebService的sayHello方法
        String resResult = wsImpl.hello();
    }
}

通过以上代码, 便实现了一个 WebService的发布, 而后访问对应的网址,
生成对应的Java, 调用并使用相应的方法及接口.

在天气, 电话号码,等等的已经开放的WebService服务在:

如: 访问2400多个城市天气预报Web服务 即可使用:

通过 wsimport
-keep url 即可;

当然在执行的时候可能会报错:

无法将名称 ‘s:schema’ 解析为 ‘element declaration’ 组件。

这个时候需要在浏览器中 打开相应的 链接, 右键另存到本地,将文件中所有出现

<s:element ref="s:schema"/><s:any/>

的地方全部使用​

 <!--这句代码的意思, 是 定义 任意 子元素出现的总次数-->
 <s:any minOccurs="0" maxOccurs="unbounded"/>

替换掉。

而后使用 wsimport -keep 文件路径即可.

  与具体的技术平台无关,即采用REST架构的应用不一定是建立在Web上。

 

2.1.2 使用 cxf搭建WebService项目

在这之前首先要提到 Java 开发WebService的几种框架:

参考链接: JAVA开发Web
Service几种框架介绍

Axis,axis2,Xfire,CXF以及JWS, 其中 JWS 为 JavaWebService, 上面所说的
使用 JDK开发 WebService即为 JWS方式, 在 Java 1.6以后支持.

如果你需要把你的实现侧重JAVA并希望和Spring集成,CXF就是更好的选择,特别是把你的Web
Service嵌入其他的程序中。

下面开始谈谈 cxf 开发WebService, 个人也是才开始了解 WebService
因此仅仅做出入门级的 使用开发案例.

官方 jar 包下载:

简单流程如下:

  1. 创建 JavaWeb项目

  2. 在创建 接口和实现类的过程 与JWS并没有区别, 不过不需要创建对应的
    main方法, 毕竟是与 Spring相结合, 需要在 Web.xml 和
    applicationContext.xml中配置即可.

下面是对应的代码:

@WebService
public interface WebServiceDemo {
    //如果没有 WebMethod注解, 且配置属性, 则方法名为 print;
    void print();
}

@WebService
public class WebServiceDemoImpl implements WebServiceDemo {

    @Override
    public void print() {
        // TODO Auto-generated method stub
        System.out.println("print");
    }

}

//applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://cxf.apache.org/jaxws
    http://cxf.apache.org/schemas/jaxws.xsd">

<jaxws:endpoint id="webServiceDemo" implementor="zyx.webservice.demo.WebServiceDemoImpl" address="/webServiceDemo"/>
</beans>

//web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>WebServiceDemo</display-name>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
    <servlet-name>CXFService</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>CXFService</servlet-name>
    <url-pattern>/webservice/*</url-pattern>
</servlet-mapping>
</web-app>

项目启动后访问路径为:

http://localhost/WebServiceDemo/webservice/webServiceDemo?wsdl

至此:使用 SOAP方式, 发布一个 WebService项目已经介绍完了.

  

Why – 为什么需要OData?

2.2 Rest方式

至于Rest的优点, 我就不再赘述, 在 1.4 节的介绍已经足够进行简单的开发了.

Java中 Restful 框架实在不少, 在这里我使用的是: Jersey

已经讲到这里了, 就将 Rest相关注解也讲一下, 有个大概的了解认识:

参考链接: REST 在 Java 中的使用

  1. 资源定位:正如之前提到的, 在统一接口 约束中的 子约束的第一条就是:
    每个资源都要有 唯一标识 即 URI.

    @ApplicationPath
    标识应用路径,用于由@Path提供的所有资源uri的基本uri。当发布在一个servlet容器中,它的值可以使用web.xml中的servlet-mapping进行重写。

    @path 标识要请求的资源类或类方法的uri路径。

    @PathParam 将uri中指定的路径参数绑定到资源方法参数

    @QueryParam
    将http请求的Query参数绑定到资源方法参数,资源类的字段,或资源类的bean属性。

    @FormParam
    与上述用法类似,不过是将http请求的Form表单中的参数绑定到资源方法参数

    @CookieParam 同上

    @HeaderParam 指的是 http header的值

    @MatrixParam(不太了解)将uri矩阵参数的值绑定到资源方法参数,资源类的字段

    @DefaultValue 配置以上参数的默认值

    //UserResource下的根路径
    @Path("/user")
        public class UserResource {
        @GET
        //传递参数 /user/user/zyx 则参数为 zyx;
        @Path("/user/{userName}")
        //访问方法的路径
        @Produces(MediaType.APPLICATION_JSON)
        ///user/user/zyx?password=123 则password参数为123
        public User getUser(@DefaultValue("zyx") @PathParam("userName") String userName, @QueryParam("password") String password) {
        ...
        }
    
    }
    

    @BeanParam
    将请求中的bean绑定到资源方法参数,资源类的字段,或资源类的bean属性

    //bean的配置
    @javax.xml.bind.annotation.XmlRootElement
    public class UserBean{
        @FormParam("userName")
        private String userName;
        @FormParam("age")
        private int age;
    ...
    }
    //资源方法配置
    @Path("/user")
        public class UserRecource {
        @javax.ws.rs.POST
        @Path("/insertUserBean")
        @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
        public String insertUserBean(@BeanParam UserBean userBean){
        ...
        }
    }
    

    数据格式:

    @Consumes
    定义一个资源类的方法或MessageBodyReader能够接受的媒体类型。方法级别的@Consumes会覆盖类级别的@Consumes。(指定接受的
    Client端媒体类型)

    @Produces
    方法级别的@Produces会覆盖类级别的@Produces(用于定义方法的响应实体的数据类型)

    @Produces(“application/json”);

    指定多个MIME类型 @Produces({“application/json”,”application/xml”});

    也可以用常量值得方式指定:

    @Produces(value = {MediaType.APPLICATION_JSON})

    媒体类型的常量值在javax.ws.rs.core.MediaType中

    同时可以为每种类型定义质量因素:
    质量因素是取值范围从0到1的小数值。如果不定义质量因素,那么该类型的质量因素默认为1,
    如果同一请求路径对应不同的 处理 方法, 则根据 质量因素优先选取

    参考链接:
    ttp://book.51cto.com/art/201701/529132.htm

    @Encoded 不需要自动解码,直接使用编码后的请求值

代码如下:

在这里我使用的是 maven: pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.com</groupId>
<artifactId>demo02</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>demo02 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.ws.rs/jsr311-api -->
    <!-- 加了会报错 -->
    <!-- <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>jsr311-api</artifactId>
        <version>1.1.1</version>
    </dependency> -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>

    <!--jersey -->
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet-core</artifactId>
        <version>2.13</version>
    </dependency>

    <!--JAXB API -->
    <dependency>
        <groupId>javax.xml.ws</groupId>
        <artifactId>jaxws-api</artifactId>
        <version>2.1</version>
    </dependency>

    <!-- Json支持 -->
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-core-asl</artifactId>
        <version>1.9.12</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>1.9.12</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-jaxrs</artifactId>
        <version>1.9.12</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.tomcat.maven/tomcat8-maven-plugin -->
    <!-- <dependency>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat8-maven-plugin</artifactId>
        <version>3.0-r1756463</version>
    </dependency> -->

</dependencies>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <display-name>demo02</display-name>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <filter>
        <filter-name>characterEncoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>JerseyServlet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <!-- 配置自己的资源加载类去加载资源 -->
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>cn.com.easy.resource.ApplicationAPI</param-value>
        </init-param>
        <!-- 配置默认的资源包路径,使用默认的配置类去加载资源 -->
        <!-- <init-param> -->
        <!-- <param-name>jersey.config.server.provider.packages</param-name> -->
        <!-- <param-value>com.cisdi.jersey.api</param-value> -->
        <!-- </init-param> -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>JerseyServlet</servlet-name>
        <url-pattern>/restful/*</url-pattern>
    </servlet-mapping>
</web-app>

定义自己的资源加载器

    /**
    * 这个类就是将各种资源加载进来,暴露给client, 可以自由选择需要加载的资源, 日志等;
    * @author Administrator
    *
    */
    public class ApplicationAPI extends ResourceConfig {

        {
            System.out.println("启动资源加载器");
        }

        public ApplicationAPI() {
            register(Demo.class);
            // 注册数据转换器
            register(JacksonJsonProvider.class);

            // 注册日志
            register(LoggingFilter.class);
        }
    }

定义对应的java类, 也即接口的访问程序

    @Path("/demo")
    public class Demo {
        @GET
        @Path("/demo01/{username }")
        @Produces(value = {MediaType.APPLICATION_JSON})
        public String demo01(@PathParam("username") String username) {
            return username;
        }
    }

再者就是 使用 Spring时的自定义配置, applicationContext.xml

    <!-- 自动注册 -->
    <context:annotation-config />

    <!-- 扫描对应的包 -->
    <context:component-scan base-package="cn.com.easy.restful" />

以上就是整个项目的搭建.

我觉得项目的创建并不难, 也并不是在以后应该关注的店,
而在开发中更应该注意的是, Restful的设计风格, 很容易一不小心就走偏了.
需要对 Restful 的 资源, 状态, 等概念理解到位, 这也是我最大的收获.

后感:
个人感觉, SpringMvc 的项目搭建方式与 Restful 方式挺相近, 不过关注点不同,
在 SpringMvc 的核心点 是功能与业务逻辑, 而在Rest中, 更重要的是 资源本身,
甚至于 Get Post 等方式的选取也更重要, 以便于理清楚Rest的 资源逻辑 问题.

  RPC

OData是一个协议,一个标准。所以这个问题等同于为什么我们需要协议。类比TCP协议就可以理解一般。假设你开发的组件必须要和某个第三方组件通信,如果第三方组件不支持TCP而只支持其内部开发的一个私有协议,你就肯定头大了,你必须在你的组件里单独为其实现这个私有协议。如果大家都支持TCP协议,不就省事了么。这就是标准协议的作用:协议和标准用于制定一个统一通用的规则。
我们只需要按照这个协议或标准生产组件,那么这个组件就可以方便的和其他组件集成/协作。而无须根据其他组件的私有标准定制化组件。

  RPC则是WebService采用的应用架构风格。

 

  RPC是一种面向操作(功能)的架构风格

前面说到Rest只是一种设计Web服务的思想,不是一种标准化的协议。正由于缺乏标准化,从而导致各家公布的Restful
API 统一通用方面的欠缺。OData就是为弥补这种欠缺而被提出来的标准协议。

  

 

  两者啥关系?可以结合Web Service跟Web API

下面全是延伸阅读可略过。

  对于WebService 和 Web
API这两个概念,WebService是一个广义的概念,既包括采用RPC的SOAP
WebService,也包括直接建立在Web上的非SOAP Web Service,后者又可以称为Web
API
。至于Web API是否是RESTful的,这取决于设计。

Web服务有两种实现方式,一是SOAP协议方式,二是REST方式。SOAP是一套完整的实现Web服务的解决方案。这里有必要先简单了解SOAP方式的Web服务,然后对比SOAP方式,我们会发现REST方式欠缺了什么。

  

 

二、SOAP(简单对象访问协议)基于百度百科加其他

SOAP方式的Web服务中的Web服务描述语言(WSDL)和简单对象访问协议(SOAP)一起构成了SOAP方式下的Web服务的结构单元。客户端通过WSDL可以了解Web服务公开了那些可以被执行的方法以及Web服务可以发送或接收的消息格式(解决了公布访问资源方法的问题)。客户端按照SOAP将调用位于远程系统上的服务所需信息序列化为消息(解决了如何调用远程方法的问题)。注意WSDL描述的服务以及SOAP消息都是符合统一标准的,都是机器可读的.

  SOAP 是基于XML 和 HTTP ,其通过XML 来实现消息描述,然后再通过 HTTP
实现消息传输的一种用于访问Web服务的协议。(是跟HTTP一块玩的,是种协议,这样理解起来~~完美~

WSDL基于XML格式,用来描述Web服务。WSDL文档可以看成是客户端和服务器之间的一个协约。使用WSDL工具,你可以自动处理这个过程,几乎不用手工编写代码就能够让应用程序整合新的服务。因此WSDL是Web服务体系结构的基础,因为它提供了一个通用语言,用来描述服务和整合这些服务的平台。

构建模块

一条 SOAP 消息就是一个普通的 XML 文档,包含下列元素:

  • 必需的 Envelope 元素,可把此 XML 文档标识为一条 SOAP 消息

  • 可选的 Header 元素,包含头部信息

  • 必需的 Body 元素,包含所有的调用和响应信息

  • 可选的 Fault 元素,提供有关在处理此消息所发生错误的信息

 

SOAP本身提供了与Web服务交换信息的方法。SOAP是序列化调用位于远程系统上的服务所需信息的标准方法,这些信息可以使用一种远程系统能够读懂的格式通过网络发送到远程系统,而不必关心远程系统运行于何种平台或者使用何种语言编写。SOAP以XML格式提供了一个简单、轻量的用于在分散或分布环境中交换结构化和类型信息的机制。实际上它通过提供一个有标准组件的包模型和在模块中编码数据的机制,定义了一个简单的表示应用程序语义的机制。

语法规则

这里是一些重要的语法规则:

  • SOAP 消息必须用 XML 来编码

  • SOAP 消息必须使用 SOAP Envelope 命名空间

  • SOAP 消息必须使用 SOAP Encoding 命名空间

  • SOAP 消息不能包含 DTD 引用

  • SOAP 消息不能包含 XML 处理指令

 

 

消息基本结构

<?xml
 version="1.0"?>
<soap:Envelope
 xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
 soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">

<soap:Header>
<!--百度百科示例-->
</soap:Header>

<soap:Body>
<!--百度百科示例-->
<soap:Fault>
<!--百度百科示例-->
</soap:Fault>
</soap:Body>
</soap:Envelope>

 

这篇文章,很好,《彻底理解webService SOAP WSDL UDDI》原文:
http://wenku.baidu.com/view/f87b55f19e31433239689314.html

图片 1图片 2

原文: http://wenku.baidu.com/view/f87b55f19e31433239689314.html

WebServices简介
先给出一个概念 SOA ,即Service Oriented Architecture ,中文一般理解为面向服务的架构,
既然说是一种架构的话,所以一般认为 SOA 是包含了运行环境,编程模型,
架构风格和相关方法论等在内的一整套新的分布式软件系统构造方法和环境,
涵盖服务的整个生命周期。而在 SOA 的架构风格中,服务是最核心的抽象手段。
SOA 中的服务是构建在一些列基于开放标准的基础之上的,
Web 服务定义了如何在异构系统之间实现通信的标准化方法,
从而就使得 Web 服务可以跨越运行平台和实现语言,
同时也使得 Web 服务成为了实现 SOA 中服务的主要技术。
至于SOA 的话,太高深的技术,这里不予讨论(嘿嘿),本篇博文只介绍 WebServices 这项技术。
引子
有没有一种办法可以实现跨应用程序进行通信和跨平台进行通信呢?
换句话说,就是有什么办法可以实现我的应用程序 A 可以和应用程序 B 进行通信呢?
或者说是,我用 Java 写的应用程序和用 . Net 开发的应用程序之间进行通信呢?
很多时候,上面提到的这些,我们是必须要使用的,比如,一个跨应用程序吧,
拿腾讯 QQ 来说吧,我估计每一个人都用过腾讯 QQ 上面的天气预报工具吧 ! ! !
上面的这个天气预报功能是如何实现的呢?
有一种办法,那就是腾讯公司放个卫星上天,并且在公司中成立一个气象部门,天天关注于天气,
然后每时每刻更新腾讯 QQ 上的这个天气预报信息,
确实,这种办法确实行得通,不过,要真这样做的话,估计马化腾也该被踢出去了(哪有这么蠢啊?),
那么上面这个是如何实现的呢?别急,且听我慢慢道来~~~
然后,我们再来看看跨平台这个东东又是什么呢?
这里主要是拿 . Net 平台和Java 平台来说明例子,
假若,有两个公司,每个公司呢都有自己的一个项目,一个公司呢使用 . Net 开发,一个呢,使用 Java 开发,
恩,本来呢,这两个是相互独立的,进水不犯河水,但是有一天,突然,这两个公司给合并了,
合并后的老总发现,如果把两个项目结合起来将会大大的赚一笔,为此,如何做?
因为要把两个项目结合在一起,那么这两个项目之间总应该通通信吧 !!!
可这两个项目又是基于不同的平台,怎么通信呢?麻烦了吧 !!!
而后再看一种情况,就是比如一个公司使用的服务器是 Windows Server 2008,
那么它如何和 IT 供应商的UNIX 或者是 Linux 服务器进行连接呢?也很复杂吧 !!!
WebServices特点介绍
WebServices 提供一个建立分布式应用的平台,使得运行在不同操作系统和不同设备上的软件,或者是用不同的程序语言和不同厂商的软件开发工具开发的软件,所有可能的已开发和部署的软件,能够利用这一平台实现分布式计算的目的。WebServices的思想是:使得应用程序也具有 Web 分布式编程模型的松散耦合性。
WebServices的特点:
(1),WebServices 是自包含的。即在客户端不需要附加任何软件,只要客户机支持 HTTP 和XML 就 OK 了。
(2),WebServices 是自我描述的。在客户端和服务端都不需要知道除了请求和响应消息的格式和内容外的任何事。
(3),WebServices 是跨平台和跨语言的。客户端和服务端均可以在不同的平台和语言环境中实现,同时,不必为了支持 WebServices 而更改现有的代码。
(4),WebServices 是基于开放和标准的。XML 和 HTTP 是WebServices 的主要技术基础,而 XML 和HTTP 早就成了业内标准了。
(5),WebServices 是动态的。
(6),WebServices 是可以组合的。也就是通过一个 WebService 访问另外一个 WebService 来达到组合的目的。通过组合 WebServices 便可以将简单的 WebServices 聚合成为实现更多复杂功能的复杂的服务。
(7),WebServices 是松散耦合的。它完全解耦了客户端和服务端。
(8),WebServices 提供编程访问的能力。换句话说,就是可以通过编写程序来访问Web 服务。
(9),WebServices 是基于经过考验的成熟技术上构建的。比如 XML 和 HTTP。
(10),WebServices 提供打包现有应用程序的能力。
(11),WebServices 通过网络进行发布,查找和使用。
上面这些特点呢,现在不清楚的话,也不用紧,等下还会有详细的说明的 !!!
WebServices到底是什么?
如果简单的说的话,WebServices就是一组函数库,不过这和我们平时概念中的函数库却又有所不同,我们平时所使用的函数库要么是自己写的(在自己的应用程序当中写一组函数库),
要么是调用底层的 API(操作系统 API 如Win32 API),上面的这两种情况有一个共同点,
那就是函数库是位于客户端本地的,比如,您调用 Win32 API的话,就是调用本地操作系统上的函数库,而这里提到 Web 服务也是一组函数库这个概念和上面提到的函数库这个概念的区别就在此处,因为 Web服务看做一组函数库的话,那么这组函数库不是位于本地的,而是位于远程机器上(当然也可以是本地机器中)。
何为 Web 服务?
也就是网络服务,那就是把网络上不知道那个地方的一些函数看做是一组服务,然后我再通过网络就可以使用这些服务。
关于什么是 Web 服务,上面的说法那是山寨版的,稍微正经一点的说法是:
Web 服务是一种部署在 Web 上的对象或者是应用程序组件。
Why WebServices?
为什么需要使用 WebServices呢?这必须根据 WebServices 的特点以及其优势进行分析了。
首先,上面呢,也说了,Web服务的话,就是一组网络上的应用程序组件,
这样的话,您便可以通过在您的应用程序中使用 Web 服务来将您的应用程序提升到服务层面上来。
既然可以看做是一组服务了的话,那么当然就是可以提供给别个(别的应用程序)使用咯。
比如,我可以通过 Web 服务来公开一些接口给别个使用,至于这些要不要收费呢?那就看我心情了,前面举了腾讯 QQ 上查询天气的例子,这个例子呢,就可以在这里来做一个解释了,
在中国,应该只有一个卫星来进行天气预报的吧?腾讯也不可能为了天气预报而专门放个卫星上天吧?
可是腾讯 QQ 又确实是可以查询天气的,这里,便可以通过 Web 服务来解决。
首先,中国气象局应该是有一个卫星的,气象局根据卫星所返回的结果实时发布全国各地的天气状况,并且将这些天气信息以 Web 服务的形式公开,然后呢,腾讯 QQ 便可以通过这个 Web 服务来访问到天气状况了,再将这些天气状况反馈到 QQ 上就 OK 了。
然后,上面提到了 Web服务是应用程序组件,既然是组件,那么就可以对这个组件重复的进行使用了,
同时可以通过 Web 服务来实现将这个应用程序组件作为一个服务来进行使用,
更为强大的是,可以将多个 WebServices组合成为更为强大的 WebServices ,
并且是通过互联网哦!!!
这也是一大优点啊,
然后呢,最基本的 WebServices是基于 XML 和 HTTP 的
(当然这是最基本的 WebServices ,比如 WebServices 还可以通过 HTTPS 或者是 SMTP 来实现通信),
这又有什么好处呢?很明显,XML 和HTTP 这些都已经是标准了,
不论你是 Java 平台呢,还是 . Net 平台开发出来的(或者是是使用 Web 服务),既然我是使用 XML 和 HTTP 的话,我才懒得鸟你什么 Java 还是 . Net 呢,我也不管你是 Linux 还是 Windows ,这一切都和我 Web 服务无关,
我关注的只是通过 HTTP 协议来传输 XML 就 OK 了,
至于这些 XML 是如何被服务提供者开发出来的或者这些 XML 是如何被服务请求者使用的,这些都和我无关,这里便可以看出 Web 服务的另一个优势了,那就是跨语言跨平台(实现协同工作),所以可以通过 Web 服务来实现不同应用程序和不同平台之间的通信。
Web 服务允许独立于实现服务基于的硬件或者是软件平台和编写服务所用的编程语言使用服务,
根据上面这两点呢,
便可以解决掉最开始提出的使用 Java 开发的应用程序如何和使用 . Net 开发的应用程序之间进行通信这一问题,
同时,也可以解决 Linux 或者是UNIX 和 Windows Server 2008 之间进行连接这一问题了。
最后就是通过使用不同的 Web 服务,也不管 Web 服务是那种编程语言实现的,
我们都可以从不同的平台和操作系统进行访问,从而大大提高了不同应用程序共享数据和应用的能力。
并且 Web服务提供了构建 SOA 所必须得技术基础。
从上面可以看出通过 WebServices解决了我们曾经想都不敢想的问题,这么强大的东西为什么不加以好好利用呢?



WebServices体系结构
在Web 服务的体系结构中,涉及到三个角色,
一个是 Web 服务提供者,一个是 Web 服务中介者,还有一个就是 Web 服务请求者,
同时还涉及到三类动作,即发布,查找,绑定,
Web 服务提供者:
可以发布 Web 服务,并且对使用自身服务的请求进行响应,
Web 服务的拥有者,它会等待其他的服务或者是应用程序访问自己。
Web 服务请求者:
也就是 Web 服务功能的使用者,它通过服务注册中心也就是 Web 服务中介者查找到所需要的服务,
再利用 SOAP 消息向 Web 服务提供者发送请求以获得服务。
Web 服务中介者:
也称为服务代理,用来注册已经发布的 Web服务提供者,并对其进行分类,同时提供搜索服务,
简单来说的话,Web 服务中介者的作用就是把一个 Web 服务请求者和合适的 Web 服务提供者联系在一起,
充当一个管理者的角色,一般是通过 UDDI来实现。
发布:
通过发布操作,可以使 Web服务提供者向 Web 服务中介者注册自己的功能以及访问的接口。
发现(查找):
使得 Web 服务请求者可以通过 Web 服务中介者来查找到特点的种类的 Web 服务。
绑定:
这里就是实现让服务请求者能够使用服务提供者提供的服务了。



WebServices三种基本元素之 SOAP
SOAP 即 Simple Object AccessProtocol 也就是简单对象访问协议。
SOAP 呢,其指导理念是“唯一一个没有发明任何新技术的技术”,
是一种用于访问 Web 服务的协议。
因为 SOAP 基于XML 和 HTTP ,其通过XML 来实现消息描述,然后再通过 HTTP 实现消息传输。
SOAP 是用于在应用程序之间进行通信的一种通信协议。
因为是基于 XML 和HTTP 的,所以其独立于语言,独立于平台,并且因为 XML 的扩展性很好,
所以基于 XML 的 SOAP 自然扩展性也不差。
通过 SOAP 可以非常方便的解决互联网中消息互联互通的需求,
其和其他的 Web 服务协议构建起 SOA 应用的技术基础。
SOAP 协议的一个重要特点是它独立于底层传输机制,Web 服务应用程序可以根据需要选择自己的数据传输协议,
可以在发送消息时来确定相应传输机制。
由于 HTTP 协议本身的一些特点和局限性,
使得当 SOAP 使用HTTP 绑定的 Web 服务并不能满足某些企业应用的需求。
比如,HTTP 不是一个可靠传输协议,所以有可能在传输过程中出现问题,
然后 HTTP 协议基于Request/Response 模型,也就是说客户端需要在等待响应消息接收完成后才能继续执行,
而此时如果响应时间过长呢?
基于上面的这些需求,便需要选择合适的传输协议了。
关于这方面的内容的话,有点深奥了,有兴趣的可以去看看 IBM 的一些关于这方面内容的介绍。
还有一点需要提及一下,那就是 SOAP 是可以绕过防火墙的,将来将会作为 W3C 的标准进行发展。



WebServices三种基本元素之 WSDL
WSDL 即Web Services Description Language也就是 Web 服务描述语言。
是基于 XML的用于描述 Web 服务以及如何访问 Web 服务的语言。
服务提供者通过服务描述将所有用于访问 Web服务的规范传送给服务请求者,
要实现 Web服务体系结构的松散耦合,服务描述是一个关键,
不管是请求者还是服务提供者,通过服务描述便可以不必了解对方的底层平台,编程语言等,
服务描述与底层的 SOAP 基础结构相结合,
足以封装服务请求者的应用程序和服务提供者的 Web服务之间的这个细节。
WSDL 描述了 Web服务的三个基本属性:
(1)服务所提供的操作
(2)如何访问服务
(3)服务位于何处(通过 URL 来确定就 OK 了)



WebServices三种基本元素之 UDDI
UDDI 即 Universal Description,Discovery and Integration,也就是通用的描述,发现以及整合。
WSDL 呢,用来描述了访问特定的 Web 服务的一些相关的信息,可以在互联网上,
或者是在企业的不同部门之间,如何来发现我们所需要的 Web 服务呢?
而 Web 服务提供商又如何将自己开发的 Web 服务公布到因特网上,
这就需要使用到 UDDI 了,UDDI的话,是一个跨产业,跨平台的开放性架构,
可以帮助 Web 服务提供商在互联网上发布 Web 服务的信息。
UDDI 呢是一种目录服务,企业可以通过 UDDI 来注册和搜索 Web 服务。
简单来时候话,UDDI 就是一个目录,只不过在这个目录中存放的是一些关于 Web 服务的信息而已。
并且 UDDI 通过SOAP 进行通讯,构建于 . Net 之上。


开发 Web服务的方式
(1)开发阶段:
        实现一个 Web 服务,使这个 Web 服务能响应和接收 SOAP 消息,
      (这个呢,其实可以通过 Visual Studio 来帮助实现),
       定义好逻辑模块(这个 Web 服务总要干点事情吧),
       然后再撰写 WSDL 文件(这个呢,开发工具会自动生成的,不需要人工撰写了)
(2)部署阶段:
       指定 Web 服务的传输协议,将 Web 服务注册到相应服务描述部署文件(这些也是可以由工具来自动完成的)
(3)发布阶段:
       将 Web 服务的接口和调用的地址公开给客户端调用,
       常用的发布方式为基于 Web 提供的WSDL 的链接,当然 UDDI 也是一个选择。



总结一下 WebServices的优点
其实呢,前面介绍的都是关于 WebServices 的优点,在这里就只是浅要的总结一下了。
首先,WebServices 是基于 Internet 和异构平台的应用,
这样便可以方便的实现通过网络来通信,同时可以实现在不同的平台之间共享数据。
然后就是,WebServices 是基于 XML 和HTTP 的,
也就是基于标准和开放的,基于 XML 的话,扩展性自然好,自然跨语言。
基于 HTTP 的话,自然跨平台了。
最后,再回忆一下 WebServices 是一种应用程序组件吧,这样便可以将 WebServices 重复使用了。
      谈谈 WebServices 的缺点
首先就是由于 XML 文件的难以解析,所以在使用 Web 服务的时候,会消耗较多的 CPU 和内存资源,
而后,SOAP 是基于XML 的,所以在网络传输中传输的是 XML 文件,
但是由 XML 文件相比于二进制文件来说,要大很多,自然就会消耗更多的网络资源了。
而后,由于通过 WSDL 解耦了Web 服务提供者和请求者,且 SOAP 消息时从发送者向接收者单向传送的,
这在一定程度上造成了 WebServices 是一种无状态服务,
尽管现在在 . Net 中可以通过 Session 来实现在客户端和服务端共享一些数据,
但是单单依靠 Session 来实现客户端和服务端的状态交互也太牵强了吧
WebServices 在数据绑定上也存在一些缺陷,
因为所有的数据在传输中都是使用的 XML 来实现的,
因此,需要在二进制数据和 XML 之间进行一个转换(通过序列化和反序列化来实现),
而在转换过程中有可能出现语义丢失的情况。
最后就是 WebServices 的技术要求相对比较高,
因为涉及到底层的 HTTP 协议以及SOAP ,WSDL 和UDDL 这三大平台元素,
所以学习的曲线也是比较长的,
当然,在 . Net 中可以通过 Visual Studio 非常快速和简单的开发和访问一个 Web 服务,
但这只限于在简单的使用上,而对于本质的东西,是比较难的。
后续
正如题目所言,是 WebServices 简介,既然是简介的话,那么自然就是以简为目标了,
说明一下的是,上面的这篇博文呢,源自前几天做的一个关于 WebServices 的演讲,
演讲的 PPT 还存有,有兴趣要的可以留个邮箱的。



. Net中 WebServices 的实战
下面呢,就来具体看看在 . Net中如何开发一个WebServices 以及如何使用这个 WebServices
开发环境:
Windows 7 下IIS 7.5
Visual Studio TeamSystem 2008
Sql Server 2008
首先来看看如何开发一个 WebServices
先建立一个 ASP.NET 应用程序项目,然后再在项目中添加一个 WebServices 服务,
然后就是在这个 WebServiceTest中编写业务逻辑了,
本次实例的业务逻辑呢就是从数据库“图书馆管理系统”中取出所有的读者的信息。
WebServiceTest.asmx中的代码如下
usingSystem.Web.Services;
usingSystem.Data; 
usingSystem.Data.SqlClient; 
usingSystem.Web.Configuration;
namespace WebServiceDemo
{ 
    [WebService(Namespace = "http://tempuri.org/")]
   [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    public classWebServiceTest :System.Web.Services.WebService
    { 
        [WebMethod] 
       public DataSet GetAllReader()
        { 
            DataSet ds = newDataSet();
           string connStr =
               WebConfigurationManager.ConnectionStrings["DBConnString"].ConnectionString;
            string sqlStr = "SELECT[ReaderID],[ReaderIDType],[ReaderName]," +
                                  "[ReaderSex],[ReaderBirth]" + 
                           "FROM [图书馆管理系统].[dbo].[Reader]";
            using (SqlConnection sqlCon = new SqlConnection(connStr))
            { 
               using (SqlCommand sqlCom =sqlCon.CreateCommand())
               { 
                   sqlCom.CommandType = CommandType.Text; 
                   sqlCom.CommandText = sqlStr; 
                  using (SqlDataAdapter sqlDa = newSqlDataAdapter(sqlCom))
                   { 
                       sqlDa.Fill(ds); 
                   } 
               } 
            } 
            return ds;
        } 
    } 
}
然后我再在这一个项目 WebServiceDemo中添加一个页面 Test . aspx
来实现访问自身应用程序中的 WebServices
(Test. aspx和 WebServiceTest . asmx 位于同一应用程序中)。
这个 Test . aspx 呢非常简单,仅仅在上面放了一个 GridView ,然后稍微写点 Code-Behind 就 OK 了,
其代码如下:
using System;
namespace WebServiceDemo
{ 
    public partial class Test : System.Web.UI.Page 
    { 
        protected void Page_Load(objectsender, EventArgs e) 
        { 
            if(!IsPostBack) 
            { 
               WebServiceTesttest= new WebServiceTest();
               GridView1.DataSource = test.GetAllReader();
               GridView1.DataBind(); 
            } 
        } 
    } 
}
再来浏览一下 Test . aspx页面
可以看出已经达到了预定的效果,也就是从数据库中通过 WebServices取出了数据。
而从上面的代码可以看出,仅仅是将 WebServices看做是一个类了,
将其作为一个类来进行使用(实质上也就是一个类而已)。
下面我们还需要看一种情况,
那就是,实现在另外的一个应用程序中访问上面建立的 WebServices。
其实这种情况呢,就是和访问网络上的 WebServices 一样了,
比如腾讯 QQ 就是使用这种方式来实现的,
为了模拟这种实现,我首先将上面建立的这个 ASP.NET 应用程序 部署到 IIS 上面,
且指定了一个端口为 81

然后我再建立一个项目 TestWebServices
并且在这个项目里面也添加一个页面 Test . aspx
在Test . aspx 上也只放一个 GridView 控件。
然后就要给这个项目添加 Web服务的引用了(右键TestWebService 点击“添加 Web 引用”)
如果您要访问的是互联网上的 Web服务,比如查询天气,
那么就需要在上面的 URL中写入 Web 服务所在的地址,然后“前往”就 OK 了,
由于本次的演示,我只是把我的 Web服务放在了本地的 IIS 上面,
所以在此处呢选择“本地计算机上的 Web服务”,
从上面的截图中就可以看出,在 81号端口上面我有一个 Web 服务,
就是前面的 Demo中建立的 Web 服务 WebServiceTest
然后我选择这个 Web 服务,单击它即可,
上面的这幅截图中便可以看出我在 Web 服务WebServiceTest 中公开的接口了,
由于我只在其中写了一个接口 GetAllReader ,所以在这里便只显示了一个了。
在这一步中,您便可以添加这个 Web 引用了,不过要注意的是,
如果在这一步添加 Web 引用的话,那么这个 Web 服务中所有被公开的方法都会被添加到您的项目中,
比如,如果我在上面的 Web 服务中还有一个 GetAllName 的方法的话,
那么在这一步添加 Web 引用的话,就会将 GetAllReader 和 GetAllName 全部添加到您的项目当中,
但是有时候,这样会太浪费了,因为我可能根本就不需要使用 GetAllName 而只需要 GetAllReader,
此时,可以单击上面的 GetAllReader 进入下一步,
在这一步中添加 Web 引用的话,那么就只会在项目中添加 GetAllReader 这个方法的引用了,
我们在这里使用这种方法来添加 GetAllReader 的引用。
单击“添加引用”,此时可以看到在项目中生成一些文件,
(这里呢,其实就是代理模式来实现了)
既然在项目中引用了这个 Web服务了,
那么下一步就是在 Test . aspx中使用这个 Web 服务了。
看看 Test . aspx的 Code-Behind 吧
using System;
namespace TestWebService
{ 
    public partial class Test : System.Web.UI.Page 
    { 
        protected void Page_Load(objectsender, EventArgs e) 
        { 
            if(!IsPostBack) 
            { 
                //WebServiceTest.GetAllReader 这一段是我引用后的服务名 
               WebServiceTest.GetAllReader.WebServiceTesttest =
                   new WebServiceTest.GetAllReader.WebServiceTest();
               GridView1.DataSource = test.GetAllReader();
               GridView1.DataBind(); 
            } 
        } 
    } 
}
下面就来看一看效果了
从上面的效果便可以看出,我们已经成功在另外的应用程序中访问了 Web 服务,
也可以得出 Web 服务实现了在不同应用程序之间数据的共享。
如果,读者对通过网络 URL 来访问WebServices 有疑问的话,
可以参考一下笔者的另外一篇稍微带有 WebServices 性质的博文,
在其中实现了一个访问互联网上提供的天气查询 Web 服务。
http://www.cnblogs.com/QinBaoBei/archive/2010/03/30/1700898.html
上面呢,通过几个 Demo 对WebServices 在 .Net 中的实战进行了一个简单的应用了。
在这里一切似乎都和前面所提到的 SOAP ,WSDL,UDDI 均扯不上关系,
其实不然,只不过这些全部都由别个(工具)给你完成了,而你只是简单的开发一下逻辑就 OK 了,
不过呢,简单归简单,理解前面的一些原理还是很有必要的。
这里还点一下,就是上面呢,我通过 WebServices 来查询数据库并且返回一个 DataSet,
不过,大家有没有想过,如果这个 DataSet 中的数据量有很大呢,比如 100万条,甚至更多,
这样的话,在网络传输中那不慢的要死啊,其实有一种比较好的解决办法,那就是压缩,
关于压缩呢,又有好几种方法,比如 GZIP 可以将其压缩到原来的一半以上,
这几种方法各有所长,一个晚上又过咯,很晚啦,以后有时间的话,可能会写一篇这方面的博文吧 !!!
关于WebServices 的简介就到这里了 !!!

View Code

 

三、实例区分RESTful风格的 Web API与采用RPC风格的SOAP WebService?

  

由于REST是面向资源的,所以一个Web
API旨在实现针对单一资源的操作。我们在前面已经说个,针对资源的基本操作唯CRUD而已,这是使我们可以为Web
API定义标准接口成可能。所谓的标准接口就是针对不同资源的Web
API定义一致性的操作来操作它们,其接口可以采用类似于下面的模式。

1: public class ResourceService
   2: {
   3:     public IEnumerable<Resource>[] Get();
   4:     public void Create(Resource resource);
   5:     public void Update(Resource resource);
   6:     public void Delete(string id);
   7: }

 

能否采用统一接口是RESTful Web API和采用RPC风格的SOAP
Web服务又一区别。如果采用RPC风格的话,我们在设计Web
API的时候首先考虑的是具体哪些功能需要被提供,所以这样的Web
API是一组相关功能的集合而已。

以一个具体的场景为例。现在我们需要设计一个Web
API来管理用于授权的角色,它只需要提供针对角色本身的CRUD的功能以及建立/解除与用户名之间的映射关系。如果我们将其定义成针对SOAP的Web服务,其服务接口具有类似于如下的结构。

public class RoleService
 {
     public IEnumerable<string> GetAllRoles();
     public void CreateRole(string roleName);
     public void DeleteRole(string roleName);

     public void AddRolesInUser(string userName, string[] roleNames);
     public void RemoveRolesFromUser(string userName, string[] roleNames);
}

 

如下我们需要将其定义成一个纯粹的RESTful的Web
API,只有前面三个方法在针对角色的CRUD操作范畴之内,但是后面两个方法却可以视为针对“角色委派(Role
Assignment)”对象的添加和删除操作。所以这里实际上涉及到了两种资源,即角色和角色委派。为了使Web
API具有统一的接口,我们需要定义如下两个Web API。

 public class RolesService
 {
    public IEnumerable<string> Get();
     public void Create(string roleName);
    public void Delete(string roleName);
}

 public class RoleAssignmentsService
{
     public void Create(RoleAssignment roleName);
     public void Delete(RoleAssignment roleName);
}

 

 

对照SOAP方式的Web服务,REST中没有用于描述资源(服务)列表,资源元数据的类似于WSDL的东东。所以有人在2009年提出了一个标准WADL去描述REST方式的Web服务,但至今没有被标准化。个人认为使用WSDL/WADL去描述REST方式的Web服务太别扭,这是典型的RPC思路,而REST是一种把服务抽象为资源的架构思想。用描述RPC的WSDL去描述REST方式的Web服务并不合适。我们需要其他策略去代替WSDL实现“公布访问资源方法的问题”。

由于没有类似于SOAP的权威性协议作为规范,因此各个网站的REST实现都自有一套,也正是因为这种各自实现的情况,在性能和可用性上会大大高于SOAP发布的web
service,但细节方面有太多没有约束的地方,其统一通用方面远远不及SOAP。

举个例子:假设A组织,B组织都实现了Restful
API来通过工号查询人员信息,因为没有统一的规范。

A的API 可能是这样:http://A/api/person/001

B的API 可能是这样:http://A/api/person/id=001

第三方客户端在实现远程调用的时候就必须考虑这些API的差异,分别查看A,B的API文档。

如果有个权威性协议作为规范做指导,规定这个API应该实现成下面这样,那么第三方客户端也只需按照这个标准去调用远程API,而不用查看A,B的API文档:

http://A/api/person/{001}

解释了这么多,就是为了引出:OData是这样的一个设计和使用Restful
API 的权威性协议.
OData定义了一些标准规则(像一个接口定义一堆方法一样),实现Restful
API时候,必须实现这些标准规则(就像实现一个接口必须实现其所有方法一样)。第三方就可以根据Odata协议定义的规则去访问Restful
API。

 

Where
–什么样的场景下可以考虑使用OData?

并不是说你创建的所有RESTful API都需要符合OData协议。只有在需要Open
Data(开放数据给其他组织)时候,才有必要按照OData协议设计RESTful
API。这里的Open
Data是指开放数据给第三方使用,并且你并不知道谁是第三方。比如博客园的RSS,谁订阅了RSS,博客园是不清楚的。如果你的数据只被你自家公司的客户端使用,
OData就是一个可选项,你完全有理由不按照OData规范去设计RESTful API。

 

How – 如何使用OData?

首先看一下C#客户端调用符合OData标准的WebApi是多么的方便(官网

第一步,通过Nuget安装OData Client for .Net包。

图片 3

 

第二步,安装VS插件:OData v4 Client Code Generator。

图片 4

 

第三步:假设存在一个可用的WebApi(后面介绍如何创建) –
.
我们修改代码模板中的MetadataDocumentUri如下,
然后保存。T4会访问

图片 5

 

第四步:在我们的代码中就可以操作CLR对象来消费远程的webAPI了。体验到Odata标准的力量了吧。

图片 6

 

接下来看一下C#服务端如何实现上面客户端需要调用的OData的WebAPI,有两种方式,有点细微的差别。

第一步:创建一个空的WebApi项目。

第二步: 通过Nuget引入EF6 和 WebApi 2.2 for OData v4.0. 如下图。

图片 7

 

第三步:创建Entity和DbContext类,以及配置数据库连接。并通过enable
migration完成数据库的创建,可在Configuration的seed的方法中,添加一些初始化的数据。

图片 8

图片 9

图片 10

第四步:配置WebApiConfig如下

图片 11

第五步:创建ProductsController

图片 12图片 13

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using ODataAPI.Models;
using System.Web.OData;

namespace ODataAPI.Controllers
{
    /*
    To add a route for this controller, merge these statements into the Register method of the WebApiConfig class. Note that OData URLs are case sensitive.

    using System.Web.Http.OData.Builder;
    using ODataAPI.Models;
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<Product>("Products");
    config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
    */
    public class ProductsController : ODataController
    {
        private ODataAPIContext db = new ODataAPIContext();

        // GET odata/Products
        //[Queryable]
        [EnableQuery]
        public IQueryable<Product> GetProducts()
        {
            return db.Products;
        }

        // GET odata/Products(5)
        //[Queryable]
        [EnableQuery]
        public SingleResult<Product> GetProduct([FromODataUri] int key)
        {
            return SingleResult.Create(db.Products.Where(product => product.ID == key));
        }

        // PUT odata/Products(5)
        public async Task<IHttpActionResult> Put([FromODataUri] int key, Product product)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (key != product.ID)
            {
                return BadRequest();
            }

            db.Entry(product).State = EntityState.Modified;

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Updated(product);
        }

        // POST odata/Products
        public async Task<IHttpActionResult> Post(Product product)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            db.Products.Add(product);
            await db.SaveChangesAsync();

            return Created(product);
        }

        // PATCH odata/Products(5)
        [AcceptVerbs("PATCH", "MERGE")]
        public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> patch)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            Product product = await db.Products.FindAsync(key);
            if (product == null)
            {
                return NotFound();
            }

            patch.Patch(product);

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Updated(product);
        }

        // DELETE odata/Products(5)
        public async Task<IHttpActionResult> Delete([FromODataUri] int key)
        {
            Product product = await db.Products.FindAsync(key);
            if (product == null)
            {
                return NotFound();
            }

            db.Products.Remove(product);
            await db.SaveChangesAsync();

            return StatusCode(HttpStatusCode.NoContent);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }

        private bool ProductExists(int key)
        {
            return db.Products.Count(e => e.ID == key) > 0;
        }
    }
}

View Code

第六步:F5运行,接着客户端就可以调用了。可以通过访问http://localhost:\#/OData/ 和 http://localhost:\#/OData/$metadata 看看resource
list 和元数据长什么样。

图片 14

 

另外,我们可以通过VS的OData
Controller模板来创建webAPIController(如下)。注意使用这种方式创建webAPIController时,不可以导入WebApi
2.2 for OData v4.0这个类库,否则会出现dll冲突。

图片 15

图片 16

 

相关文章