我的情况如下:
我有一个规范化的数据库,其中包含有关机场的地理信息。结构是:
airport --is in--> city --is in--> country --is in--> continent
现在我想让用户管理这些数据,而不是让他们直接访问数据库。我们需要通过Web服务提供此管理界面。
现在,在设计服务时,我们讨论了如何定义操作。我们提出了不同的解决方案:
解决方案A:具体操作
对于四个表(机场,城市,国家,大陆)中的每一个,我们定义了3个操作:
这将导致12个操作,其中2个请求/响应对象= 24个对象
要创建一个包含所有依赖项的全新机场,至少需要4个请求。
解决方案B:通用
只有一个操作,通过参数控制。此操作能够创建管理数据库所需的一切。
操作将决定需要做什么并执行它。如果发生错误,它将回滚所有内容。
==> 1操作= 2个高度复杂的请求/响应对象
解决方案C:在中间见面1
每个表一个通用操作,它能够执行get,insert,update,就像解决方案B一样,但每个都集中在一个表上。
==> 4个操作= 8个复杂的请求/响应对象
解决方案D:在中间见面2
每个操作一个通用操作(get,insert,delete),它可以在每个表上运行并解决依赖关系。
==> 3个操作= 6个稍微复杂的请求/响应对象
示例
由于这是相当抽象的,所以用于创建请求对象的简化示例(JFK /纽约/美国/北美):
解决方案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是否足以理解,发生了什么?
答案 0 :(得分: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架构风格的优缺点。
我个人更喜欢RPC样式(解决方案A)和一些REST-ful属性。我希望协议能够进行解析工作并验证消息。这通常是人们实现SOAP Web服务接口的方式。
您的界面今天可能看起来很简单,但明天您的一位客户会要求您拨打一个不适合REST模型的新电话,并且您会发现自己将其嵌入现有的四条消息中。
答案 1 :(得分: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>
此资源位于/airports/JFK
之类的URI,可使用GET
方法检索,使用PUT
方法更新,并使用DELETE
删除方法
在这样的设计中,URI /airports/
将代表数据库中所有机场的容器资源,而/airports/?city=New+York
和/airports/?country=USA
之类的URI将是容器上的过滤器返回机场的子集。这两种方法都是GET
方法,资源将包含上面定义的机场资源列表,要么是完整的(因为它们很小),要么包含一些有用的属性和href
指向每个机场的全部资源。
最后,添加新资源可以是机场完整URI上的PUT
方法,也可以是POST
上的/airports/
方法。在这两种情况下,请求的主体都是机场资源,如上所示。这些方法之间的区别在于谁来决定机场的最终URI:客户决定PUT
,服务决定POST
。您使用哪一个取决于您的客户是否可以合理地确定正确的URI。通常服务决定因为URI包含数字唯一标识符,服务必须选择它。
当然,你最初的问题是关于SOAP,而不是REST。我将继续按照我的描述设置RESTful设计,然后使用XSD和SOAP服务将我的资源描述为复杂类型,其操作复制GET
,PUT
,{{1}和RESTful服务的DELETE
操作。这将为您提供RPC等效于:
POST