简体   繁体   中英

REST API: Is it acceptable to PUT to resource containing query param filters?

I'm designing an API Service that returns only JSON representations.

Some background and context to my question... Behind the scenes a product in my database has an associated set of prices. Prices consist of ( qty , currency_code , unit_price ) tuples. Each set of prices belong to a particular product and price list.

Here's a glance at the relational database data. Each row has a unique constraint on ( product_id , price_list_id , currency_code , qty ). Both product_id and price_list_id are forreign keys.

dev=# SELECT * FROM
        price
      WHERE
        product_id = 1 AND price_list_id = 1 AND currency_code = 'GBP';
 id |                 uuid                 | product_id | price_list_id | qty | currency_code | unit_price |          created           |          modified          
----+--------------------------------------+------------+---------------+-----+---------------+------------+----------------------------+----------------------------
  1 | 6fcbbb5b-8e51-4a4c-bf63-270f5d3f1ff8 |          1 |             1 |   1 | GBP           |      20417 | 2019-08-15 15:49:19.508808 | 2019-08-15 15:49:19.508808
 16 | c044e9fe-bb5f-4996-b8e6-88b4a1b9f125 |          1 |             1 |   2 | GBP           |    3453345 | 2019-08-15 15:49:37.896681 | 2019-08-15 15:49:37.896681
 17 | c488d372-e58f-4441-a583-281e4c2b1310 |          1 |             1 |   3 | GBP           |  312353345 | 2019-08-15 15:49:41.320622 | 2019-08-15 15:49:41.320622

To retrieve a set of prices for a given product I intend to use a GET request to the /products/:product_id/prices?price_list_id=1&currency_code=GBP resource. I expect to receive:

[
  { id: "6fcbbb5b-8e51-4a4c-bf63-270f5d3f1ff8", "qty": 1, "unit_price": 20417 },

  ... etc  // 3 items total
]

If I want to update a set of prices for a given ( product_id , price list_id and currency_code ), is it acceptable to do the exact reverse by doing a PUT request to the same URI that I used for the GET , ie, PUT /products/:product_id/prices/price_list_id=1&currency_code=GBP or should I use an alternative?

In the context of a GET request having price_list_id=1 and currency_code=GBP act like filters. Whilst using a PUT request I'm not sure if it's okay to identify a resource for updates using a query parameter as a filter.

Alternatives I've considered are:

  1. PUT /products/:product_id/prices and place the price_list_id and currency_code in the request body. eg
{
  "price_list_id": "<uuid>",
  "currency_code": "GBP",
  "data": [
    { "qty": 1, "unit_price": <newprice> },
    ...  /* new set of prices */
  ]
}

thereby deleting all existing prices and replacing the set with those in the request body.

  1. PUT /products/:product_id/prices/price-lists/:price_list_id which starts to look very long winded. products have many prices so the first part of the resource looks okay to nest as a subresource. However, prices don't have price-lists (it's the opposite way around) so it makes no sense to have price-lists as a sub resource of prices.

  2. PUT /prices/:price_id . This means I have to first retrieve a list, delete them one by one and update prices individually. This is not a good solution as I want to operate on prices as a set collectively. I also want the set to be replaced as a whole or none at all.

The url you are sending a PUT request to should be the 'identity' of the thing you are updating. Including information to locate the resource inside the request body does therefore not make sense.

All the information the server needs to 'locate' the resource you are updating should be in the resource locator (that's the url!)

All of these are completely fine from a HTTP protocol perspective:

  1. PUT /products/:product_id/prices?price_list_id=1&currency_code=GBP
  2. PUT /products/:product_id/prices/price-lists/:price_list_id
  3. PUT /prices/:price_id

It doesn't really matter if your ids are in the path part of query part of the url, it should be irrelevant. For purposes of locating resources there's no difference between /article/5 and /article?id=5 .

So which one you choose should be based on consistency within your own API, user-friendliness, etc.

It's interesting that you don't like option 2 for being too long-winded, but you're ok with option 1 which is even longer though ;)

You can use PATCH /products/:product_id/prices

See this answer to a similar question:

https://stackoverflow.com/a/32101994/853837

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM