PHP Developers Network

A community of PHP developers offering assistance, advice, discussion, and friendship.
 
Loading
It is currently Wed Aug 16, 2017 6:54 pm

All times are UTC - 5 hours




Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: Thu Aug 03, 2017 11:49 am 
Offline
Forum Contributor

Joined: Wed Sep 25, 2013 4:09 am
Posts: 106
Hi,

I tried following Corey Maynards tutorial for RESTful API located at http://coreymaynard.com/blog/creating-a-restful-api-with-php/

Im running the script at my page and after changing:

Syntax: [ Download ] [ Hide ]
// Abstracted out for example
        $APIKey = new Models\APIKey();
        $User = new Models\User();
 


to

Syntax: [ Download ] [ Hide ]
// Abstracted out for example
        $APIKey = "Test";
        $User = "Testuser";
 


I got it to work but I do not know how to supply the APIKey "Test" to the script in my call so I only get the error {"error":"No API Key provided"}

I run my script on http://fairdea.com/api/barcode/

So anything I write after barcode/ is sent to the script.. like /../barcode/GET/1234/Test where I assume the script gets the "GET/1234/Test" part..

I would also appreciate if someone could give some ideas of how the part
Syntax: [ Download ] [ Hide ]
// Abstracted out for example
        $APIKey = new Models\APIKey();
        $User = new Models\User();
 

is supposed to work?

Thanks :)


Top
 Profile  
 
PostPosted: Thu Aug 03, 2017 11:57 am 
Offline
Forum Contributor

Joined: Wed Sep 25, 2013 4:09 am
Posts: 106
Full code for the lazy :D

File API.Class.php
Syntax: [ Download ] [ Hide ]
<?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: *");
        header("Access-Control-Allow-Methods: *");
        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;
        }
    }
   
    public function processAPI() {
        if (method_exists($this, $this->endpoint)) {
            return $this->_response($this->{$this->endpoint}($this->args));
        }
        return $this->_response("No Endpoint: $this->endpoint", 404);
    }

    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(  
            200 => 'OK',
            404 => 'Not Found',  
            405 => 'Method Not Allowed',
            500 => 'Internal Server Error',
        );
        return ($status[$code])?$status[$code]:$status[500];
    }
}
?>

 


api.php

Syntax: [ Download ] [ Hide ]
<?php
require_once 'API.class.php';
class MyAPI extends API
{
    protected $User;

    public function __construct($request, $origin) {
        parent::__construct($request);

        // Abstracted out for example
        $APIKey = "Test"; //new Models\APIKey();
        $User = "Testuser"; //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";
        }
     }
 }
 
 
 // Requests from the same server don't have a HTTP_ORIGIN header
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()));
}


 ?>
 


above code written by Corey Maynards


Top
 Profile  
 
PostPosted: Thu Aug 03, 2017 6:51 pm 
Offline
Moderator
User avatar

Joined: Tue Nov 09, 2010 3:39 pm
Posts: 6268
Location: Montreal, Canada
hybris wrote:
Im running the script at my page and after changing:

Syntax: [ Download ] [ Hide ]
// Abstracted out for example
        $APIKey = new Models\APIKey();
        $User = new Models\User();
 


to

Syntax: [ Download ] [ Hide ]
// Abstracted out for example
        $APIKey = "Test";
        $User = "Testuser";
 

APIKey and User are clearly objects in this example, so you can't just swap them out for strings and expect them to work.


hybris wrote:
I got it to work but I do not know how to supply the APIKey "Test" to the script in my call so I only get the error {"error":"No API Key provided"}

Pass it in as a query string. /api/barcode/foo/bar/?apiKey=whatever

hybris wrote:
I would also appreciate if someone could give some ideas of how the part
Syntax: [ Download ] [ Hide ]
// Abstracted out for example
        $APIKey = new Models\APIKey();
        $User = new Models\User();
 

is supposed to work?

It can be whatever you like. The user model is just there for demonstration purposes, as seen in
Syntax: [ Download ] [ Hide ]
    /**
     * Example of an Endpoint
     */

     protected function example() {
        if ($this->method == 'GET') {
            return "Your name is " . $this->User->name;
        } else {
            return "Only accepts GET requests";
        }
     }
 


