Reputation: 10031
I'm working on getting a basic RESTful API example. Currently I'm using the example I found here however it has some bugs and is incomplete.
I've already added the following lines to my .htaccess as the example stated.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule api/v1/(.*)$ api/v1/MyAPI.php?request=$1 [QSA,NC,L]
</IfModule>
My problem however is simply getting the concrete class of mine to print "12". I don't know which URL to visit to get the message "12" to be shown on the screen.
Here is my abstract class code:
<?php
abstract class API
{
/**
* Property: method
* The HTTP method this request was made in, either GET, POST, PUT or DELETE
*/
protected $method = '';
/**
* Property: endpoint
* The Model requested in the URI. eg: /files
*/
protected $endpoint = '';
/**
* Property: verb
* An optional additional descriptor about the endpoint, used for things that can
* not be handled by the basic methods. eg: /files/process
*/
protected $verb = '';
/**
* Property: args
* Any additional URI components after the endpoint and verb have been removed, in our
* case, an integer ID for the resource. eg: /<endpoint>/<verb>/<arg0>/<arg1>
* or /<endpoint>/<arg0>
*/
protected $args = Array();
/**
* Property: file
* Stores the input of the PUT request
*/
protected $file = Null;
/**
* Constructor: __construct
* Allow for CORS, assemble and pre-process the data
*/
public function __construct($request) {
header("Access-Control-Allow-Orgin: *"); //any origin can be processed by this page
header("Access-Control-Allow-Methods: *"); //any HTTP method can be accepted
header("Content-Type: application/json");
$this->args = explode('/', rtrim($request, '/'));
$this->endpoint = array_shift($this->args);
if (array_key_exists(0, $this->args) && !is_numeric($this->args[0])) {
$this->verb = array_shift($this->args);
}
$this->method = $_SERVER['REQUEST_METHOD'];
if ($this->method == 'POST' && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) {
if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'DELETE') {
$this->method = 'DELETE';
} else if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'PUT') {
$this->method = 'PUT';
} else {
throw new Exception("Unexpected Header");
}
}
switch($this->method) {
case 'DELETE':
case 'POST':
$this->request = $this->_cleanInputs($_POST);
break;
case 'GET':
$this->request = $this->_cleanInputs($_GET);
break;
case 'PUT':
$this->request = $this->_cleanInputs($_GET);
$this->file = file_get_contents("php://input");
break;
default:
$this->_response('Invalid Method', 405);
break;
}
}
/**
* Determine if the concrete class implements a method for the endpoint that the client requested. If it does, then it calls that method, otherwise a 404
* response is returned
*/
public function processAPI() {
if ((int)method_exists($this->endpoint) > 0) {
return $this->_response($this->{$this->endpoint}($this->args));
}
return $this->_response('', 400);
}
private function _response($data, $status = 200) {
header("HTTP/1.1 " . $status . " " . $this->_requestStatus($status));
return json_encode($data);
}
private function _cleanInputs($data) {
$clean_input = Array();
if (is_array($data)) {
foreach ($data as $k => $v) {
$clean_input[$k] = $this->_cleanInputs($v);
}
} else {
$clean_input = trim(strip_tags($data));
}
return $clean_input;
}
private function _requestStatus($code) {
$status = array(
100 => 'Continue',
101 => 'Switching Protocols',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => '(Unused)',
307 => 'Temporary Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported');
return ($status[$code])?$status[$code]:$status[500];
}
}
And here is my concrete class code (trouble on line 8):
<?php
require_once 'API.php';
class MyAPI extends API
{
protected $User;
public function __construct($request, $origin)
{
echo "12";
parent::__construct($request);
// Abstracted out for example
//$APIKey = new Models\APIKey();
//$User = new Models\User();
if (!array_key_exists('apiKey', $this->request)) {
throw new Exception('No API Key provided');
} else if (!$APIKey->verifyKey($this->request['apiKey'], $origin)) {
throw new Exception('Invalid API Key');
} else if (array_key_exists('token', $this->request) && !$User->get('token', $this->request['token'])){
throw new Exception('Invalid User Token');
}
//$this->User = $User;
}
/**
* Example of an Endpoint
*/
protected function example()
{
if ($this->method == 'GET') {
return "Your name is " . $this->User->name;
} else {
return "Only accepts GET requests";
}
}
}
As you can see line 8 on the concrete class (in the constructor) never gets printed. Currently I'm trying to make my example work by going to:
www.mysite.com/api/myAPI.php?request=get
Upvotes: 2
Views: 9471
Reputation: 31
Not sure if you still need an answer on this, but I just got mine working, with a few tweaks. There are a few problems with your setup. First is the .htaccess file. You have it pointing to MyAPI.php, but if you read the tutorial, he actually has a third file, api.php, that instantiates the MyAPI class. The line:
RewriteRule api/v1/(.*)$ api/v1/MyAPI.php?request=$1 [QSA,NC,L]
should be:
RewriteRule api/v1/(.*)$ api/v1/api.php?request=$1 [QSA,NC,L]
You shouldn't be trying to access the MyAPI.php file directly. Rather, apache should point to api.php, passing the request details along. The contents of the api.php file (per CM's site) are:
<?php
require_once 'MyAPI.php';
if (!array_key_exists('HTTP_ORIGIN', $_SERVER)) {
$_SERVER['HTTP_ORIGIN'] = $_SERVER['SERVER_NAME'];
}
try {
$API = new MyAPI($_REQUEST['request'], $_SERVER['HTTP_ORIGIN']);
echo $API->processAPI();
} catch (Exception $e) {
echo json_encode(Array('error' => $e->getMessage()));
}
?>
The second problem is with your URL - you should follow his example of passing an endpoint (in his/my case, /api/v1/example). This will produce both the text inside the MyAPI constructor (if you leave that in yours), and the message from the example endpoint function. For what it's worth, I stripped out all of the key and user stuff just so I could get it running. So my MyAPI.php file looks like:
<?php
require_once 'AbstractAPI.php';
class MyAPI extends API
{
protected $testmessage;
public function __construct($request, $origin) {
parent::__construct($request);
$this->testmessage = "Test String";
}
protected function example()
{
return $this->testmessage;
}
}
?>
I took the "start simple, dress it up later" approach. Good luck!
Upvotes: 3
Reputation: 398
In my opinion this URL is against REST principles
www.mysite.com/api/myAPI.php?request=get
If one mentions in the url request=get
then the purpose of using HTTP GET dies.
A RESTful url should be something like
www.mysite.com/api/myAPI.php
and a HTTP GET request to this url should do the work.
Upvotes: 2