can i call a web service like this
and not use put and delete method?
is this a REST web service really?
....
$url = 'webservice.php';
$data = array(
'username' => 'a',
'password' => 'a',
'param1'=> 'param1',
'operation'=> 'delete',
'output'=>'xml'
);
$client = curl_init($url);
curl_setopt($client, CURLOPT_POST,1);
....
...................
//webservice.php
$operation=$_POST['operation'];
$param1=$_POST['param1'];
if ($operation=='delete')
{
$r=mysql("delete from list where id='$param1'");
}
if ($operation=='insert')
{
$r=mysql("insert into list values ('$param1')");
}
.....
One of the major ideas behind REST is to not give operational methods as part of the argument (in this case the 'operation' parameter).
Instead, each resource has its unique URL and the HTTP verbs (GET, POST, PUT or DELETE) are used on those resources.
Your approach is not RESTful.
Example:
Deleting a comment using the flickr API:
GET /services/rest/?method=flickr.photos.comments.deleteComment&comment_id=28
Note how they use a 'method' parameter to determine what operation to perform (like in your example).
The RESTful implementation would accept something like:
DELETE /comment/28
Best practise in this case would be to use POST for creating new resources, DELETE for removing them, GET for retrieving existing and PUT to update existing.
This kind of technique is sometimes called "tunneling" because it buries the true operation in the body of a generic POST operation. In my opinion it's kind of a hack and defeats the purpose of REST but sometimes it's necessary if your web host doesnt allow the required HTTP verbs.
Using standard verbs and understanding the implications they make about your service is an important aspect of REST that should not be overlooked. For example, you should be able to issue a DELETE operation repeatedly on the same resource and that should result in only the first DELETE having any effect (idempotent). With GET it should be assumed that no changes are being made on the server (safe). But hiding the operation inside of a POST obscures that.
This isn't really a RESTful service. You don't need to implement all HTTP verbs to be RESTful, but you shouldn't use POST to DELETE data.
Personally I'd implement DELETE for removing items and only use POST for inserting data.
I'm going to go against the tide here and say it is perfectly fine to create RESTful services without PUT and DELETE. The only key thing that you need to be aware of is what you are giving up in doing so.
1) Intermediary components(e.g. proxies, caches, load balancers) will not know whether you are creating/updating or deleting resources, they will only know if the operation is safe (GET) or unsafe (POST). My question is, do you know of any intermediary components that take advantage of this knowledge?
2) The programmer who is going to be accessing your service will not be able to guess how to DELETE a resource, you are going to have to include a link with a link relation that has some documentation that explains to the programmer that they will need to POST to the link's URI to do a delete. It's a bit more work for the client developer but at least you are being explicit about how to do it.
And before I get downvoted into oblivion, here is a quote from Roy Fielding who says it is ok too!
In any case, there are plenty of
RESTful services that do nothing but
GET and POST. The key is how those
methods are used, not what methods are
used. To give a specific example, what
makes a wiki more or less RESTful is
almost always determined by how it
(ab)uses GET and sessions -- whether
it uses PUT or POST for editing pages
is sugar topping in comparison.
http://code.google.com/p/implementing-rest/wiki/FAQ
Having said all of this, creating a single resource called webservice.php and passing a "operation" parameter is a nasty design smell from the perspective of REST.
Related
We have a CORS REST API. It's clean. It works. It's almost perfect. But...
I'm trying to implement an endpoint with request specific parameters. This endpoint should accept POST, PUT, LINK and DELETE requests. And I'm not sure how to implement these parameters.
An example:
A user wants to DELETE a model. There are two scenario's: one where the model is deleted and nothing else happens, and one where the model is deleted + a notification email is sent out.
Initially I implemented this parameter in a header called "X-NOTIFY-OWNER". This worked since it could be added to any of the 4 actions. But now we'd like to get rid of that header, since it's too specific to this single endpoint.
What whould be the best place to put this parameter? A query parameter sounds cleanest (since DELETE and LINK technically don't need a body), but query parameters should be used to filter content. A parameter in the request body would work too, and seems to be the prefered method; but it means sending a body with the DELETE and LINK actions...
Any thoughts on best practice in this case?
I would stick to a query string, DELETE is supposed to ignore the body and only read the URL so it makes sense to use a query string here.
You should use an URL parameter. As you stated, they should be used to filter output and an e-mail can be considered to be output.
I would recommend setting up a new endpoint for the cleanest solution.
example.com/endpoint
example.com/endpointAndNotify
You could either:
Setup the notify endpoint to extend the base endpoint and then add the notification logic to the notify action.
Abstract out the shared logic from both actions, update each action to extend the base class and then add the specific notification logic to the notify action
This way both endpoints stay clean and concise and if you define a standard for this endpoint, any other endpoints that need notification logic can use the same standard.
Backbone tutorials I have read implement some type of a mini-framework (i.e. Slim) with a RESTful architecture performing CRUD on a server db, like this. The Backbone docs state you need a RESTful api, which I believe is due to the Backbone Route and Sync functionality that keeps models up to date, which is a huge aspect of my choosing to use Backbone.
For example, the line below maps a faux url (route) to the 'addWine' function (within a Slim api):
$app->post('/wines', 'addWine');
Assumption 1: If I have a (PHP) CMS backend (and not a mini-framework) I assume I could simply replace the 2nd parameter (addWine) with my own CMS class method call and return a json object.
Assumption 2 But I would not be able to directly call that same class method from a link in the html without causing backbone to lose state and thus it's ability to sync the model data (and remember the browsers history).
Assumption 3 In that case, I will need to use the Slim api and route backbone urls through (Slim) RESTful CRUD calls in order to access my CMS database to keep backbone happy.
If those assumptions are correct, then it would seem backbone is intercepting those HTTP calls - which leaves me wondering how the whole RESTful + Backbone relationship works. Can you explain some of it?
If my assumptions are incorrect, then I need more help than I thought. Can you help with that?
Thanks
I can't speak intimately to your three assumptions, but as for your final question -- Backbone does not "intercept" HTTP calls -- it constructs them, just as any other javascript library would to create an AJAX request.
Backbone is relatively agnostic to your server side language/framework. Here is what Backbone expects any time "sync" is called:
Backbone's sync function uses different HTTP request types based on which method was called. These different HTTP request types are:
POST
GET
PUT
DELETE
Your framework needs to support all of the above to support the "out of the box" functionality of Backbone. This means that you must specify all of the above routes within your application in order to work with Backbone.
One other thing to note is the "create" and "update" method does not carry post data with the request specifically -- instead it sends a content body with a json digest of the data and expects the server side to properly parse a JSON object and deal with it appropriately.
I say yes to all three assumptions and also agree with #Andy Baird.
Also, the only problem to your project is how to notify Backbone that you have updated the database and you would like it to update itself in the front-end. I can only see two solutions:
1) using Javascript's setInterval() - if you do not need the front end to be updated immediately on DB update, you can check for changes every 1 minute, Backbone knows to only update what has changed and add new stuff but of course this is not healthy to the server if you have 1k active people making repeated request every minute
2) using SocketIO or similar service - this way you can send from the server to Backbone either the entire list of modifications to your DB or a simple 'Please refresh, new stuff waiting'. Check this discussion.
I'm adding an API to a Symfony-application which should act as a REST web-service. But there are a few open issues.
Different URIs for bots?
I often read the "suggestion" to use URIs like /api/:id/[...], but I think they wouldn't be REST-compliant: No matter whether bot or human - the same unique resource is identified.
I'm asking, since my statement above makes sense, but I don't expect all the others to be at fault.
Modifying existing controllers?
There are several reasons why I need a separate controller-logic for both cases:
No session-login in the case of a api-requests
different Symfony-forms have to be created (For instance, no widgets are required, at all.)
JSON / XML instead of HTML output
I don't want to modify existing controllers. According to the Open-Closed Principle, classes should be open for extension but closed for modifications, and the controller classes are already in use in a "production"-environment.
My idea is to use an extra HTTP header-field (e.g. "X-UseApi"). The routing should call different actions by evaluating it. Is this possible inside routing.yml? How? Do you have other ideas?
Authentication
This is how I implemented bot-authentication:
$user = Doctrine_Core::getTable('sfGuardUser')->findOneByUsername($params['user']);
if($user->checkPassword($params['password']))
{
//...
}
But the code looks like a workaround to my eyes. Are there better solutions for the whole REST authentication issue? Is the sfGuardPlugin / sfDoctrineGuardPlugin not meeting conditions for such use cases?
Thanks in advance and cheers,
fishbone
my way of doing this would be to use sf_format in routes to distinguish between robot and human (robot will probably need to consume XML whereas human will want HTML.
I would alter my controllers in a way that I would delegate the logic to separate classes depending on what format is requested (this shouldn't be too much work and you would get the flexibility you need).
As for authentication - please provide a bit more information on how do you do it now - the example isn't enough for me to get the general idea of how your implementation works.
Different URIs for bots?
I suggest to not worry too much about URIs. There are more problems with them and thinking too much about it just results in losing time. IMHO it would be great if there would be standardized conventions how to define RESTful URIs. Here is an article about it: http://redrata.com/restful-uri-design/ . You can see that every way of designing your uris has its pros and cons.
But today I would reject the statement that 'api/...' isn't REST compliant. I would just avoid it.
Controller and authentication
Finally, my solution was to implement some sfFilters with responsibilities as follows:
ApiAccessFilter: sets request-attribute 'isApiRequest' if X-ApiKey is defined as header field.
ApiKeyAuthFilter: identifies a user by X-ApiKey, calls signIn / forwards to login-action.
SecureApiAccessFilter: Checks whether the current user has credential
'apiWriteAccess', if HTTP-method is POST, PUT or DELETE.
The first filter allows me to call $request->getAttribute('isApiRequest') later in my actions. That's similar to isXmlHttpRequest(). Finally I came to the conclusion that I have to modify existing actions, because requirements have changed due to the web-service extension.
Cheers, fishbone
Over the last few weeks I've been learning about iOS development, which has naturally led me into the world of APIs. Now, searching around on the Internet, I've come to the conclusion that using the REST architecture is very much recommended, due to its supposed simplicity and ease of implementation.
However, I'm really struggling with the implementation side of REST. I understand the concept; using HTTP methods as verbs to describe the action of a request and responding with suitable response codes, and so on. It's just, I don't understand how to code it.
I don't get how I map a URI to an object. I understand that a GET request for domain.com/api/user/address?user_id=999 would return the address of user 999 - but I don't understand where or how that mapping from /user/address to some method that queries a database has taken place.
Is this all coded in one PHP script? Would I just have a method that grabs the URI like so:
$array = explode("/", ltrim(rtrim($_SERVER['REQUEST_URI'], "/"), "/"))
And then cycle through that array, first I would have a request for a "user", so the PHP script would direct my request to the user object and then invoke the address method. Is that what actually happens?
The main thing I'm not getting is how that URI /user/address?id=999 somehow is broken down and executed - does it actually resolve to code?
class user(id) {
address() {
//get user address
}
}
Actually the API you're trying to describe now is not RESTful. There are many sources describing how to make RESTful APIs. So you should first define your API (taking in account how your client software would use it) and then implement it. I'm quite sure that almost any RESTful API can be implemented in PHP.
Here are some other tips on how to make a RESTful API.
In my opinion GlassFish Server REST Interface is a good example of RESTful design.
That's two questions.
To honor RESTful HTTP verbs, you have to query $_SERVER["REQUEST_METHOD"]. It will contain the usual GET or POST unless a more specialized HTTP request was received. The whole REST buzz is somewhat misleading in itself, and also in misusing the HTTP verbs just for routing.
Anyway, the mapping of request URLs to functions can be achieved in two ways. It's most reliable to use a static map, for example an array that lists URL patterns and destination methods. Most PHP frameworks use an implicit mapping like so:
$args = explode("/", trim($_SERVER['REQUEST_URI'], "/"));
$class = array_shift($args);
$method = array_shift($args);
call_user_func_array("$class::$method", $args);
Note how this is a bad example, security-wise. Only allowed and specifically prepared classes and methods should be able to receive requests. Most frameworks just check if it was derived from an acceptable base class after loading it from a known path and/or instantiating. But a static map is really preferable.
Anyway, regular expression mapping or handling by mod_rewrite is also common. And for utilizing HTTP verbs, just include it as method name.
Have a look at FRAPI - http://getfrapi.com/
As it says focus on your business logic, not presentation.
The agavi framework uses the PUT request for create and POST for updating information. Usually in REST this is used the other way around (often referring to POST adding information while PUT replacing the whole data record).
If I understand it correctly, the important issue is that PUT must be idempotent, while POST does not have this requirement. Therefore, I wounder how creating a new record can be idempotent (i.e. multiple request do not lead to multiple creations of a record) in particular when usually the ORM uses an id as a primary key and the id of a new record would not be known to the client (since it is autocreated in the database), hence cannot be part of the request. How does agavi maintain the requirement of idempotence in light of this for the PUT request.
Thanks.
PUT can be used both for creation and for updating complete records. POST is usually used for partial updates and related operations as well as for creating a new type of record on the server without specifying a URL for the resource (e.g. POST to /articles/23/comments returns a 201 status and a Location: /articles/23/comments/283136 header). So in your case (with a sequence/autoincrement ID), you would use that approach.
However, HTML (and thus web forms) is different. It only knows GET and POST, not DELETE and PUT. For deletion and updating operations, it overloads the POST method.
That's why Agavi, by default, maps POST to "write" and GET to "read" - it's the most common use case, and "read" and "write" were chosen because they are relatively neutral and also in a way represent the safety aspects of GET vs POST (safety as in "GET can be called without side effects" and blah).
You can change the mapping of verbs for the AgaviWebRequest implementation in factories.xml; refer to the Agavi users mailing list or the IRC channel if you need help with that (or ask away here). A lot of people on the IRC channel are also quite experienced with URL scheme design in case you need more help with making your API pretty.
Instead of thinking of PUT as creating, think of it as "putting". You put a resource at a URI (ie send an entire resource to a URI).
PUT http://example.com/articles/1
If you repeat this (send the same entire resource to the same URI) you get the same result and you haven't changed the resource at that URI, that's what makes it idempotent.
If agavi's implementation of PUT is idempotent then it is implementing PUT correctly. Otherwise it's not.
PUT can be used to create a resource, except that if the resource already exists (or has already been created by a previous PUT) it will just update it. POST should not update resources, however, if this is simple CRUD. Note that the HTTP verbs do not have a defined mapping to certain actions necessarily, since they're useful for a lot more than just CRUD.
Also note that this question has nothing to do with REST - just proper HTTP usage. So please remove the REST tag.
I've had this problem before. This can be solved by changing the factories.xml