We have a frontend portal on React JS, we created a Lumen API for some request, the problem is that if we use Axios, Fetch always send the preflight OPTIONS to our API, we modify the Middleware for that issue becasue always we get the "405 Method Not Allowed", the code is below:
bootstrap/app.php:
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
'cors' => App\Http\Middleware\CorsMiddleware::class
]);
The middleware on the app :
<?php
namespace App\Http\Middleware;
use Closure;
class CorsMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$response->header('Access-Control-Allow-Origin','*');
return $response;
}
}
Our we set the middleware cors routes/web.php:
$app->group(['middleware' => 'cors'], function($app)
{
$app->post('/data/reset/ function() {
return response()->json([
'message' => 'CORS Ok!',
]);
});
});
On our Rest Clients all work fine, but on our request from React app:
axios.post('our_url', {
preview: '1'
seed: 'data_1'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
When the POST execute a Pre-flight OPTIONS is sended and the response from Lumen is:
405 Method Not Allowed
Response Headers:
HTTP/1.1 405 Method Not Allowed
Access-Control-Allow-Headers: content-type, origin, x-requested-with, content-type, origin, x-requested-with, content-type
Access-Control-Allow-Methods: POST, PUT, GET, POST, DELETE, OPTIONS, PUT, GET, POST, DELETE, OPTIONS
Access-Control-Allow-Origin: *, *, *
Allow: POST
Cache-Control: no-cache, private
Content-Type: text/html; charset=UTF-8
Date: Sun, 29 Oct 2017 19:48:24 GMT
Server: Apache/2.4.18 (Ubuntu)
transfer-encoding: chunked
Connection: keep-alive
What we are missing?
Regards.
My app runs successfully.
<?php
namespace App\Http\Middleware;
use Closure;
class CorsMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
//Intercepts OPTIONS requests
if ($request->isMethod('OPTIONS')) {
$response = response('', 200);
} else {
// Pass the request to the next middleware
$response = $next($request);
}
// Adds headers to the response
$response->header('Access-Control-Allow-Methods', 'HEAD, GET, POST, PUT, PATCH, DELETE');
$response->header('Access-Control-Allow-Headers', $request->header('Access-Control-Request-Headers'));
$response->header('Access-Control-Allow-Origin', '*');
$response->header('Access-Control-Expose-Headers', 'Location');
// Sends it
return $response;
}
}
Check it out!
I'm having the same issue of @ulisescarreonalvarez ... I have the front end separated from the lumen in diferente servers...
Anyone fixed it?
https://github.com/barryvdh/laravel-cors
https://github.com/abellion/laravel-cors
The latest is lightweight but may not cover 100% use cases.
@rafadpedrosa Did you try the middleware?
My CORS works nice. Pay attention for... Cara, 茅 s贸 usar o middleware disponibilizado acima. Funciona sem problemas.
sry for long post
Hi @hakuno! Yeah, i did... Been honest? I'v use many of then.
Anyway! I solve the problem last weekend. I don't find the link for the github thread that help me, sry... But i put an explanation to solve it if there is anyone with the same problem.
---- Down here has an explanation. The problem and solution. ----
I try many middleware - like i said - and they work fine! But i wasn't understand the exception and message error that i was getting from lumen (MethodNotAllowed) and from the browser (cors blah blah blah).
After read about cors, about the lumen exception and ask about it in 3 threads in github, i solve it!
THE PROBLEM:
I'm using axios + vuejs + lumen. When axios send a request to the server, it sends a request type OPTION and another POST/PUT/GET/DELETE depending on what you are requesting. That said, Lumen wasn't finding the route OPTIONS and was throwing the exeption MethodNotAllowed. I dunno why but it's very especific from axios... Because when i try by "adhoc mode" with Postman, ARC and javascript vanilla it was working fine.
THE SOLUTION:
$app->routeMiddleware([
'cors' => \Barryvdh\Cors\HandleCors::class, // THIS ONE!
]);
$app->register(Barryvdh\Cors\ServiceProvider::class);// cors problem....
Route::options(
'/{any:.*}',
[
'middleware' => ['cors'],
function (){
return response(['status' => 'success']);
}
]
);
OBS: like i said, the exception was been throwed because of the absence of OPTIONS route.That's why this code save my life. You can debug the lumen to see it (RoutesRequests.php)
Tanks for the help and sry for the long post again :-)
@rafadpedrosa
It's named preflight request.
And you might make it as follows
$app->middleware([
App\Http\Middleware\CorsMiddleware::class
]);
With no need of creating options route like that. In fact, your solution seems a workaround ("gambiarra") only.
But, all right. Let's code.
Hi everyone, i solved this with the nexcode, obviously if you need the routes only for one of them you need to do the next:
And yes is called "preflight request" but only with the middleware will not work because you need to set the params of the header on the CorsMiddleware, maybe it help:
bootstrapp/app.php
<?php
require_once __DIR__.'/../vendor/autoload.php';
try {
(new Dotenv\Dotenv(__DIR__.'/../'))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
//
}
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| Here we will load the environment and create the application instance
| that serves as the central piece of this framework. We'll use this
| application as an "IoC" container and router for this framework.
|
*/
$app = new Laravel\Lumen\Application(
realpath(__DIR__.'/../')
);
$app->withFacades();
$app->withEloquent();
/*
|--------------------------------------------------------------------------
| Register Container Bindings
|--------------------------------------------------------------------------
|
| Now we will register a few bindings in the service container. We will
| register the exception handler and the console kernel. You may add
| your own bindings here if you like or you can make another file.
|
*/
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
/*
|--------------------------------------------------------------------------
| Register Middleware
|--------------------------------------------------------------------------
|
| Next, we will register the middleware with the application. These can
| be global middleware that run before and after each request into a
| route or middleware that'll be assigned to some specific routes.
|
*/
// $app->middleware([
// App\Http\Middleware\ExampleMiddleware::class
// ]);
$app->routeMiddleware([
'cors' => App\Http\Middleware\CorsMiddleware::class,
'auth' => App\Http\Middleware\Authenticate::class,
]);
/*
|--------------------------------------------------------------------------
| Register Service Providers
|--------------------------------------------------------------------------
|
| Here we will register all of the application's service providers which
| are used to bind services into the container. Service providers are
| totally optional, so you are not required to uncomment this line.
|
*/
// $app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
// $app->register(App\Providers\EventServiceProvider::class);
$app->register(Appzcoder\LumenRoutesList\RoutesCommandServiceProvider::class);
$app->register(\Tymon\JWTAuth\Providers\LumenServiceProvider::class);
/*
|--------------------------------------------------------------------------
| Load The Application Routes
|--------------------------------------------------------------------------
|
| Next we will include the routes file so that they can all be added to
| the application. This will provide all of the URLs the application
| can respond to, as well as the controllers that may handle them.
|
*/
$app->router->group([
'namespace' => 'App\Http\Controllers',
], function ($router) {
require __DIR__.'/../routes/web.php';
});
return $app;
Middleware/CorsMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Response;
class CorsMiddleware
{
protected $settings = array(
'origin' => '*', // Wide Open!
'allowMethods' => 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
);
protected function setOrigin($req, $rsp) {
$origin = $this->settings['origin'];
if (is_callable($origin)) {
// Call origin callback with request origin
$origin = call_user_func($origin,
$req->header("Origin")
);
}
$rsp->header('Access-Control-Allow-Origin', $origin);
}
protected function setExposeHeaders($req, $rsp) {
if (isset($this->settings['exposeHeaders'])) {
$exposeHeaders = $this->settings['exposeHeaders'];
if (is_array($exposeHeaders)) {
$exposeHeaders = implode(", ", $exposeHeaders);
}
$rsp->header('Access-Control-Expose-Headers', $exposeHeaders);
}
}
protected function setMaxAge($req, $rsp) {
if (isset($this->settings['maxAge'])) {
$rsp->header('Access-Control-Max-Age', $this->settings['maxAge']);
}
}
protected function setAllowCredentials($req, $rsp) {
if (isset($this->settings['allowCredentials']) && $this->settings['allowCredentials'] === True) {
$rsp->header('Access-Control-Allow-Credentials', 'true');
}
}
protected function setAllowMethods($req, $rsp) {
if (isset($this->settings['allowMethods'])) {
$allowMethods = $this->settings['allowMethods'];
if (is_array($allowMethods)) {
$allowMethods = implode(", ", $allowMethods);
}
$rsp->header('Access-Control-Allow-Methods', $allowMethods);
}
}
protected function setAllowHeaders($req, $rsp) {
if (isset($this->settings['allowHeaders'])) {
$allowHeaders = $this->settings['allowHeaders'];
if (is_array($allowHeaders)) {
$allowHeaders = implode(", ", $allowHeaders);
}
}
else { // Otherwise, use request headers
$allowHeaders = $req->header("Access-Control-Request-Headers");
}
if (isset($allowHeaders)) {
$rsp->header('Access-Control-Allow-Headers', $allowHeaders);
}
}
protected function setCorsHeaders($req, $rsp) {
// http://www.html5rocks.com/static/images/cors_server_flowchart.png
// Pre-flight
if ($req->isMethod('OPTIONS')) {
$this->setOrigin($req, $rsp);
$this->setMaxAge($req, $rsp);
$this->setAllowCredentials($req, $rsp);
$this->setAllowMethods($req, $rsp);
$this->setAllowHeaders($req, $rsp);
}
else {
$this->setOrigin($req, $rsp);
$this->setExposeHeaders($req, $rsp);
$this->setAllowCredentials($req, $rsp);
}
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next) {
if ($request->isMethod('OPTIONS')) {
$response = new Response("", 200);
}
else {
$response = $next($request);
}
$this->setCorsHeaders($request, $response);
return $response;
}
}
routes/web.php
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It is a breeze. Simply tell Lumen the URIs it should respond to
| and give it the Closure to call when that URI is requested.
|
*/
$router->post('/', function () use ($router) {
return ["Version" => "1.0"];
});
$router->options(
'/{any:.*}',
[
'middleware' => ['cors'],
function (){
return response(['status' => 'success']);
}
]
);
$router->group(['middleware' => 'cors'], function($router){
$router->post('/auth/login', function() {
return response()->json([
'message' => 'CORS OPTIONS Accepted.',
]);
});
$router->post('/auth/promise/', 'AuthController@postLogin');
$router->get('/clients/', 'ClientsController@getAll');
});
I hope it works for you.
@rafadpedrosa
@ulisescarreonalvarez You should close this thread. It is solved and... isn't a bug.
First of all, thx @hakuno and @ulisescarreonalvarez !
@hakuno, i know that i wrote a long post but, as i said, i didn't find the links saying that it was a very specific issue. To clarify - if you have time - read this and that. This issues helped me to undestand the specific problem that i had. And this threads, like others that i saw before, had explanations of what is prelift. But thx any way.... :)
Still, i tried to find the last url saying to do what i've done (and what @ulisescarreonalvarez post). I don't find it, sry. Again, it's a specific problem using axios + lumen 5.4^ + cors browsing. I try hard to find the third link because it had the solution and - i'm not complete sure about it - but it was validated by some contributor (like you). I remmember that one of this 2 threads had the guy who solve this problem using what i said.
@ulisescarreonalvarez yep! That's was exactly what i did! :)
Do you remember where you find this solution? I saw it in a stackoverflow and in some lumen thread...
:(
ps: Yes, it apperas to be an workaround/"gabiarra" hehehe. But it's not mine and i think it's a good one... I'm not the one using this :P
i can create a public repo with the solution, just give a couple days and i will send you a message :) @rafadpedrosa
@ulisescarreonalvarez great :)
Maybe it can help others 馃憤
i just had this issue using Axios with this package's version (0.10.1). Everything was set as default as in the package's document except that the global middleware was applied after another middleware which converted the response into a standard response type for my Api.
This failed:
$app->middleware([
App\Http\Middleware\Custom\APItoJSON::class,
\Barryvdh\Cors\HandleCors::class,
])
This worked:
$app->middleware([
\Barryvdh\Cors\HandleCors::class,
App\Http\Middleware\Custom\APItoJSON::class,
)]
I'm having the same issue using route loaded CorsMiddleware. It will only work as global middleware.
Indeed!
It will only work as global middleware.
Most helpful comment
Hi everyone, i solved this with the nexcode, obviously if you need the routes only for one of them you need to do the next:
And yes is called "preflight request" but only with the middleware will not work because you need to set the params of the header on the CorsMiddleware, maybe it help:
bootstrapp/app.php
Middleware/CorsMiddleware.php
routes/web.php
I hope it works for you.
@rafadpedrosa