A common way of passing parameters to a RESTful web service is in the URL:
website.com/action.php?table=myTable&key=myKey&values=myValues
Another way would be with JSON:
{
"data":
[
{
"parameters": {"table":"myTable", "key":"myKey", "values":"myValues"}
},
{
"content": {"data1":"dataVal1","data2":"dataVal2"}
}
]
}
What would be the pros and cons of these two methods:
When would I use one over the other
Benefits of each one
Weaknesses
Performance differences
To retrieve values using REST, you must use a GET request. There are no request bodies with GET requests, so your only option is the url.
When changing values in REST service, you typically use the PUT request. This PUT request should contain the new resource state in it's body.
So it's not an either/or matter. Where you place certain request parameters depends on what kind of operation you are doing, and what the meaning of the parameter is.
Related
There are tons of articles, blogs and API docs about REST API resource field expansion but none about how to implement an expansion in aspect of technique and data query in right way.
Simple example for a flat resource response:
GET /api/v1/customers
{
data: [
{
"id": "209e5d80-c459-4d08-b53d-71d11e216a5d",
"contracts": null
},
{
"id": "c641cb83-af29-485d-9be2-97925057e3b2",
"contracts": null
}
],
expandable: ["contract"]
}
Simple example for expanded resource:
GET /api/v1/customers?expand=contract
{
data: [
{
"id": "209e5d80-c459-4d08-b53d-71d11e216a5d",
"contracts": [
{......},
{......},
{......},
]
},
{
"id": "c641cb83-af29-485d-9be2-97925057e3b2",
"contracts": [
{......},
{......},
{......},
]
}
],
expandable: ["contract"]
}
Lets assume we use a api rest controller class which handles the enpoints and a read service (maybe cqs/cqrs based) which uses plain sql for read performance. At which point does the expansion logic start and what is the right way of handling queries without an exponential increase in queries?
Afaik, this is not possible in one or few SQL queries (except the dirty way of GROUP_CONCAT and separation of all data into one field). Should I query all customers and then iterate over all customers and query expanded data for each customer? This would cause an exponential increase of queries.
I was looking for the same thing and I would say that the correct answer is it depends on the infrastructure used.
In a general manner, the implementation should receive the expand or expandable' object in your endpoint and respond acordingly. So, if we have something like your example, when requesting /api/v1/customersviaGETpassing theexpandableobject, you should run another query to the database to get, as in the example, theuser contracts`.
There is not a unique, one size fits all answer for this question, specially if we are not talking about a specific language + framework.
I want to update one of the resources I defined (user) to update his current status (online/offline) and/or location. Therefore I would use a PUT request like this:
PUT http://server/v1/users/12345
Payload
{
"status": 0
}
to set the status offline for example.
Or it could be:
Payload
{
"latitude": 100,
"longitude": 100
}
The backend is based on Laravel/PHP and I respond to this request in my controller:
public function update(Request $request, $userReference) {
// Get payload from request
$bodyContent = json_decode($request->getContent(), true);
$userReference = $userReference;
// Update the user location
$updateResponderLocationCommand = new UpdateResponderLocationCommand($bodyContent);
$this->commandBus->execute($updateResponderLocationCommand);
$response = [
'userReference' => $userReference
];
return $this->setStatusCode(201)->respond($response);
}
This controller uses a command system I integrated that will trigger the task of doing the update.
My questions are, where I struggle:
How do I do differentiate between the commands that should be executed. Right now, only the LocationUpdate is in this method. But I don't want to write a new update message just for the status update.
So how does a request need to work properly? Can I still use the approach with PUT http://server/v1/user/100 and by having keywords in the payload and a select switch - approach within the controller a differentiation between tasks to be executed upon?
Am I suppsoed to use PUT at all, when I only update a single component? I read that I should use POST instead?
Typically, the corresponding action would be sent through the routing mechanism. We would have an action property on your json object that corresponds to the function that should take place.
Lets have a look at that:
public function update(....){
switch($request->get('action'))
{
case 'location':
//execute the command here
break;
case 'status':
//execute the command here
break;
}
}
In this way, we can make sure that our commands are dispatched according to our actions. This is the method that I prefer.
Regarding the whole put v.s. post - there's no reason to do another write-up as this question thread here has the best breakdown and explanation by far.
You should refer back to the link in #2. When creating a resource, you should use PUT. This makes sure that the request is idempotent and will not be duplicated (client's hung, server bottlenecked, etc).
I am building a RESTful API with Yii2 but have some questions in regards to HATEOAS support. Requests will output the pagination headers and include the HATEOAS header.
However the HATEOAS header contains all links as one long string. This is not very helpful for the consumer. Is this standard? Is there a way to change the format in Yii into something that is easier to handle?
Does the following look good?
"_links": {
"self": {
"href": "http://localhost/users?page=1"
},
"next": {
"href": "http://localhost/users?page=2"
},
"last": {
"href": "http://localhost/users?page=50"
}
}
If so, you can easily have links like that. Make sure your data model implements Linkable interface and then implement getLinks() method:
class User extends ActiveRecord implements Linkable
{
public function getLinks()
{
return [
Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),
];
}
}
Serializer will automatically add "_links" to your response.
More info here.
Multiple HTTP headers with the same name can be combined by comma-separating them.
The easiest way to get access to each individual link is to use a HTTP Link header parser library, any of which will for sure already have support multiple comma-separated header values.
It's very important that any client that consumes this supports both a HTTP Header appearing multiple times, and the comma-separated syntax because an intermediate (like a proxy, load balancer, CDN) might change the many-header to combined-header syntax. A good client supports both.
I view my PHP code and JS code as one cohesive unit. I want to begin there interaction by creating an object on the client that looks like the structure below.
By doing this I only have to pass around one object. Sometimes all of the fields are populated, sometimes only 2 or more of the fields are populated.
So by trading off some wasted object properties, I only have to concern myself with passing o_p to different modules with in the MVC on the client and server.
I have functions to convert JavaScript to JSON to PHP.
Is this a valid approach?
Mo.o_p = function (type) {
return {
// current result or data about the data
result : 0,
// send client data
client : {
model : type,
page : {},
args : {}
},
// returned server data
server : {
bookmarks : {},
tweets : {},
smalls : {}
}
};
};
If your model requires these attributes and being empty is an important information for your application, i see no problem there. On the other hand, if your client and server objects are not necessarily connected and handled by different processes, there would be no need to couple them. Just passing some empty attributes should not be a performance problem.
I'm working with a PHP MVC Framework. Works really well. I like the separation of the business layer (model) with the business logic (controller). But i just stumbled upon a problem. Here's the thing:
Suppose i navigate to the following url:
http://localhost/user/showall/
In this case the userController.php is called and within that file there is a method showallAction() which gets executed.
In the showallAction() method i simply do a request to a model which gets all the users for me. Something like this:
public function showallAction()
{
// create userModel object
$users = new userModel();
// get all users and assign the data to a variable which can be accessed in the view
$this->view->users = $users->getAllUsers();
// render views
$this->view->render();
}
So this method gets all the users, assigns the data returned from the userModel to a variable and i can easily work with the returned data in my view. Just a typical MVC thing.
Now here comes the problem.
I also need to create a native iphone variant. Ofcourse the looks will be totally different. So all i actually want to do is to request this url:
http://localhost/user/showall/
And that it just gives me the array (in json format) back. So i can use that for the mobile development.
But this obviously can't be done right now because the showallAction() method assumes that it is for web browser display. It doesn't echo JSON formatted, instead it simply assings the array of users to a variable.
So that means i have to create another method "showallMobileAction()" in order to get the data, but specifically for the mobile device. But this is not an elegant solution. I'm sure that are better ways...
Anyone any idea how can i solve this problem??
In your situation i would modify the routing mechanism.
It would be useful, if you could add extension at the end of URL, which represents the format you expect, like :
http://foo.bar/news/latest >> HTML document
http://foo.bar/news/latest.html >> HTML document
http://foo.bar/news/latest.rss >> you RSS feed
http://foo.bar/news/latest.json >> data in JSON format
It's a simple pattern to recognize. And you can later expand this to add .. dunno .. pdf output, or Atom feeds.
Additionally , two comments :
Model is not a type of objects. Instead it is a layer, containing objects responsible for business logic, and objects responsible for data storage/retrieval.
View should be a full blown object, to which you bind the domain objects (objects responsible for business logic).
You could pass parameters to your url:
/user/showall/json
and get the third URL segment with a custom function or a built-in one. For instance, with CodeIgniter: $this->uri->segment(3).
Some frameworks will pass the additional parameters to your method. Just try this with the URL I wrote above:
public function showallAction()
{
print_r(func_get_args());
}
I'm not familiar with PHP MVC but in general terms I'd use the "accepts" HTML header field to request the response in either "text/html" or "text/json", the controller would check for the accepts type and return the response accordingly.