-
Notifications
You must be signed in to change notification settings - Fork 0
REST API
Comhon! framework permit to build REST API without any line of codes. Actually you just have to define manifest, serialization, options and Comhon! do the rest!
Dockers images are available on Docker Hub in repository comhon/comhon.
Each following example may be tested with the image 0.2-sample.
To launch a container locally you just have to execute following command.
> docker run -p 80:8000 comhon/comhon:0.2-sample
We will use and configure an apache server as example.
We route all HTTP requests to an unique entry point index.php by defining .htaccess file. These two files must be in same directory.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^((?s).*)$ index.php
</IfModule>
Note that using .htaccess files requires your apache installation to have the AllowOverride All option set.
The entry point PHP file must contain at least comhon dependency, the path to configuration file and a call to HTTP request handler.
<?php
use Comhon\Api\RequestHandler;
use Comhon\Object\Config\Config;
// dependencies
require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
// set the path to configuration file
$config_af = __DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.json';
Config::setLoadPath($config_af);
// Comhon framework handle common requests
// and return a response object that implements Psr\Http\Message\ResponseInterface
$handler = new RequestHandler('/api/comhon');
$response = $handler->run();
// send response to client
$response->send();As seen in entry point, to handle the request you have to use RequestHandler that implements \Psr\Http\Server\RequestHandlerInterface (see psr-15).
To do so, instanciate a request handler...
$handler = new RequestHandler('/api/comhon');...and call run method, and the appropriate response will be returned. The generated response is an object that implements Psr\Http\Message\ResponseInterface (see psr-7).
$response = $handler->run();...or call handle method, with a provided server request and the appropriate response will be returned. The generated response is an object that implements Psr\Http\Message\ResponseInterface (see psr-7).
$response = $handler->handle($request);...and finally send response to client.
$response->send();The base path is the first parameter passed to RequestHandler constructor. This parameter is required. It permit to know which route handler must handle.
For example if we set base path as '/api/comhon' :
- Following URI will be handled
GET http://localhost:8000/api/comhon/person
PUT http://localhost:8000/api/comhon/woman/1000
- Following URI will not be handled
PUT http://localhost:8000/comhon/woman/1000
GET http://localhost:8000/api/person
PUT http://localhost:8000/api/comhonwoman/1000
If a URI is not handled, the returned response contain status code 404 and message not handled route
The API Model name handler may be passed to RequestHandler constructor (optional second parameter). It permit to transform the model name in URI path to a comhon model name (with namespace). Actually a URI path may be case insensitive and some characters are encoded like backslash \ (and make URI path pretty ugly).
Without handler, if we request model Test\Person URI path will look like this :
GET http://localhost:8000/api/comhon/Test%5cPerson/10
To have a prettier URI you may define an API Model name handler to associate a URI path model name to a comhon model. Your API Model name handler must be an intance of a class that implement interface ApiModelNameHandlerInterface
example :
You can see a class example here. And it may be used as following :
$apiModelNames = [
[
"api_model_name" => "persons"
"comhon_model_name" => "Test\Person"
],
[
"api_model_name" => "women"
"comhon_model_name" => "Test\Person\Woman"
"extends" => ["Test\Person"]
],
[
"api_model_name" => "houses"
"comhon_model_name" => "Test\House"
]
];
$response = RequestHandler::handle('/api/comhon', new ApiModelNameHandler(true, $apiModelNames));- A request with URI 'https://www.mydomain.com/api/persons' will handle request with 'Test\Person' model.
- A request with URI 'https://www.mydomain.com/api/hoUseS/10' will handle request with 'Test\House' model.
- A request with URI 'https://www.mydomain.com/api/foo' will not found comhon model and generate a 404 not found response.
if API Model name handler is not specified the API model name must be the comhon model name.
GET https://www.mydomain.com/comhon/Test%5cPerson/10
If request is not handled, request handler help you to handle specific routes.
- You can retrieve server request. It is an object that implements
\Psr\Http\Message\ServerRequestInterface(see psr-7) and contains request informations like URI, headers body...
$request = RequestHandler::getServerRequest();- You can transform request path to urldecoded it, remove duplicated slashes and remove trailing slash.
$path = RequestHandler::filterPath('/my///path//foo%5cbar/', true);
echo $path; // output '/my/path/foo\bar'- You can instanciate interfacer to import export body according headers
$interfacer = RequestHandler::getInterfacerFromContentType('application/json');
$interfacer = RequestHandler::getInterfacerFromContentTypeHeader($request);
$interfacer = RequestHandler::getInterfacerFromAcceptHeader($request);- You can import body easly if body contain an interfaced comhon object
$model = ModelManager::getInstance()->getInstanceModel('Test\Person');
$comhonObject = RequestHandler::importBody($request, $model);You can use RequestHandler with several frameworks because Comhon use PHP standard (psr-7, psr-15)
For example with Slim :
$config_af = __DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.json';
Config::setLoadPath($config_af);
$app = AppFactory::create();
$app->any('/api/comhon[/{resource:.*}]', new RequestHandler('/api/comhon', $resolver));
$app->run();Comhon! permit to retrieve, create, update and delete resources very easly. We will eplain how client must use API for each actions.
There are two kind of URI that comhon API is able to handle : Unique and Collection.
Unique URI look like :
domain / base path / resource model name / resource identifier
It might be use to retrieve, update, or delete a unique resource identified by its identifier.
Collection URI look like :
domain / base path / resource model name
It might be use to retrieve several resources in same time or to create a unique resource.
OPTIONS method permit to know allowed methods (in response header Allow). If an options manifest file is defined on server side, options are returned in response body, otherwise the response body is empty. You can choose the response body format by specifying the header Accept. See available media types here. If header Accept is not provided, the default format is JSON.
// request
OPTIONS http://localhost:8000/api/comhon/man/1
// response
200 OK
Allow: OPTIONS, GET, HEAD, PUT, DELETE
// request
OPTIONS http://localhost:8000/api/comhon/man
// response
200 OK
Allow: OPTIONS, GET, HEAD, POST
GET method permit to retrieve one or several resources. You can choose the response body format by specifying the header Accept. See available media types here. If header Accept is not provided, the default format is JSON.
Unique URI permit to retrieve one resource.
// request
GET http://localhost:8000/api/comhon/man/1
Accept: application/xml
// response
200 OK
Content-Type: application/xml
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="1" firstName="john" lastName="doe">
<birthPlace>1</birthPlace>
<father xsi:nil="true"/>
<mother xsi:nil="true"/>
</root>
Collection URI permit to retrieve several resource.
// request
GET http://localhost:8000/api/comhon/woman
Accept: application/json
// response
200 OK
Content-Type: application/json
[
{
"id": 2,
"firstName": "jane",
"lastName": "doe",
"birthPlace": 2,
"father": null,
"mother": null
},
{
"id": 3,
"firstName": "marie",
"lastName": "doe",
"birthPlace": 3,
"father": 1,
"mother": 2
},
{
"id": 5,
"firstName": "emilie",
"lastName": "doe",
"birthPlace": 2,
"father": 1,
"mother": null
}
]
Request may be customized/filtered by specifying some query parameters.
- You can filter returned resources by specifying properties name and filter values.
For example if you want men called john doe, request and response may look like :
// request
GET http://localhost:8000/api/comhon/man?firstName=john&lastName=doe
Accept: application/json
// response
200 OK
Content-Type: application/json
[
{
"id": 1,
"firstName": "john",
"lastName": "doe",
"birthPlace": 1,
"father": null,
"mother": null
}
]
- You can filter a property that may have several values (like a
INoperator). To do so, you have to suffix property name by[]. For example if you want women called emilie or jane, request and response may look like :
// request
GET http://localhost:8000/api/comhon/woman?firstName[]=emilie&firstName[]=jane
Accept: application/json
// response
200 OK
Content-Type: application/json
[
{
"id": 2,
"firstName": "jane",
"lastName": "doe",
"birthPlace": 2,
"father": null,
"mother": null
},
{
"id": 5,
"firstName": "emilie",
"lastName": "doe",
"birthPlace": 2,
"father": 1,
"mother": null
}
]
- By default, filter is a
conjonction, that mean if you have conditions on several properties, conditions are linked byANDoperator. But you can set adisjunction(OR) by adding-clausequery parameter. For example if you want women that have id2or firstName emilie, request and response may look like :
// request
GET http://localhost:8000/api/comhon/woman?firstName=emilie&id=2&-clause=disjunction
Accept: application/json
// response
200 OK
Content-Type: application/json
[
{
"id": 2,
"firstName": "jane",
"lastName": "doe",
"birthPlace": 2,
"father": null,
"mother": null
},
{
"id": 5,
"firstName": "emilie",
"lastName": "doe",
"birthPlace": 2,
"father": 1,
"mother": null
}
]
- You can order returned resources with query parameter
-order. Order may be ascending (ASC) or descending (DESC) and may be set on several properties. Order value must be aJSON. For example if you want men ordered by birthPlace descending and firstName ascending, request and response may look like :
// request
GET http://localhost:8000/api/comhon/man?-order=[{"property":"birthPlace", "type":"DESC"},{"property":"firstName", "type":"ASC"}]
Accept: application/json
// response
200 OK
Content-Type: application/json
[
{"id": 4, "firstName": "philippe", "birthPlace": 3,...},
{"id": 7, "firstName": "jesse", "birthPlace": 2,...},
{"id": 6, "firstName": "walter", "birthPlace": 2,...},
{"id": 1, "firstName": "john", "birthPlace": 1,...}
]
- You can define a range of returned resources with query parameter
-range. It must be used with query parameter-order. The value of range parameter must look likex-ywithxandynatural number (starting from0) andx <= y. For example if you want the second man to third man, ordered by birthPlace descending and firstName ascending, request and response may look like :
// request
GET http://localhost:8000/api/comhon/man?-range=1-2&-order=[{"property":"birthPlace", "type":"DESC"},{"property":"firstName", "type":"ASC"}]
Accept: application/json
// response
200 OK
Content-Type: application/json
[
{"id": 7, "firstName": "jesse", "birthPlace": 2,...},
{"id": 6, "firstName": "walter", "birthPlace": 2,...}
]
- You can define returned properties with query parameter
-properties.-propertiesmay appear several times and must be suffixed by[]. For example if you want women, and you only want firstName and mother properties, request and response may look like :
// request
GET http://localhost:8000/api/comhon/woman?-properties[]=firstName&-properties[]=mother
Accept: application/json
// response
200 OK
Content-Type: application/json
[
{
"id": 2,
"firstName": "jane",
"mother": null
},
{
"id": 3,
"firstName": "marie",
"mother": 2
},
{
"id": 5,
"firstName": "emilie",
"mother": null
}
]
HEAD works exactly as GET but only headers are returned. See MDN HEAD documentation.
POST method is only available for collection URI. It permit to create a resource. The resource to create must be described in request body. You must specify body media type by specifying the header Content-Type. See available media types here. If resource model has auto generated values, you must omit them.
For example if you want to create a man (with auto generated id), request and response may look like :
// request
POST http://localhost:8000/api/comhon/man
Content-Type: application/json
{
"firstName": "john",
"lastName": "smith"
}
// response
201 Created
Content-Type: application/json
{
"id": 12,
"firstName": "john",
"lastName": "smith",
"birthPlace": null,
"father": null,
"mother": null
}
You can't create resource for abstract model. For example if you want to create a person you will have a response error :
// request
POST http://localhost:8000/api/comhon/person
Content-Type: application/json
{
"firstName": "john",
"lastName": "smith"
}
// response
405 OK
Allow: GET, HEAD, OPTIONS
Content-Type: text/plain
method POST not allowed
PUT method is only available for unique URI. It permit to update a resource. The resource to update must be described in request body. You must specify body media type by specifying the header Content-Type. See available media types here. The identifier must be provided in URI since update is available for unique URI. The identifier may be described also in body but it is not necessary.
For example if you want to update the man previously created, request and response may look like :
// request
PUT http://localhost:8000/api/comhon/man/12
Content-Type: application/json
{
"firstName": "john",
"lastName": "smith",
"birthPlace": 1
}
// response
200 OK
Content-Type: application/json
{
"id": 12,
"firstName": "john",
"lastName": "smith",
"birthPlace": 1,
"father": null,
"mother": null
}
DELETE method is only available for unique URI. It permit to delete a resource. For example if you want to update the man previously created, request and response may look like :
// request
DELETE http://localhost:8000/api/comhon/man/12
// response
204 No Content
There are existing specific URIs that we will explained.
You can retrieve collection objects as GET method but instead of specifying query parameters you may specify a request body. It permit to build more complex request to retrieve more specific resources.
You may call complex request using URI path /request
// request
POST http://localhost:8000/api/comhon/request
Content-Type: application/json
Accept: application/xml
{...}
You must use method POST and you may provide Content-Type header (if not specified by default body is parsed as JSON). See available media types here. The request format is explained in requester chapter.
If you have defined patterns, you may retrieve them by using URI path /pattern/[pattern_name].
// request
GET http://localhost:8000/api/comhon/pattern/email
// response
200 OK
Content-Type: text/plain
/^\\S+@\\S+\\.[a-z]{2,6}$/
If you have defined API model name handler, you may retrieve API model names by using URI path /models.
// request
GET http://localhost:8000/api/comhon/models
// response
200 OK
Content-Type: application/json
[
{
"api_model_name" => "persons"
"comhon_model_name" => "Test\Person"
},
{
"api_model_name" => "women"
"comhon_model_name" => "Test\Person\Woman"
"extends" => ["Test\Person"]
},
{
"api_model_name" => "houses"
"comhon_model_name" => "Test\House"
}
]
There are three available media types.
| format | media type |
|---|---|
| XML | application/xml |
| JSON | application/json |
| YAML | application/x-yaml |