7

我的情况如下:

我有一个规范化的数据库,其中保存了有关机场的地理信息。结构是:

airport --is in--> city --is in--> country --is in--> continent

现在我想让用户管理这些数据,而不是让他们直接访问数据库。我们需要通过 Web 服务提供这个管理界面。

现在,在设计服务时,我们遇到了关于如何定义操作的讨论。我们提出了不同的解决方案:

方案A:具体操作

对于四个表(机场、城市、国家、大陆)中的每一个,我们定义了 3 个操作:

  • 插入
  • 得到
  • 更新

这将导致 2 个请求/响应对象 = 24 个对象的 12 个操作

要创建一个具有所有依赖项的全新机场,至少需要 4 个请求。

解决方案 B:通用

只有一种操作,通过参数控制。此操作能够创建管理数据库所需的一切。

该操作将决定需要做什么并执行它。如果发生错误,它将回滚所有内容。

==> 1 个操作 = 2 个高度复杂的请求/响应对象

解决方案 C:在中间见面 1

每个表一个通用操作,能够执行 get、insert、update,就像解决方案 B 一样,但每个表都集中在一个表上。

==> 4 个操作 = 8 个复杂的请求/响应对象

方案 D:在中间相遇 2

每个操作(get、insert、delete)一个通用操作,它可以在每个表上工作并解决依赖关系。

==> 3 个操作 = 6 个稍微复杂的请求/响应对象

例子

由于这是相当抽象的,因此为创建请求对象提供了一个简化示例(JFK/New York/USA/North America):

解决方案 A:

请求 1/4:

<insertContinent>North America</insertContinent>

请求 2/4:

<insertCountry continent="North America">USA</insertCountry>

请求 3/4:

<insertCity country="USA">New York</insertCity>

请求 4/4:

<insertAirport city="New York">JFK</insertAirport>

解决方案 B:

请求 1/1:

<action type="insertCountry" parent="North America">USA</action>
<action type="insertAirport" parent="New York">JFK</action>
<action type="insertContinent" parent="">North America</action>
<action type="insertCity" parent="USA">New York</action>

解决方案 C:

请求 1/4:

<countryAction type="insert" parent="North America">USA</countryAction>

请求 2/4:

<airportAction type="insert" parent="New York">JFK</airportAction>

请求 3/4:

<continentAction type="insert" parent="">North America</continentAction >

请求 4/4:

<cityAction type="insert" parent="USA">New York</cityAction >

解决方案 D: 请求 1/1:

<insert airport="JFK" city="New York" country="USA" continent="North America" />

解决方案 D 对我来说似乎相当优雅,因此我尝试将其放入 XSD:

代码:

<complexType name="NewContinent">
    <sequence>
        <element name="NAME" type="string"></element>
    </sequence>
</complexType>

<complexType name="NewCountry">
    <sequence>
        <element name="ISOCODE" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="newCONTINENT" type="tns:NewContinent"></element>
            <element name="CONTINENT" type="string"></element>
        </choice>
    </sequence>
</complexType>

<complexType name="NewCity">
    <sequence>
        <element name="IATA" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="COUNTRY" type="string"></element>
            <element name="newCOUNTRY" type="tns:NewCountry"></element>
        </choice>
    </sequence>

</complexType>

<complexType name="NewAirport">
    <sequence>
        <element name="IATA" type="string"></element>
        <element name="NAME" type="string"></element>
        <choice>
            <element name="CITY" type="string"></element>
            <element name="newCITY" type="tns:NewCity"></element>
        </choice>
    </sequence>

</complexType>

相应的请求将如下所示:

<complexType name="Request">
    <choice>
        <element name="AIRPORT" type="tns:NewAirport"></element>
        <element name="CITY" type="tns:NewCity"></element>
        <element name="COUNTRY" type="tns:NewCountry"></element>
        <element name="CONTINENT" type="tns:NewContinent"></element>
    </choice>
</complexType>

现在我的问题是:这真的是最好的解决方案吗?XSD 是否足以理解,这是怎么回事?

4

2 回答 2

5

大概您正在编写一个可以理解您的不同消息类型的协议层。您还需要一个应用层来解析消息的内容。您提到的不同方法将转移这两层之间的解析负担。例如:

解决方案 A:协议层完成所有解析并返回数据和命令。应用层只能使用数据。这也称为 RPC 模式。