APIKey needs to respond to a verifyKey message. A simple implementation would be to have a table containing valid keys, which you could then query against and have the verifyKey method return true if it finds the provided key and false if it does not.

_________________
Supported PHP versions No longer supported versions


Top
 Profile  
 
PostPosted: Thu Aug 03, 2017 6:53 pm 
Offline
Moderator
User avatar

Joined: Tue Nov 09, 2010 3:39 pm
Posts: 6268
Location: Montreal, Canada
Syntax: [ Download ] [ Hide ]
 // Requests from the same server don't have a HTTP_ORIGIN header
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()));
}

This doesn't belong in the same file as your MyAPI class. This is, very loosely, a front controller.

_________________
Supported PHP versions No longer supported versions


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 5:31 am 
Offline
Forum Contributor

Joined: Wed Sep 25, 2013 4:09 am
Posts: 106
Celuran, I said it before but you truely are my Hero :) Thanks!!

Ok so now I know how to pass the apiKey and get the following error:

Fatal error: Call to a member function verifyKey() on string in /customers/d/7/2/fairdea.com/httpd.www/api/barcode/MyAPI.class.php on line 16

which is expected since I do not have a Models class yet.

what does the following part mean?

Syntax: [ Download ] [ Hide ]
$APIKey = new Models\APIKey();
$User = new Models\User();
 


I do get the lines create new objects from the class Models but I cant figure out the \APIKey() or \User() part. I only made classes that you call like /../ new MyClass($str1, $str2);
In my codewindow I see the Models part turn yellow and the \APIKey() part remain black why i figure in the codezippet above there is a class Models and it somehow contain APIKey and User.. I tried to google what it means to call a class like new class\extension but I dont know what to search for..what its called or how it works so I cant find any info about this practice?

So far I "cheated and made 2 classes one for key and one for user to get it to work:

in myClass.php
Syntax: [ Download ] [ Hide ]
       // Abstracted out for example
        $APIKey = new ModelsAPI(); //new Models\APIKey();
        $User = new ModelsUSER(); //new Models\User();
 


Syntax: [ Download ] [ Hide ]
class ModelsAPI {
     public function verifyKey($key, $origin){
         if($key == 'Test'){
         return true;
         } else {
             return false;
         }
     }
 }
 
 class ModelsUSER {
     public function verifyKey($token, $origin){
         if($token == '1'){
             return "PETER";
         } elseif($token == '2') {
             return "Thomas";
         } else {
         
         return false;
         }
     }
 



Some other questions:
how do I supply "token" to this API request anything i write after /?apiKey=Test seems to be interpret as apiKey so if i write /?apiKey=Test/?token=user1 the script will think I provided the apiKey Test/?token=user1 which is not equal to Test and the api will tell me i have the wrong apiKey.

The test function "example" uses GET method
Syntax: [ Download ] [ Hide ]
protected function example() {
        if ($this->method == 'GET') {
            return "Your name is " . $this->User->name;
        } else {
            return "Only accepts GET requests";
        }
     }
 

from what I understand when i type in browser http://www.fairdea.com/api/barcode/example/?apiKey=Test
this is a GET method but how do I write a POST method to this API?
Say I have a function
Syntax: [ Download ] [ Hide ]
protected function test() {
        if ($this->method == 'POST') {
            return "Your name is " . $this->User->name;
        } else {
            return "Only accepts POST requests";
        }
     }
 

How would I call this in the browser?
http://www.fairdea.com/api/barcode/POST ... piKey=Test
Tells me POST is not an endpoint (since the endpoint is test which is moved one step to the left in the request).


Last edited by hybris on Fri Aug 04, 2017 6:56 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 5:36 am 
Offline
Moderator
User avatar

Joined: Tue Nov 09, 2010 3:39 pm
Posts: 6268
Location: Montreal, Canada
hybris wrote:
what does the following part mean?

Syntax: [ Download ] [ Hide ]
$APIKey = new Models\APIKey();
$User = new Models\User();
 


I do get the lines create new objects from the class Models but I cant figure out the \APIKey() or \User() part.

Models is actually the namespace. It creates two classes of different types, an APIKey class and a User class. For example,

Syntax: [ Download ] [ Hide ]
<?php

namespace Models;

class APIKey
{
    public function verifyKey($key, $source)
    {
        // Do stuff here
    }
}
 

_________________
Supported PHP versions No longer supported versions


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 7:04 am 
Offline
Forum Contributor

Joined: Wed Sep 25, 2013 4:09 am
Posts: 106
Aha, I never used namespaces before, thanks :)

