-
-
Notifications
You must be signed in to change notification settings - Fork 315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
clean the baseUrl so restler can be not in root #671
base: master
Are you sure you want to change the base?
Conversation
any change someone can review this and merge it into the code base? |
Let's discuss this here! What is your use case? I could not understand your statement above Once you provide your use case, let me come up with an example that makes it work without your fix. If I could not do that we can see how we can fix the issue Agreed? |
First sorry for the delay: Our use case is that we have an endpoint in our application. Our application is / and the endpoint lives in /endpoint/ the rest endpoint lives in /endpoint/rest/ (and the soap in /endpoint/soap/). As we really like the integration and api view and security this package provides: we are embeding your package so it is only called on /endpoint/rest/ by our application. As we can set an baseUrl (so you can let the package live not in / but in /endpoint/rest/) the path's are not "cleaned" against the baseUrl. We are initation restler: Defaults::$throttle = 20; //time in milliseconds for bandwidth throttling
$cacheDir = Config::$dirs['tmp'] . "/restler/";
if (!file_exists($cacheDir)) {
mkdir($cacheDir);
}
Defaults::setProperty('cacheDirectory', $cacheDir);
Defaults::setProperty('apiAccessLevel', 0);
$productionMode = false;
if (strtolower(Config::getEnv()) != 'dev') {
$productionMode = true;
}
$Restler = new Restler($productionMode);
Restler::addListener('onRespond', function () {
header('X-Powered-By: Mosquito Framework');
});
$Restler->setBaseUrls(Router::generateUrlFromHostName() . Router::addVars2activePath(array('rest')));
$Restler->addAPIClass('\\Luracast\\Restler\\Explorer\\v2\\Explorer', 'explorer'); //this creates resources.json at API Root
$Restler->addFilterClass('\\Luracast\\Restler\\Filter\\RateLimit'); //Add Filters as needed
foreach ($routes as $key => $className) {
preg_match_all("/\{vars\[[\d]\]\}/", $className, $varsA);
foreach ($varsA[0] as $var) {
$index = ((int)str_replace(array('{vars[', ']}'), array('', ''), $var) + $offset);
if (!empty($vars[$index])) {
$className = str_replace($var, $vars[$index], $class);
}
}
if (class_exists($className)) {
$Restler->addAPIClass($className, $key);
}
}
$Restler->addAPIClass(__NAMESPACE__ . '\\Endpoint\\Auth', 'auth');
$Restler->addAuthenticationClass(__NAMESPACE__ . '\\Endpoint\\AccessControl');
$Restler->handle(); then the path isn't reconised because 'sims' != '/endpoint/rest/sims' So this fix will '/endpoint/rest/sims' convert to sims because the basepath is So this fixes makes that if you use |
You are using Apache server or Nginx? |
Both. That is for the user of our simpel inhouse framework, mostly it is apache. But we write everything so it is compatible with both. |
Ok I have an possible other fix: If we can set the path from our end and the list($base, $path) = Util::splitCommonPath(
strtok(urldecode($_SERVER['REQUEST_URI']), '?'), //remove query string
$_SERVER['SCRIPT_NAME']
); then a user can bypass the hole So an simple setPath() that sets the $this->path and the getPath begin that changes from: /**
* Parses the request url and get the api path
*
* @return string api path
*/
protected function getPath()
{
// fix SCRIPT_NAME for PHP 5.4 built-in web server
if (false === strpos($_SERVER['SCRIPT_NAME'], '.php'))
$_SERVER['SCRIPT_NAME']
= '/' . substr($_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT']) + 1);
list($base, $path) = Util::splitCommonPath(
strtok(urldecode($_SERVER['REQUEST_URI']), '?'), //remove query string
$_SERVER['SCRIPT_NAME']
); to /**
* Parses the request url and get the api path
*
* @return string api path
*/
protected function getPath()
{
if (isset($this->path) && is_string($this->path)) {
$base = '';
$path = $this->path;
} else {
// fix SCRIPT_NAME for PHP 5.4 built-in web server
if (false === strpos($_SERVER['SCRIPT_NAME'], '.php'))
$_SERVER['SCRIPT_NAME']
= '/' . substr($_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT']) + 1);
list($base, $path) = Util::splitCommonPath(
strtok(urldecode($_SERVER['REQUEST_URI']), '?'), //remove query string
$_SERVER['SCRIPT_NAME']
);
} this way, you can use possible more systems then apache/nginx if you feed restler on your own and correctly.... |
Here is an example that is tested in my localhost endpoint.zip under Apache webserver. Unzip the content under webroot and try it out It shows BMI example API under Hope this helps |
No that doesn't help. Let me explain furter: /endpoint/rest/ is in your example handled by the script /endpoint/rest/index.php. So restler sees the basepath /endpoint/rest/ But with our code the /endpoint/rest/ is handled by: So /endpoint/rest/ will call the script below because our internal router will call only that part of the script when on /endpoint/rest/ if it is /endpoint/soap/ it will initiate an soap server and so on. Defaults::$throttle = 20; //time in milliseconds for bandwidth throttling
$cacheDir = Config::$dirs['tmp'] . "/restler/";
if (!file_exists($cacheDir)) {
mkdir($cacheDir);
}
Defaults::setProperty('cacheDirectory', $cacheDir);
Defaults::setProperty('apiAccessLevel', 0);
$productionMode = false;
if (strtolower(Config::getEnv()) != 'dev') {
$productionMode = true;
}
$Restler = new Restler($productionMode);
Restler::addListener('onRespond', function () {
header('X-Powered-By: Mosquito Framework');
});
$Restler->setBaseUrls(Router::generateUrlFromHostName() . Router::addVars2activePath(array('rest')));
$Restler->addAPIClass('\\Luracast\\Restler\\Explorer\\v2\\Explorer', 'explorer'); //this creates resources.json at API Root
$Restler->addFilterClass('\\Luracast\\Restler\\Filter\\RateLimit'); //Add Filters as needed
foreach ($routes as $key => $className) {
preg_match_all("/\{vars\[[\d]\]\}/", $className, $varsA);
foreach ($varsA[0] as $var) {
$index = ((int)str_replace(array('{vars[', ']}'), array('', ''), $var) + $offset);
if (!empty($vars[$index])) {
$className = str_replace($var, $vars[$index], $class);
}
}
if (class_exists($className)) {
$Restler->addAPIClass($className, $key);
}
}
$Restler->addAPIClass(__NAMESPACE__ . '\\Endpoint\\Auth', 'auth');
$Restler->addAuthenticationClass(__NAMESPACE__ . '\\Endpoint\\AccessControl');
$Restler->handle(); Do you understand it now? We are not looking to add an fysical folder in the webroot with an custom index.php. Our webroot only contains /index.php and an .htaccess. |
If you want the <?php
require_once '../vendor/autoload.php';
use Luracast\Restler\Restler;
$r = new Restler();
$r->addAPIClass('BMI', 'endpoint/rest/bmi');
$r->handle(); I would strongly recommend the above method instead using symlinked public folder as a subfolder in the webroot. This gives the possibility of
|
If I do it like you says, the explorer is completly empty: (but the rest api will work) {
"swagger": "2.0",
"host": "tim.dev.tool.nl",
"basePath": "",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"paths": [],
"definitions": {},
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "query"
}
},
"info": {
"version": "1",
"title": "Restler API Explorer",
"description": "Live API Documentation",
"contact": {
"name": "Restler Support",
"email": "[email protected]",
"url": "luracast.com/products/restler"
},
"license": {
"name": "LGPL-2.1",
"url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"
}
}
} location of json: and the endpoint lives at: We are NOT symlinking. As those would not survive git correctly so that is not an option. We have an internal php router that simply does: run the class with the code above in previous posting. In my opinion I am now poluting restler routes with endpoint-v2/rest/ as all restler routes will start with that. And the baseUrl is in fact the hostname with port/http scheme... But in my humble opinion a baseUrl is more then a hostname+scheme. The baseUrl is: https://tim.dev.tool.nl/endpoint-v2/rest/ and so the routes are clean. (And we need the explorer for our customers! ;-)) |
also with my fix the explorer is correctly populated with the correct info, so you can also use an external swagger: {
"swagger": "2.0",
"host": "tim.dev.tool.nl",
"basePath": "/endpoint-v2/rest",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"paths": {
"/say/hello": {
"post": {
"operationId": "sayHello",
"tags": [
"say"
],
"parameters": [
{
"name": "sayHelloModel",
"description": "**session** (required) \nto \n",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/sayHelloModel"
}
}
],
"summary": "hello 🔐",
"description": "",
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "string"
}
}
},
"security": [
{
"api_key": []
}
]
}
},
"/say/hi": {
"get": {
"operationId": "sayHi",
"tags": [
"say"
],
"parameters": [
{
"name": "to",
"type": "string",
"description": "",
"in": "query",
"required": true
}
],
"summary": "hi ◑",
"description": "",
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "string"
}
}
},
"security": [
{
"api_key": []
}
]
}
},
"/auth/inloggen": {
"post": {
"operationId": "authCreateInloggen",
"tags": [
"auth"
],
"parameters": [
{
"name": "authCreateInloggenModel",
"description": "**username** (required) \n**password** (required) \n",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/authCreateInloggenModel"
}
}
],
"summary": "Functie om in te loggen op de webservice 🔓",
"description": "",
"responses": {
"200": {
"description": "$session",
"schema": {
"type": "string"
}
}
}
}
}
},
"definitions": {
"sayHelloModel": {
"properties": {
"session": {
"type": "string",
"description": ""
},
"to": {
"type": "string",
"description": "",
"defaultValue": "world"
}
},
"required": [
"session"
]
},
"authCreateInloggenModel": {
"properties": {
"username": {
"type": "string",
"description": ""
},
"password": {
"type": "string",
"description": ""
}
},
"required": [
"username",
"password"
]
}
},
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "query"
}
},
"info": {
"version": "1",
"title": "API Explorer",
"description": "Live API Documentation",
"contact": {
"name": "tool.nl",
"email": "[email protected]",
"url": "https://tim.dev.tool.nl"
},
"license": {
"name": "",
"url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"
}
}
} as you can see the basePath is correctly filled. I can even execute from the external swagger to our server. As the explorer and swagger are correctly populated, I am more certain that our fix is the correct one. |
Hi Arul, Can you please make the suggested change and merge this? I hate it when we are on p[roduction with an temparly forked version because of an merge that is stall |
Hi Arul, Sorry for the ping, but had you time to check this merge? |
I have noticed the baseUrl doens't work as I would expect.
If baseUrl is /endpoint/rest/ and the explorer lives in "explorer" (so with path /endpoint/rest/explorer) the route can't be found, because it will do in router.php
/endpoint/rest/explorer==explorer
so it can't be found.
If you remove the baseUrl from the path it will work
path /endpoint/rest/explorer wil be cleaned to explorer
in in router.php
explorer==explorer