优点:您可以验证您的消息。您可以将消息直接映射到应用程序调用。

缺点:如果您需要更改接口,则您的协议会更改。

解决方案 B:协议层返回两个值和一个命令。应用层必须使用命令将值解析为类型。

优点:协议永远不会改变。

缺点:您无法验证消息。您的应用程序代码更复杂。

解决方案 C:协议层返回两个已知类型和一个必须解析的命令。应用层可以只解析命令并使用数据。

优点:我想不出,似乎不是一个很好的折衷方案。

缺点:仅部分完成解析。

解决方案 D:协议层返回已知类型(您实现它的方式)和通用命令。应用层必须查看它接收到的数据并将通用命令转换为特定命令。这类似于 REST 架构。

优点:调用是不同的操作,因此您可以例如缓存获取响应。

缺点:应用层的复杂性

REST 模型的实现方式通常与您所概述的不同。它使用 HTTP GET、POST、PUT、DELETE 消息来传递任意文档。参数作为 URL 的一部分给出。例如:

<insert airport="JFK" city="New York" country="USA" continent="North America" />

变成

<insert URL="airport?city=Chicago">ORD</insert>

或者,如果您使用的是 HTTP,它会成为对机场 URL 的 POST 请求,其中包含城市的参数,内容是有关机场的信息。请注意,在您拥有多个元素和混合类型的更复杂的数据中,其中一些变得更加清晰。例如,如果您想发送机场缩写、全名和海拔高度。

我认为 REST 架构可以很好地用于您描述的界面。只要您需要做的就是支持 CRUD 操作。有许多网站可以为您提供 REST 架构风格的优缺点。

我个人更喜欢带有一些 REST-ful 属性的 RPC 样式(解决方案 A)。我希望协议完成解析工作并验证消息。这通常是人们实现 SOAP Web 服务接口的方式。

您的界面今天可能看起来很简单,但明天您的一位客户会要求您进行一个不太适合 REST 模型的新呼叫,您会发现自己将其嵌入到现有的四个消息中。

于 2008-10-08T19:42:06.737 回答
1

这是一个老问题,我敢肯定该服务是很久以前写的,但无论如何我想提供一个答案。

RESTful 方法是定义一个机场资源,例如:

<airport href="/airports/JFK">
    <name>JFK</name>
    <city>New York</city>
    <country>USA</country>
    <continent>North America</continent>
</airport>

或者,如果您想使用与浏览器兼容的微格式:

<div class="object airport" href="/airports/JFK">
    <ul class="attributes"> 
        <li class="name">JFK</li>
        <li class="city">New York</li>
        <li class="country">USA</li>
        <li class="continent">North America</li>
    </ul>
</div>

此资源将位于类似 的 URI 中/airports/JFK,该 URI 将使用方法检索、使用GET方法更新并使用PUT方法删除DELETE

在这样的设计中,URI/airports/将代表数据库中所有机场的容器资源,并且 URI 类似于容器/airports/?city=New+York/airports/?country=USA的过滤器,以返回机场的子集。这两个都是GET方法,资源将包含上面定义的机场资源列表,可以是完整的(因为它们很小),也可以是一些有用的属性,并且href指向每个机场的完整资源。

最后,添加新资源可以是PUT机场完整 URI 上的方法,POST也可以是/airports/. 在这两种情况下,请求的主体都是机场资源,如上所示。方法之间的区别在于谁来决定机场的最终 URI:客户端决定,PUT服务决定POST。您使用哪一种取决于您的客户是否可以合理地确定正确的 URI。通常服务会做出决定,因为 URI 包含一个数字唯一标识符,并且服务必须选择它。

当然,您最初的问题是关于 SOAP,而不是 REST。我将继续按照我所描述的那样设置一个 RESTful 设计,然后将我的资源描述为使用 XSD 和 SOAP 服务的复杂类型,该服务具有复制RESTful 服务的 、 、 和 操作GETPUT操作DELETEPOST这将为您提供 RPC 等价物:

class Airport
    has String name
    has String city
    has String country
    has String continent
    method void update(name, city, country, continent)
    method void delete()

class AirportList
    method Airport[] get(opt name, opt city, opt country, opt continent)
    method void add(name, city, country, continent)
于 2010-07-17T17:13:36.703 回答