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.
Related
I use my PHP (like normal) in a server architecture where I have an incoming request and build an outgoing response. All request attributes (like client ID) are given to each sub function and class to make magic with (like in my Mappers and Models and Helpers).
Is there an idea to work with request data WITHOUT putting it from function to function to function.
Idea 1: Make my Request/Response object static. I have exactly one request and one response so there is no problem to do this.
Idea 2: Saving data in Session (or similar). This also sounds uncomfortable and heavy.
Is there another idea doing this?
Implement your own Request or RequestContext class and populate a object of this type with all the data that ongoing methods might need. This way you don't need a growing number of arguments for each of your functions, instead you just forward the entire Request.
This is much easier to extend and you don't suffer drawbacks of "global" data. It's a typical pattern which is used by a lot of frameworks as well.
I am planning to use a architecture (MVC = PHP->Yii) where I will be using REST API based architecture for native site (main web app) and non-native (apps such as iPhone, BB, WAP etc).
No my question is it advisable to use View-Controller (HTML+User Requests) (For Main Site) with Controller-Model (Request/Response+DB) (for API) and same API platform will be used for non-native apps, or should I use full MVC for main site and build separate platform for APIs. This is because I don't want additional HTTP (cURL) overhead for main site.
Update:
#karmakaze
That's the though with me but still I have to write RestControllers because there is lots of code involved. So anyway we ends up having two different controllers e.g.
UserController extends CController {
actionRegister() {
...Some calculations
.
.
Instead of making Calls to model We will use call controller i.e.
$userRest = new UserRestController();
/*
* This will return json data or php obj depending on params passed or
* may raise an exception
*/
$userRest->actionCreate($param1, $param2);
// Process data obtained.
}
}
or is there any other way around?
If it fits your needs, you may build a front-end based on some JavaScript libraries like AngularJs, BackBone.JS or any other MVC JavaScript library.
This way you should build only one RESTful API in Yii, as the back-end of your app.
This solution, however, lets something uncovered: it will be hard to make the application crawlable.
The perspective of the question made me to understand that it is relatively important to render the HTML on the server side. I am thinking at this solution:
make normal MVC app, including controllers and views
Use any of the following ticks:
a GET parameter that will be false by default, but ture when it is an API call:
Check this example:
// in a controller:
public function actionView($id, $api=false) {
// some calculations, getting the $model variable
if ($api) {
echo $model->json_output(); // you can implement it in components/model.php or generate the json output some other way
} else {
render('view', array('model'=>$model));
}
}
a subdomain called, for instance, api (you'll have api.yourapp.tld),
or use another HTTP parameter from the request to determine if it is an API call or not.
Any of these version will bring a way of verifying if the client requests a JSON/XML response (API call) or HTML for the browser.
This way you avoid the headache of building separate controllers for the API and the main site.
Note It is not required to do this trick for actions when they simply render a form - it is useless.
Note 2 You can use the latter method to render the requests with _request_fragment and assume that every request is an API call unless $_GET['_request_fragment'] is specified. Like this you can make an AngularJs, Blackbone.js app crawlable with Yii.
UPDATE The _request_fragment is specified here, and it is used by most search engines to crawl AJAX web applications.
You can use the same for both native and non-native app, it will also reduce you work at development time as well update or change ur logic.... I do have so many experience with such situation..
Use full MVC for main web app and reuse the same Models and Controllers to build the REST API. There are extensions which will do this automagically if you follow Yii conventions while building the main web app. Just search REST in the Yii extensions. We'll be doing the same for our mobile apps. Currently investigating RESTFullYii.
There are hundreds of tutorials of how to create your own simple MVC
I can't find any tutorial how to implement HMVC with it but I do understand how does HMVC work, it is a bit like Ajax, you can request many controllers from a controller.
Could you come up with small pseudo design or oop design how does HMVC work? So I will get rough idea where to start!
First of all. There are two kinds of HMVC.
1 Kohana style. Where you can access other controllers within you application
Possible implementation:
All request data (URL, POST, GET ...) are stored in Request variable. In this case none of controllers can access POST, GET or other request variables directly. All data should be taken from Request variable.
When controller1 executes method to access controller2, new (fake) Request instance is being created and transferred to controller2. Controller2 does not know that he is executed not via real request, but fake one and process data as usual.
2 Real HTTP request over the internet. In this case you can access other online services within your controller. Unfortunately I don't know real world examples.
Possible implementation: using CURL
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
I keep seeing this line of code in many plugins. What exactly does it do, and what other possibilities are there other than Request_Http
if (!$request instanceof Zend_Controller_Request_Http)
return;
I believe that originally all of ZF's routing was based on the REQUEST_URI, so many of the routing classes operated on $request which was a string. In order to facilitate things such as routing for subdomains this was changed so that the whole request object was passed in. So checks such as the one in your example are merely checking that $request is a request object (and not a string) before calling its methods.
(Disclaimer: this is speculation based on my knowledge of ZF's routing classes, I may be wrong!)
Sifting through the Zend Framework docs, I see that a sibling class of Zend_Controller_Request_Http is Zend_Controller_Request_Simple, which is used to get information about command-line requests to a ZF application. The plugins in question probably only work for web apps requested using a browser (or whatever HTTP client), so these plugins back out otherwise.
Even it is unusual every class, that extends Zend_Controller_Request_Abstract can occur.