I am trying to design a rest api and i have a resource with links to another resource. say
GET /resource1/id1
{
"property1": "value1",
...
"links" : [
{
"rel": "res1to2rel",
"href": "/resource2/r2"
}
]
now on a post to create resource1, i want to pass a relationship to resource2. should the POST call look like
POST /resource1
{
"res1to2rel": "/resource/r2"
}
or should it look like
POST /resource1
{
"links" : [
{
"rel": "res1to2rel",
"href": "/resource2/r2"
}
]
}
What is the best practice, should the POST match the GET or can it have properties that you dont see on GET?
There're few absolute rules when it comes to REST design, but I think that it'd be easier for the client if a resource has a consistent set of representations. When you POST a representation, you typically create a new resource. This can be made even more explicit by:
Returning 201 Created.
Returning a Location header in the response.
Returning a copy of the representation as the server sees it.
Thus, a POST might look like this:
POST /resource1 HTTP/1.1
Content-Type: application/json
{
"property1": "value1",
...
"links" : [
{
"rel": "res1to2rel",
"href": "/resource2/r2"
}
]
}
The response might then be:
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: /resource1/id1
{
"property1": "value1",
...
"links" : [
{
"rel": "self",
"href": "/resource1/id1"
},
{
"rel": "res1to2rel",
"href": "/resource2/r2"
}
]
}
Notice that the server is allowed to enrich the resource, in the above example by adding a "self" link.
If a client later makes a GET request against the address supplied in that LOCATION header, it'll receive the same response as the echo it received from the POST request:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"property1": "value1",
...
"links" : [
{
"rel": "self",
"href": "/resource1/id1"
},
{
"rel": "res1to2rel",
"href": "/resource2/r2"
}
]
}
This makes it easier for clients to interact with the API because it can use the same internal model for reading and writing (i.e. serialising and deserialising) representations of a resource.
Related
I've got a data flow that has a REST API source.
The REST API is paginated, an example JSON that links to the next page is shown below:
"links": [
{
"href": "/api/v1/widgets/?page_number=1",
"method": "GET",
"rel": "first"
},
{
"href": "/api/v1/widgets/?page_number=2",
"method": "GET",
"rel": "next"
},
{
"href": "/api/v1/widgets/?page_number=30",
"method": "GET",
"rel": "last"
}
]
To retrieve all the data, I need to use a Pagination Rule. This can be done using the JSON path to the correct value in the document.
I've got the following rule set, but this does not trigger data factory to call the next page:
$.links[?(#.rel=="next")].href
RFC5988 is set to false, which I believe should trigger the rule to be run instead.
What am I doing wrong that's causing the page to not be called?
I've got a strange issue with PayPal's Sandbox / API V2.
After creating an order with the AUTHORIZE intent (pre-auth). I'm taking the user to the APPROVE URL, and after selecting the payment method PayPal says that it's redirecting me back to my redirect_url, but instead it just reloads the payment selection screen.
I don't know what's wrong.... This is what I'm passing directly to the API:
curl -v -X POST https://api.sandbox.paypal.com/v2/checkout/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <my-access-token>" \
-d '{
"intent":"AUTHORIZE",
"description":"Description goes here",
"soft_descriptor":"Descriptor",
"purchase_units":[
{
"amount":{
"currency_code":"CAD",
"value":"351.75"
}
}
],
"order_application_context":{
"return_url":"redacted_for_privacy",
"cancel_url":"redacted_for_privacy"
}
}
That call is obviously working as PayPal is returning the CREATED response. I have looped through the returned HATEOAS links and redirected the user to the approve URL ... Then the problem starts...
API response is:
{
"id": "8KF74291SN313461D",
"intent": "AUTHORIZE",
"status": "CREATED",
"purchase_units": [
{
"reference_id": "default",
"amount": {
"currency_code": "CAD",
"value": "351.75"
},
"payee": {
"email_address": "sb-iuaiy3198427#business.example.com",
"merchant_id": "DXYXG2JAU3SQQ"
}
}
],
"create_time": "2020-09-15T05:13:59Z",
"links": [
{
"href": "https://api.sandbox.paypal.com/v2/checkout/orders/8KF74291SN313461D",
"rel": "self",
"method": "GET"
},
{
"href": "https://www.sandbox.paypal.com/checkoutnow?token=8KF74291SN313461D",
"rel": "approve",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v2/checkout/orders/8KF74291SN313461D",
"rel": "update",
"method": "PATCH"
},
{
"href": "https://api.sandbox.paypal.com/v2/checkout/orders/8KF74291SN313461D/authorize",
"rel": "authorize",
"method": "POST"
}
]
}
Issue was their confusing API documentation.
order_application_context should be changed to: application_context in the API Call
I am trying to create dynamic mocks using WireMock. I have a situation where if I specify URL like :
http://localhost:8089/api/account/abc#abc.com
then I should receive response like:
{
"account" : "abc#abc.com"
}
In brief, the path param is returned in the response body. I am able to make the request URL generic by using urlPathPattern set to /api/account/([a-z]*) however, I am not sure how should I capture abc#abc.com and return this in the response using regular expressions.
In WireMock the regular expressions can be used to recognize the email format in the Request Matching. For the purpose of this example I used a very crude example. Your production implementation may require a more robust approach.
This request:
http://localhost:8181/api/account/someone#somewhere.net
Is matched by this rule:
{
"request": {
"method": "GET",
"urlPathPattern": "/api/account/([a-z]*#[a-z]*.[a-z]*)"
},
"response": {
"status": 200,
"jsonBody": {
"account": "{{request.path.[2]}}"
},
"transformers": ["response-template"],
"headers": {
"Content-Type": "application/json"
}
}
}
And returns this response:
{
"account": "someone#somewhere.net"
}
It makes use of a Response Template processing functionality in WireMock. The Request Model variables [{{request.path.[2]}}] can be used to obtain sections from the request.
The same can be done using WireMock.Net - Response-Templating
The rule looks like:
{
"Request": {
"Path": {
"Matchers": [
{
"Name": "RegexMatcher",
"Pattern": "^/api/account/([a-z]*#[a-z]*.[a-z]*)$"
}
]
},
"Methods": [
"get"
]
},
"Response": {
"StatusCode": 200,
"BodyAsJson": {
"account": "{{request.PathSegments.[2]}}"
},
"UseTransformer": true,
"Headers": {
"Content-Type": "application/json"
}
}
}
The HATEOAS definition say that each response must contains the associated links, but following the REST pattern an URI must be the same for all operations, what changes is the HTTP method.
Per example:
Request: GET http://example.com/book
Response:
[
{
"id": 1,
"title:" "foo",
"links": {
"self": http://example.com/book/1
}
}
]
The response inform that the link that can be used to read the book data is "http://example.com/book/1", but with only this link i unknown if the user that did this request is authorized to do a DELETE or PUT for this resource.
The HATEOAS or other specification define some pattern for inform what methods are available for each resource?
Per example:
Request: GET http://example.com/book
Response:
[
{
"id": 1,
"title:" "foo",
"links": {
"self": {
"url": "http://example.com/book/1",
"methods": [
"GET",
"PUT",
"DELETE"
]
}
}
]
You should look for Siren (http://hyperschema.org/mediatypes/siren)
It will tell your clients what methods do they can use with links.
Let's assume we have the following REST resource collection endpoint:
/products
The resource looks like this:
GET /products/123
Accept: application/json
{
"id": "123",
"name": "Shampoo",
...
"ingredients": [
"Sodium Lauryl Sulfate",
"Sodium Laureth Sulfate",
"Hydrochloric Acid",
...
]
}
Now, let's say I want to update the resource, and add or remove an ingredient to / from the ingredients field.
One way is to GET the resource, manipulate the array, and PUT or PATCH back. However, this is pretty chatty protocol, and it also poses scaleability problem, if the array is too big.
What is the best way to achieve this add / remove operation without having the source array?
I already explored the following: JSON Patch Specification
But it seems the spec is not REST. It uses the same resource with different "operation" like representation, which is entirely different from the resource's representation.
I would like to keep as much as possible the same representation of resource for PUT / PATCH method as it is appearing in GET.
Here are my thoughts on the matter.
Change the string type item to object
Add meta field to the object called $op
So in this design, here is how the resource looks like on GET
GET /products/123
Accept: application/json
{
"id": "123",
"name": "Shampoo",
...
"ingredients": [
{
"name": "Sodium Lauryl Sulfate"
},
{
"name": "Sodium Laureth Sulfate"
},
{
"name": "Hydrochloric Acid"
},
...
]
}
And now if I want to remove an item from the ingredients array:
PATCH /products/123
Content-Type: application/json
Accept: application/json
{
"ingredients": [
{
"$op": "remove",
"name": "Hydrochloric Acid"
}
]
}
Or add it back:
PATCH /products/123
Content-Type: application/json
Accept: application/json
{
"ingredients": [
{
"$op": "add",
"name": "Hydrochloric Acid"
}
]
}
Does anyone has a better way? Is there some standard on the subject?
In my opinion the best solution would be to provide another REST endpoint for product's ingredients management. When you in addition provide links in a HATEOAS manner, you will be able to fully controll that array right after getting a product resource.
Consider this representation:
GET /products/123
Accept: application/json
{
"id": "123",
"name": "Shampoo",
...
"ingredients": [
{
"name": "Sodium Lauryl Sulfate"
"links": [
{
"rel": "self",
"href": "/products/123/ingredients/1"
}
]
},
{
"name": "Sodium Laureth Sulfate",
"links": [
{
"rel": "self",
"href": "/products/123/ingredients/2"
}
]
},
...
],
"links": [
{
"rel": "self",
"href": "/products/123"
},
{
"rel": "ingredients",
"href": "/products/123/ingredients"
}
]
}
Having this you could add new ingredient to a product...
POST /products/123/ingredients
Content-Type: application/json
Accept: application/json
{
"name": "Hydrochloric Acid"
}
...or delete existing one.
DELETE /products/123/ingredients/2
What is the best way to achieve this add / remove operation without having the source array?
If you remove ingredients array from product's JSON representation and leave only link to it you are still able to add new ingredient right away.
Remove operation still need source array but you could download it asynchronously. In fact i see no point in deleting anything from array without having it first. How do you know that ingredient you try to remove is inside?
Best regards