from:http://www.ibm.com/developerworks/cn/webservices/ws-restwsdl/
简介: Web 服务的核心是定义一种使用网络和 XML 来进行计算机到计算机交互的机制。Web 服务的一个重要组件是使用 Web 服务描述语言(Web Services Description Language,WSDL)的正式描述。一直以来没有一种正式的语言来描述 REpresentational State Transfer (REST) Web 服务,直到现在出现了 WSDL 2.0。本文向您介绍 REST 和 WSDL 2.0,并指导您创建 REST Web 服务的 WSDL 2.0 描述。
引言
Web 服务 这个术语通常与使用 SOAP 和 WS* 标准(例如 WS-Addressing 和 WS-Security)的基于操作的服务相关联。REST Web 服务 这个术语一般是指使用 HTTP 和 XML 的基于资源的 Web 服务体系结构。其中每种体系结构的 Web 服务风格各有其影响力,但是直到最近,WSDL 不再同等地支持这两种风格。WSDL 1.1 HTTP 绑定不足以描述使用 HTTP 和 XML 的通信,因此没有办法使用 WSDL 来正式地描述 REST Web 服务。WSDL 2.0 在设计时考虑到了 REST Web 服务,将 WSDL 2.0 作为 World Wide Web Consortium (W3C) 建议标准来发布意味着现在有了用于描述 REST Web 服务的语言。
REST
REST 是一种体系结构风格,它将 Web 服务视为以资源为中心的应用程序。实际上,这意味着 RESTful 应用程序中的每个 URL 表示一个资源。URL 还易于理解和记忆。例如,某个书店可能为其销售的图书列表定义 URL http://www.bookstore.com/books/,并对 ISBN 为 0321396855 的特定图书的相关详细信息定义了 URL http://www.bookstore.com/books/0321396855/。这与以操作为中心的应用程序形成鲜明对比,后者通常具有很长的加密 URL,用于描述要执行的特定操作,例如 http://www.bookstore.com/action/query?t=b&id=11117645532&qp=0321396855。查询参数用于筛选结果。使用同样的书店示例,指定主题参数可以将图书列表限制到关于某个特定主题的图书。例如,URL http://www.bookstore.com/books/?subject=computers/eclipse 将返回关于 Eclipse 平台的图书列表。
Roy Fielding 博士在他的博士论文中提出了术语 REST,他在其中提到了“作为应用程序状态引擎的超媒体”。这意味着资源预期将包含超链接。这些超链接是用以进行转换的方法,该转换或者改变资源状态,或者转移到另一个资源。虽然超链接在由人类使用的 (X)HTML 应用程序中司空见惯,但它们通常不出现在由计算机使用的 XML 中。与 (X)HTML 一样,REST Web 服务在 XML 中利用了超链接。
传统 Web 应用程序使用 HTTP GET 或 POST 操作来访问资源。相反,RESTful 应用程序全面使用了 HTTP 谓词(POST、GET、PUT 和 DELETE),按照创建、读取、更新和删除 (CRUD) 模式来访问资源。
REST 应用程序存在一个更重要的组件:RESTful 应用程序应该是无状态的。这意味着在 REST 应用程序中,服务器上没有存储任何会话状态。满足请求所需要的所有信息都携带在请求消息本身之中。因此在服务显式地允许的情况下,客户端可以缓存资源的表示形式,从而显著改进应用程序的性能。要了解有关 REST 的更多信息,请参阅本文结尾的参考资料部分。
WSDL 和 REST
WSDL 描述包含 Web 服务的所有详细信息,包括:
服务的 URL。
服务所理解的通信机制。
服务所能执行的操作。
服务消息的结构。
客户端可以使用这些详细信息来与服务进行交互。
毫无疑问,REST Web 服务到目前为止还没有利用 WSDL 1.1 HTTP 绑定的一个重要原因是由于 WSDL 1.1 HTTP 绑定不足以描述 REST Web 服务。
WSDL 2.0 于 2007 年 6 月被宣布为 W3C 建议标准。创建 WSDL 的这第二个版本是为了解决 WSDL 1.1 存在的问题,Web 服务互操作性(Web Services Interoperability,WS-I)组织已确定了其中的许多问题。此外,WSDL 2.0 还具有对 HTTP 绑定的良好支持。
WSDL 是一种正式地描述 Web 服务的 XML 语言。可以将 Web 服务的 WSDL 描述看作是其与客户端之间签署的的 API 契约。WSDL 描述指定 Web 服务的地址、允许的通信机制、接口和消息类型。简而言之,WSDL 描述为客户端提供了使用 Web 服务所需要的所有信息。
WSDL 的可用性不只是用作 API 契约。作为正式的定义,WSDL 可由 Web 服务工具用于执行操作,例如:
采用各种语言生成客户端和服务存根。
发布 Web 服务。
动态测试 Web 服务。
绝大多数 Web 服务工具都包括对 WSDL 1.1 的支持,并且对 WSDL 2.0 的支持也正在增加。Apache Web 服务项目包含两个目前支持 WSDL 2.0 的子项目。Woden 是一个基于 Java? 的 WSDL 2.0 验证分析器。该项目还包含 XSL Transformation (XSLT) WSDL 2.0 优质打印机,用于提供更加可读的 WSDL 文档形式。同样来自于 Apache 的 Axis2 是一个流行的 Web 服务运行时引擎,能够在 WSDL 2.0 文档的基础上生成 Java 客户端和服务器存根。
使用 WSDL 2.0 描述 REST Web 服务
本文的其余部分将指导您完成所需的步骤,以使用下面的简单示例场景来为一个 REST Web 服务创建 WSDL 2.0 描述。
您将运作一家具有创造性 URL 的书店:http://www.bookstore.com。您先前已经创建了两个 REST Web 服务:
图书列表服务 检索您的书店中销售的图书的列表。
图书详细信息服务检索有关某一本特定图书的详细信息。
信息在 XML 文档中返回。请考虑一下有关那些服务的详细信息:
图书列表服务的 URL 为 http://www.bookstore.com/books/。因为您是一家历史悠久的图书零售商,所以您销售的图书的列表相当大。因此您提供了以下查询参数列表,客户可以使用这些参数来筛选结果:
Author
Language
Publisher
Subject
Title
例如,URL http://www.bookstore.com/books/?subject=computers/eclipse 返回有关 Eclipse 的计算机图书的列表,如清单 1 所示。
清单 1. 来自图书列表服务的响应
<booklist:bookList xmlns:booklist="http://www.bookstore.org/booklist/xsd" xmlns:book="http://www.bookstore.org/book/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bookstore.org/booklist/xsd booklist.xsd http://www.bookstore.org/book/xsd book.xsd"> <booklist:book url="http://www.bookstore.com/books/0321442598" title="BIRT: A Field Guide to Reporting"/> <booklist:book url="http://www.bookstore.com/books/0321205758" title="Contributing to Eclipse: Principles, Patterns, and Plug-Ins"/> <booklist:book url="http://www.bookstore.com/books/0321245873" title="Eclipse AspectJ: Aspect-Oriented Programming with AspectJ and the..."/> <booklist:book url="http://www.bookstore.com/books/0321288157" title="Eclipse Distilled"/> <booklist:book url="http://www.bookstore.com/books/0131425420" title="Eclipse Modeling Framework"/> <booklist:book url="http://www.bookstore.com/books/0321334612" title="Eclipse Rich Client Platform: Designing, Coding, and Packaging Java..."/> <booklist:book url="http://www.bookstore.com/books/0321396855" title="Eclipse Web Tools Platform: Developing Java Web Applications"/> <booklist:book url="http://www.bookstore.com/books/032142672X" title="Eclipse: Building Commercial-Quality Plug-Ins (2nd Edition)"/> <booklist:book url="http://www.bookstore.com/books/0321443853" title="Integrating and Extending BIRT"/> <booklist:book url="http://www.bookstore.com/books/0321268385" title="Official Eclipse 3.0 FAQs"/> <booklist:book url="http://www.bookstore.com/books/0321256638" title="Official Eclipse 3.0 FAQs"/> </booklist:bookList>
图书详细信息服务的 URL 为 http://www.bookstore.com/books/ISBN_NUMBER,其中 ISBN_NUMBER 应该替换为特定图书的 ISBN。例如,URL http://www.bookstore.com/books/0321396855 将返回我的图书 Eclipse Web Tools Platform 的详细信息,如清单 2 所示(请参阅参考资料以获得指向该图书网站的链接)。
清单 2. 来自图书详细信息服务的响应
<book:book xmlns:book="http://www.bookstore.org/book/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bookstore.org/book/xsd book.xsd"> <book:title>Eclipse Web Tools Platform: Developing Java Web Applications</book:title> <book:author> <book:firstName>Naci</book:firstName> <book:lastName>Dai</book:lastName> </book:author> <book:author> <book:firstName>Lawrence</book:firstName> <book:lastName>Mandel</book:lastName> </book:author> <book:author> <book:firstName>Arthur</book:firstName> <book:lastName>Ryman</book:lastName> </book:author> <book:overview> Discover Eclipse Web Tools Platform (WTP), the new end-to-end toolset for Java-based Web development. The WTP seamlessly integrates all of the tools today's Java Web developer needs. Eclipse WTP is both an unprecedented open source resource for working developers and a powerful foundation for state-of-the-art commercial products. This book offers in-depth descriptions of every tool included in WTP, introducing powerful capabilities never before available in Eclipse. The authors cover the entire Web-development process -- from defining Web application architectures and development processes through testing and beyond. And if you're seeking to extend WTP, this book provides an introduction to the platform's rich APIs. The book also </book:overview> <book:pages>752</book:pages> <book:publisher>Addison-Wesley Professional</book:publisher> <book:language>English</book:language> <book:isbn-10>0321396855</book:isbn-10> <book:isbn-13>978-0321396853</book:isbn-13> <book:price>54.99</book:price> </book:book>
在下面的几个部分中,您将了解如何使用上面列出的详细信息来创建图书列表服务的 WSDL 2.0 描述。图书详细信息服务虽然在该场景中有用,但其 WSDL 描述并没有结构上的不同,因此本文将不介绍该服务。然而,本文结尾的下载部分提供了图书列表和图书详细信息服务的 WSDL 2.0 描述以及清单 1 和清单 2 中的相同文档。
在开始图书列表服务的 WSDL 描述之前,让我们首先看一下 WSDL 2.0 基础。
WSDL 2.0 基础
WSDL 2.0 是核心命名空间为 http://www.w3.org/ns/wsdl 的 XML 语言。WSDL 2.0 文档的根元素是 description 元素。description 具有四个子元素,它们共同封装了关于某个 Web 服务的所有详细信息。
types
interface
binding
service
WSDL 2.0 文档的框架如清单 3 所示。
清单 3. WSDL 2.0 文档框架
<wsdl:description xmlns:wsdl="http://www.w3.org/ns/wsdl"> <wsdl:types/> <wsdl:interface/> <wsdl:binding/> <wsdl:service/> </wsdl:description>
types 元素包含了描述 Web 服务消息的所有 XML 模式元素和类型定义。WSDL 2.0 对其他类型系统的使用保持开放,但实际上仅与 XML 模式一起使用。
interface 元素定义 Web 服务操作,包括特定的输入、输出和所传递的错误消息,以及传递那些错误消息的顺序。
binding 元素定义客户端如何与 Web 服务通信。在 REST Web 服务的情况下,一个绑定指定客户端可以使用 HTTP 进行通信。
service 元素将 Web 服务的地址与特定的接口和绑定相关联。
WSDL 2.0 定义了另外两个关于 REST Web 服务的相关命名空间:
HTTP 命名空间 http://www.w3.org/ns/wsdl/http,其中包括 HTTP 绑定元素
WSDL 扩展命名空间 http://www.w3.org/ns/wsdl-extensions,其中包括三个属性的定义:两个用于将 XML 文档中的超链接与 Web 服务描述相关联,第三个用于安全地描述 Web 服务操作
下面几个部分将更详细地介绍本部分中讨论的所有 WSDL 元素。
处理图书列表服务
提醒一下,图书列表服务的 URL 为 http://www.bookstore.com/books/。为了处理该服务,您将使用 WSDL service 元素,此元素至少需要一个 endpoint 子元素。endpoint 元素的 address 属性用于指定该服务的 URL,如清单 4 所示。endpoint 元素还用于将某个绑定与具有 binding 属性的服务关联起来。service 元素反过来又将某个接口与具有 interface 属性的服务关联起来。您将在以下几个部分中创建接口和绑定,因此现在可以将这些属性值保留空白。
图书列表服务的 WSDL 2.0 文档还需要一个目标命名空间,因此定义命名空间 [b]http://www.bookstore.org/booklist/wsdl。
清单 4. 图书列表服务定义
<wsdl:description xmlns:wsdl="http://www.w3.org/ns/wsdl" targetNamespace="http://www.bookstore.org/booklist/wsdl" xmlns:tns="http://www.bookstore.org/booklist/wsdl"> <wsdl:service name="BookList" interface=""> <wsdl:documentation> The bookstore's book list service. </wsdl:documentation> <wsdl:endpoint name="BookListHTTPEndpoint" binding="" address="http://www.bookstore.com/books/"> </wsdl:endpoint> </wsdl:service> </wsdl:description>
与图书列表服务进行 HTTP 通信
图书列表服务的绑定定义需要指定该服务使用 HTTP 来进行通信。为此,可以为 binding 元素的 type 属性指定值 http://www.w3.org/ns/wsdl/http。
该绑定还可以选择引用某个接口。将 interface 属性保留空白;您将在下一个部分中创建该属性。如果某个接口与该绑定相关联,binding 可以选择声明一个子 operation 元素,用于镜像接口的 operation 元素。您需要创建一个存根 operation 元素,并在创建接口以后填充此引用。
存在四个 HTTP 通信谓词:
GET
PUT
POST
DELETE
图书列表服务是读取请求,因此使用 HTTP GET 进行通信。使用 WSDL 2.0 HTTP 命名空间中的 HTTP method 属性,在 operation 元素上设置 GET 谓词。要使用此属性,您首先需要在 Description 元素上声明命名空间 http://www.w3.org/ns/wsdl/http。
清单 5 显示了图书列表服务的 HTTP 绑定声明。现在既然已经声明了绑定,您可以将 endpoint 元素的绑定引用更新为引用 tns:BookListHTTPBinding。
清单 5. 图书列表绑定定义
<wsdl:description xmlns:wsdl="http://www.w3.org/ns/wsdl" targetNamespace="http://www.bookstore.org/booklist/wsdl" xmlns:tns="http://www.bookstore.org/booklist/wsdl" xmlns:whttp="http://www.w3.org/ns/wsdl/http"> <wsdl:binding name="BookListHTTPBinding" type="http://www.w3.org/ns/wsdl/http" interface=""> <wsdl:documentation> The RESTful HTTP binding for the book list service. </wsdl:documentation> <wsdl:operation ref="" whttp:method="GET"/> </wsdl:binding> <wsdl:service name="BookList" interface=""> <wsdl:documentation> The bookstore's book list service. </wsdl:documentation> <wsdl:endpoint name="BookListHTTPEndpoint" binding="tns:BookListHTTPBinding" address="http://www.bookstore.com/books/"> </wsdl:endpoint> </wsdl:service> </wsdl:description>
定义图书列表服务操作
到目前为止,您已经了解如何处理图书列表 Web 服务并与之通信。接下来您将指定图书列表服务操作,该操作描述图书列表服务的功能。
interface 元素及其子 operation 元素用于定义服务的操作。在该图书列表服务的情况下,您将定义单个操作 getBookList,此操作使用图书列表来响应请求。
下一步,指定 operation 元素的三个属性:
pattern:用于指定该操作的消息交换模式(message exchange pattern,MEP)。MEP 定义操作中的消息顺序及其方向。在此例中,请指定值 http://www.w3.org/ns/wsdl/in-out,以指示该服务接收一个输入消息――图书列表请求――并发送一个输出消息――图书列表。为了支持此 MEP,请指定 operation 元素的 input 和 output 子元素。这些元素用于引用定义消息结构的 XML 模式元素,下一部分将创建那些消息结构。
style:用于指定有关某个操作的附加信息。请指定值 http://www.w3.org/ns/wsdl/style/iri,从而对 input 元素内容施加限制,例如要求它仅使用 XML 模式元素。
wsdlx:safe:在 WSDL 扩展命名空间中,此属性声明该操作是等幂的。这种类型的操作不修改资源,因此可以调用多次并获得相同的结果。为了利用此元素,可以在 description 元素上声明 WSDL 扩展命名空间 http://www.w3.org/ns/wsdl-extensions。
您可以在“WSDL 2.0 Part 2: Adjuncts”(请参阅参考资料以获得链接)中找到预定义的 MEP、样式和 safe 属性定义。
在清单 6 中查看图书列表服务的接口声明。既然现在已经声明了接口和操作,您可以更新 service 和 binding 元素的接口引用和绑定 operation 元素的接口操作引用。
清单 6. 图书列表接口定义
<wsdl:description xmlns:wsdl="http://www.w3.org/ns/wsdl" targetNamespace="http://www.bookstore.org/booklist/wsdl" xmlns:tns="http://www.bookstore.org/booklist/wsdl" xmlns:whttp="http://www.w3.org/ns/wsdl/http" xmlns:wsdlx="http://www.w3.org/ns/wsdl-extensions"> <wsdl:interface name="BookListInterface"> <wsdl:operation name="getBookList" pattern="http://www.w3.org/ns/wsdl/in-out" style="http://www.w3.org/ns/wsdl/style/iri" wsdlx:safe="true"> <wsdl:documentation> This operation returns a list of books. </wsdl:documentation> <wsdl:input element=""/> <wsdl:output element=""/> </wsdl:operation> </wsdl:interface> <wsdl:binding name="BookListHTTPBinding" type="http://www.w3.org/ns/wsdl/http" interface="tns:BookListInterface"> <wsdl:documentation> The RESTful HTTP binding for the book list service. </wsdl:documentation> <wsdl:operation ref="tns:getBookList" whttp:method="GET"/> </wsdl:binding> <wsdl:service name="BookList" interface="tns:BookListInterface"> <wsdl:documentation> The bookstore's book list service. </wsdl:documentation> <wsdl:endpoint name="BookListHTTPEndpoint" binding="tns:BookListHTTPBinding" address="http://www.bookstore.com/books/"> </wsdl:endpoint> </wsdl:service> </wsdl:description>
定义图书列表服务操作消息
图书列表 Web 服务具有两个消息:一个输入消息和一个输出消息。您需要描述特定的消息结构,以便客户端知道要向服务发送什么消息,以及预期服务会返回什么消息。
WSDL 2.0 支持使用多种类型系统来描述消息内容,但是目前仅使用了 XML 模式。本部分不介绍 XML 模式的详细信息。许多应用程序中都使用了 XML 模式,例如 WSDL 1.1,并且存在许多关于它的优秀文章。本部分重点介绍如何将 XML 模式用于图书列表 REST Web 服务,以及如何使用 WSDL 2.0 定义的附加属性来标注模式属性。
要为图书列表 REST Web 服务创建两个消息,您需要创建两个全局元素:
getBookList 表示输入消息。其中包含元素序列,包括服务上允许的每个查询参数,即作者、标题、出版商、主题和语言。getBookList 元素的内容仅限于元素,因为您为接口操作指定了 IRI 样式。
bookList 表示输出消息。其中包含 book 元素的序列。每个 book 元素包含 title 和 url 属性。title 属性应该是不言而喻的。url 属性是指向图书详细信息 REST Web 服务的链接,该服务返回特定图书的详细信息。
您的 url 属性定义包括两个来自 WSDL 扩展命名空间的属性。属性 wsdlx:interface 和 wsdlx:binding 标识该服务的特定 WSDL 2.0 接口和绑定。工具可以使用此语义信息来自动发现该服务。要利用这些属性,可以在 schema 元素上指定 WSDL 扩展命名空间。此外,还要在 WSDL 2.0 描述中包括图书详细信息服务的命名空间,以引用该服务的接口和绑定。
图书列表服务的 XML 模式如清单 7 所示。您可以在下载部分获取图书详细信息服务的描述。
清单 7. 图书列表服务的 XML 模式
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.bookstore.org/booklist/xsd" xmlns:tns="http://www.bookstore.org/booklist/xsd" xmlns:booksvc="http://www.bookstore.org/book/wsdl" xmlns:wsdlx="http://www.w3.org/ns/wsdl-extensions" elementFormDefault="qualified"> <element name="getBookList" type="tns:getBookListType"> <annotation> <documentation> The request element for the book list service. </documentation> </annotation> </element> <element name="bookList" type="tns:bookListType"> <annotation> <documentation> The response element for the book list service. </documentation> </annotation> </element> <complexType name="getBookListType"> <sequence> <element name="author" type="string" minOccurs="0" maxOccurs="unbounded"/> <element name="title" type="string" minOccurs="0" maxOccurs="1"/> <element name="publisher" type="string" minOccurs="0" maxOccurs="1"/> <element name="subject" type="string" minOccurs="0" maxOccurs="1"/> <element name="language" type="string" minOccurs="0" maxOccurs="unbounded"/> </sequence> </complexType> <complexType name="bookListType"> <sequence> <element name="book" type="tns:bookType" minOccurs="0" maxOccurs="unbounded"/> </sequence> </complexType> <complexType name="bookType"> <attribute name="title" type="string"/> <attribute name="url" type="anyURI" wsdlx:interface="booksvc:BookInterface" wsdlx:binding="booksvc:BookHTTPBinding"/> </complexType> </schema>
要引用 XML 模式中声明的输入和输出元素,您必须将该模式导入您的 WSDL 文档。要导入某个模式,您可以在 types 部分使用 import 元素,如清单 8 所示。您还需要在接口操作的 input 和 output 元素中添加对 getBookList 和 bookList 元素的引用,并将 XML 模式(图书列表模式)的命名空间声明添加到 description 元素。
清单 8 显示了图书列表 REST Web 服务的完整 WSDL 2.0 描述。可以在下载部分中获取此服务和图书详细信息 REST Web 服务的描述。
清单 8. 图书列表 REST Web 服务的 WSDL 2.0 描述
<wsdl:description xmlns:wsdl="http://www.w3.org/ns/wsdl" targetNamespace="http://www.bookstore.org/booklist/wsdl" xmlns:tns="http://www.bookstore.org/booklist/wsdl" xmlns:whttp="http://www.w3.org/ns/wsdl/http" xmlns:wsdlx="http://www.w3.org/ns/wsdl-extensions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msg="http://www.bookstore.org/booklist/xsd"> <wsdl:documentation> This is a WSDL 2.0 description of a sample bookstore service listing for obtaining book information. </wsdl:documentation> <wsdl:types> <xs:import namespace="http://www.bookstore.org/booklist/xsd" schemaLocation="booklist.xsd"/> </wsdl:types> <wsdl:interface name="BookListInterface"> <wsdl:operation name="getBookList" pattern="http://www.w3.org/ns/wsdl/in-out" style="http://www.w3.org/ns/wsdl/style/iri" wsdlx:safe="true"> <wsdl:documentation> This operation returns a list of books. </wsdl:documentation> <wsdl:input element="msg:getBookList"/> <wsdl:output element="msg:bookList"/> </wsdl:operation> </wsdl:interface> <wsdl:binding name="BookListHTTPBinding" type="http://www.w3.org/ns/wsdl/http" interface="tns:BookListInterface"> <wsdl:documentation> The RESTful HTTP binding for the book list service. </wsdl:documentation> <wsdl:operation ref="tns:getBookList" whttp:method="GET"/> </wsdl:binding> <wsdl:service name="BookList" interface="tns:BookListInterface"> <wsdl:documentation> The bookstore's book list service. </wsdl:documentation> <wsdl:endpoint name="BookListHTTPEndpoint" binding="tns:BookListHTTPBinding" address="http://www.bookstore.com/books/"> </wsdl:endpoint> </wsdl:service> </wsdl:description>
总结
在本文中,您了解了 REST 以及 WSDL 2.0 REST Web 服务如何使用 HTTP 和 XML 来进行通信。RESTful 应用程序以资源为中心而不是以操作为中心。以正式的方式描述 REST Web 服务的价值是使用该描述作为客户端与服务提供者之间的正式契约,并支持工具。WSDL 2.0 支持 REST Web 服务的描述。您逐步地完成了使用 WSDL 2.0 和 XML 模式来描述图书列表 REST Web 服务的过程。
但愿,您将使用这里介绍的步骤来创建图书列表服务的 WSDL 2.0 描述,从而为您自己的 REST Web 服务创建描述。