构建RESTful API - 处理POST请求和验证

时间:2012-03-07 12:32:33

标签: api rest post

我正在构建一个API,并且正在努力处理发布和更新资源的最佳方法。此时,用户将使用XML将数据发布到产品资源。我根据xsd文件验证了这个XML。这很有效,但意味着想要更新一个字段的用户必须发布与产品相关的所有字段。这是预期的,还是应该以其他方式做到这一点?

我有另一个资源需要“status”或“dispatched”更新,但只会更新一个或另一个,而不会同时更新。因此,我是否会以与产品资源相同的方式构建它,并且要求它们始终POST两个字段,即使只有一个字段会被更新或将它们作为两个单独的资源?

2 个答案:

答案 0 :(得分:2)

您可以通过其他方式处理更新。第一个问题是您的客户如何知道如何更新资源?如果他们在带外接收信息,那么你就不会做REST。通过在带内包含更新信息,它使得如何更新显式,从而为您提供更大的自由度和灵活性。例如,product资源可以表示如下

<product href="/products/123"
    name="iPad 64GB + 4G"
    price="829.00">
    <description>It's cool, ya</description>
    <update href="/products/123" method="PUT">
        <name type="xs:string" cardinality="optional"/>
        <price type="xs:decimal" cardinality="optional"/>
        <description type="xs:string" cardinality="optional"/>
    </update>
    ... you could have a delete form here as well ...
</person>

同时,products集合可以表示为

<people href="/products">
    ... the first set of items in the collection and pagination links could go here ...
    <create href="/product" method="POST">
        <name type="xs:string" cardinality="required"/>
        <price type="xs:decimal" cardinality="required"/>
        <description type="xs:string" cardinality="required"/>
    </create>
</people>

注意更新和创建之间参数的基数是如何不同的。

就PUT替换整个资源状态而言,请以这种方式考虑:当我更新product并仅指定price时,namedescription默认为其现有值,然后更新整个资源。我们唯一需要做的就是在我们的媒体类型中明确定义这个逻辑。

根据您的描述,您目前可以按照

的方式表达某些内容
<product href="/products/123"
    name="iPad 64GB + 4G"
    price="829.00">
    <description>It's cool, ya</description>
    <update href="/products/123" method="PUT">
        <product type="my:product" cardinality="required"/>
    </update>
    ... you could have a delete form here as well ...
</person>


<people href="/products">
    ... the first set of items in the collection and pagination links could go here ...
    <create href="/product" method="POST">
        <product type="my:product" cardinality="required"/>
    </create>
</people>

这一点也不错,它只是更新的类型,因为你必须包括所有的字段。你觉得这是错的并且对此提出质疑是正确的。

此外,我强烈建议您不要使用XSD来验证请求。除非XSD设计得非常谨慎,否则它会在您的客户端和API之间产生紧密耦合,这将迫使您同时或以特定顺序(例如,服务器,然后客户端)更新这两者。请改为查看Schematron

现在,就具有orderstatus详细信息的dispatch而言,请将order资源视为一个小型状态机。它可能看起来像这样:

Start ----> Received ----> Processed -------> Dispatched ------> End
               |               |                                  ^
               |               V                                  |
               ----------> Cancelled ------------------------------ 

因此,在创建订单时,会自动将其状态设置为已接收。从那里它可以被处理或取消,并且从处理它可以去发送或取消。我们的想法是通过资源中提供的表单和链接来表示转换。基于此,以下是我们如何为各种状态

表示order

收到(从客户的角度):

<order href="/orders/123" status="received">
    ... order details go here ...
    <cancel href="/orders/123" method="delete"/>
</order>

收到(从员工的角度来看):

<order href="/orders/123" status="received">
    ... order details go here ...
    <process href="/orders/123" method="put">
        ... whatever details need to be submitted at the same time ...
    </process>
</order>

已处理(从客户的角度来看)

<order href="/orders/123" status="processed">
    ... order details go here ...
    <cancel href="/orders/123" method="delete"/>
</order>

已处理(从员工的角度来看):

<order href="/orders/123" status="processed">
    ... order details go here ...
    <dispatch href="/orders/123" method="POST">
        <company type="xs:string" cardinality="required"/>
        <con-note type="xs:string" cardinality="required"/>
        <tracking type="xs:anyURI" cardinality="optional"/>
        ... whatever details need to be submitted at the same time ...
    </dispatch>
</order>

调度(从客户和员工的角度来看)

<order href="/orders/123" status="dispatched">
    ... order details go here ...
    <shipping-details href="/order/123/shipping">
</order>

取消(从客户和员工的角度来看)

<order href="/orders/123" status="cancelled">
    ... order details go here ...
</order>

根据权限将不同的状态转换呈现给不同的用户。员工无法取消订单,同样客户也无法处理或发送订单。此外,仅根据订单的当前状态显示允许的状态转换。

答案 1 :(得分:1)

使用POST,您可以灵活地定义为给定资源定义的状态。 POST可以是一种全能的方法。如果您使用的是PUT方法,那么您需要替换整个资源状态,因为HTTP规范将此定义为正确的行为。实际上,为状态创建单独的资源并使用自己的GET / POST / PUT / DELETE行为调度以表示您正在描述的用例可能是有意义的。

相关问题