Hello, I'm developing a RESTful API and I need to enable my API to accept request from different domain. So, I need to enable Cross Origin Resource Sharing. Anybody know how can I do that?
Thanks
|
Oct '17 |
10 |
9468 |
3 |
Hello, I'm developing a RESTful API and I need to enable my API to accept request from different domain. So, I need to enable Cross Origin Resource Sharing. Anybody know how can I do that?
Thanks
Hi, I found the solution.
First you need to define some headers to your method:
$response = $app->response;
$response->setHeader('Access-Control-Allow-Origin', '*');
$response->setHeader('Access-Control-Allow-Headers', 'X-Requested-With');
$response->sendHeaders();
And you need to create a method to preflight. Like that:
$app->get('/preflight', function() use ($app) {
$content_type = 'application/json';
$status = 200;
$description = 'OK';
$response = $app->response;
$status_header = 'HTTP/1.1 ' . $status . ' ' . $description;
$response->setRawHeader($status_header);
$response->setStatusCode($status, $description);
$response->setContentType($content_type, 'UTF-8');
$response->setHeader('Access-Control-Allow-Origin', '*');
$response->setHeader('Access-Control-Allow-Headers', 'X-Requested-With');
$response->setHeader("Access-Control-Allow-Headers: Authorization");
$response->setHeader('Content-type: ' . $content_type);
$response->sendHeaders();
});
I have a basic API project that handles this: https://github.com/cmoore4/phalcon-rest
Check out controllers/RESTController.php. See the optionsBase and optionsOne functions.
Then all you need to do is define a route: $app->options('/', 'optionsBase'); See the index.php file for examples.
Preflight is handled via OPTIONS in most browsers.
Hi guys, here is how to handle it:
$app->before(function() use ($app) {
$origin = $app->request->getHeader("ORIGIN") ? $app->request->getHeader("ORIGIN") : '*';
$app->response->setHeader("Access-Control-Allow-Origin", $origin)
->setHeader("Access-Control-Allow-Methods", 'GET,PUT,POST,DELETE,OPTIONS')
->setHeader("Access-Control-Allow-Headers", 'Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type, Authorization')
->setHeader("Access-Control-Allow-Credentials", true);
});
$app->options('/{catch:(.*)}', function() use ($app) {
$app->response->setStatusCode(200, "OK")->send();
});
The ->before() block is required if your API is consummed by client-side JS (Angular, EmberJS...) and is executed ONLY if a route has been matched, that's why you need the ->options() catch-all block.
@Pierre your method works only with Phalcon\Mvc\Micro because Phalcon\Mvc\Application class doesn't have options() and before() methods.
Just in case it results useful for someone else I publish here my plugin:
use Phalcon\Events\Event;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\User\Plugin;
class CORSPlugin extends Plugin {
public function beforeHandleRoute(Event $event, Micro $app) {
$origin = $app->request->getHeader('ORIGIN') ? $app->request->getHeader('ORIGIN') : '*';
if (strtoupper($app->request->getMethod()) == 'OPTIONS') {
$app->response
->setHeader('Access-Control-Allow-Origin', $origin)
->setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
->setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type, Authorization')
->setHeader('Access-Control-Allow-Credentials', 'true');
$app->response->setStatusCode(200, 'OK')->send();
exit;
}
$app->response
->setHeader('Access-Control-Allow-Origin', $origin)
->setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
->setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type, Authorization')
->setHeader('Access-Control-Allow-Credentials', 'true');
}
}
Then when defining the Micro app:
$di->set('cors', function() {
return new CORSPlugin;
}, true);
$em = new EventsManager;
$em->attach('micro:beforeHandleRoute', $di->get('cors'));
$app = new Micro($di);
$app->setEventsManager($em);
Edited: It was the Access-Control-Allow-Origin that was wrong. Sorry
Hi all,
I using the @Pierre solution. It works well, but after the 200 OK response to the options my app does not manage the POST. Any clue?
I have added a bit more to the class in-case anyone else wants cors and preflight in their apps.
I am not using micro on this project so this on an application.
I created a listener class that extends Phalcon\Di\Injectable
<?php
namespace RealWorld\Listener;
use Phalcon\Events\Event;
use Phalcon\Di\Injectable;
use Phalcon\Http\Request;
use Phalcon\Http\Response;
use Phalcon\Mvc\Dispatcher;
/**
* Class PreFlightListener
* @package RealWorld\Listener
* @property Request $request
* @property Response $response
*/
class PreFlightListener extends Injectable
{
/**
* @param Event $event
* @param Dispatcher $dispatcher
*/
public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher) {
$di = $dispatcher->getDI();
$request = $di->get('request');
$response = $di->get('response');
if ($this->isCorsRequest($request)) {
$response
->setHeader('Access-Control-Allow-Origin', $this->getOrigin($request))
->setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE')
->setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type, Authorization')
->setHeader('Access-Control-Allow-Credentials', 'true');
}
if ($this->isPreflightRequest($request)) {
$response->setStatusCode(200, 'OK')->send(); exit;
}
}
/**
* @param Request $request
* @return bool
*/
public function isCorsRequest(Request $request)
{
return !empty($request->getHeader('Origin')) && !$this->isSameHost($request);
}
/**
* @param Request $request
* @return bool
*/
public function isPreflightRequest(Request $request)
{
return $this->isCorsRequest($request)
&& $request->getMethod() === 'OPTIONS'
&& !empty($request->getHeader('Access-Control-Request-Method'));
}
/**
* @param Request $request
* @return bool
*/
public function isSameHost(Request $request)
{
return $request->getHeader('Origin') === $this->getSchemeAndHttpHost($request);
}
/**
* @param Request $request
* @return string
*/
public function getSchemeAndHttpHost(Request $request)
{
return $request->getScheme() . '://' . $request->getHttpHost();
}
/**
* @param Request $request
* @return string
*/
public function getOrigin(Request $request)
{
return $request->getHeader('Origin') ? $request->getHeader('Origin') : '*';
}
}
Then in services.php
I registered the preflight service:
$di->set('preflight', function() {
return new PreFlightListener();
}, true);
I then attached the class to an event in the disptacher service:
$di->setShared(
"dispatcher",
function () use ($di) {
$eventsManager = new Manager();
// Attach a listener
$eventsManager->attach("dispatch:beforeExecuteRoute", $di->get('preflight'));
$dispatcher = new Dispatcher();
$dispatcher->setEventsManager($eventsManager);
$dispatcher->setDefaultNamespace("RealWorld\\Controllers");
return $dispatcher;
}
);
I don't like the exit in the preflight check but not sure how to send back the headers without an error occurring.
Im using @Pierre solution
I still get CORS header ‘Access-Control-Allow-Origin’ missing when doing $this->crypt->decryptBase64