I didn't see Your answer so I edited my post above with some more questions how to supply a token to this script and how to call a POST function (instead of GET function).

Sorry for the noob questions but I never worked with API:s before :( Your help is much appreciated :)

EDIT:
I downloaded the insomnia client to test the API and added a PUT function (test1) and a post function (test) (and the GET function example).

when I chose in the client GET and call .../barcode/example/?apiKey=Test it works. Same with the PUT command (test1) it works but when I try the POST command it suddenly say NO API KEY provided.. If i test the POST function (test) with a PUT command it reports correctly it only accepts POST commands so I guess I call it wrong somehow?

Syntax: [ Download ] [ Hide ]
protected function test() {
        if ($this->method == 'POST') {
            return "Your command is POST " . $this->User->name;
        } else {
            return "Only accepts POST requests";
        }
     }
 


(All the 3 functions ( test(POST), test1(PUT), example(GET) ) look the same I only change the POST to GET or PUT


Top
 Profile  
 
PostPosted: Sat Aug 05, 2017 5:51 am 
Offline
Moderator
User avatar

Joined: Tue Nov 09, 2010 3:39 pm
Posts: 6268
Location: Montreal, Canada
When you change the request type to POST, are you continuing to send the key as a query string, or are you including it in the body of the request?

_________________
Supported PHP versions No longer supported versions


Top
 Profile  
 
PostPosted: Sat Aug 05, 2017 6:03 pm 
Offline
Forum Contributor

Joined: Wed Sep 25, 2013 4:09 am
Posts: 106
Celauran wrote:
When you change the request type to POST, are you continuing to send the key as a query string, or are you including it in the body of the request?


Well I still try to send it as a query... When I put it in the body it .... works! :) Thanks!




How do I know when to put it in body and when to put it as a query string and why do you need to put it in the body for some commands instead of always using a query string or always put it in the body?

Why did my PUT command work when I put key as query, I thought PUT was hidden in a POST request???

I never worked with API or restAPI before and my brain is apparently still on vaccation :)

Do you know any good link where i can learn more about restAPI, especially how to call them (like pass key in query for GET requests and in body for POST requests...)

Cheers! :)


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 7:20 am 
Offline
Moderator
User avatar

Joined: Tue Nov 09, 2010 3:39 pm
Posts: 6268
Location: Montreal, Canada
hybris wrote:
How do I know when to put it in body and when to put it as a query string and why do you need to put it in the body for some commands instead of always using a query string or always put it in the body?

Browsers only really support GET and POST, PHP has $_GET and $_POST superglobals, and the API has been designed accordingly. If you look at the code, he's setting the request property to be the contents of the $_GET superglobal when the request is GET or PUT, which is why they work the same.

hybris wrote:
Do you know any good link where i can learn more about restAPI, especially how to call them (like pass key in query for GET requests and in body for POST requests...)

I believe Laracasts has a few series on building out REST APIs, and Phil Sturgeon has a good book on it. https://apisyouwonthate.com/

_________________
Supported PHP versions No longer supported versions


Top
 Profile  
 
PostPosted: Sun Aug 06, 2017 12:26 pm 
Offline
Forum Contributor

Joined: Wed Sep 25, 2013 4:09 am
Posts: 106
I thought the GET is GET and
POST can include PUT or DELETE?

Syntax: [ Download ] [ Hide ]
Download ] [ Hide ] [ Select ] [ Expand ]


        $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");
            }
 


Or am I reading it wrong?
This is what I thought GET was apiKey from QUERY and POST, PUT and DELETE would work the same.

As it is now GET and PUT works the same (QUERY) and POST only work IF i provide the key in BODY and DELETE doesn't work at all?


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 5 hours


Who is online

Users browsing this forum: Bing [Bot] and 12 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group