12
votes

I recently tries enabling CORS in Laravel 5.4 but unfortunately it doesn't want to work. I have included the code and the error that it's giving me below. Can anyone help finding out why it isn't working? I have passed the required headers.

I have renamed my domain to domain.uk just for example purposes and I don't wan't to expose the domain of my site just yet as its under development.

Routes (Made the one route ::any for testing purposes while developing, usually on production it would be post):

Route::group(['domain' => 'api.domain.uk', 'namespace' => 'Api'], function() {
    Route::group(['middleware' => ['cors'], 'prefix' => 'call'], function() {
        Route::get('/rooms/{id}/get-locked-status', 'ApiController@getRoomLockStatus');
        Route::any('/rooms/{id}/update-locked-status', 'ApiController@updateRoomLockStatus');
    });
});

Error:

XMLHttpRequest cannot load http://api.domain.uk/ajax/rooms/1/update-locked-status. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://ice.domain.uk' is therefore not allowed access. The response had HTTP status code 500.

Middleware:

namespace App\Http\Middleware;

use Closure;

class Cors
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request)
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
            ->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With, Application');
    }
}

Ajax:

function toggleDoors(roomId) {
    $.ajax({
        url: 'http://api.domain.uk/ajax/rooms/' + roomId + '/update-locked-status',
        type: "POST",
        success: function(data) {
            alert(data);
        }
    });
}

ApiController:

<?php
namespace App\Http\Controllers\Api;

use Auth;
use App\User;
use App\Http\Controllers\Controller;
use Validator;
use Redirect;
use Illuminate\Http\Request;
use App\Database\Frontend\Other\Rooms;

class ApiController extends Controller
{
    public function getRoomLockStatus($id) {
        $room = Rooms::find($id);

        if ($room == null) {
            return response('bad request', 400);
        } 
        else {
            return $room->rp_locked;
        }
    }

    public function updateRoomLockStatus(Request $request, $id) {
        $room = Rooms::find($id);

        if ($room == null) {
            return response('bad request', 400);
        } 

        $room->rp_locked = $room->rp_locked == '1' ? '0' : '1';
        $room->save();

        $responseText = $room->rp_locked == '1' ?
            'Your doors have been locked.' : 'Your doors have been unlocked.';

        return response($responseText, 200);
    }
}
9
Did you update the $routeMiddleware variable in /app/Http/Kernel.php to include the new cors middleware?user1669496
Yes I did......VoiD HD
I found a solution that allows to apply your CORS library only on a subset of endpoints (for security reason) without using any external lib: stackoverflow.com/questions/34748981/…Martin Braun

9 Answers

7
votes

See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS#Preflighted_requests_in_CORS

If your problem in OPTIONS method.

Kernel::$routeMiddleware not working in Laravel 5.4 for request method OPTIONS, see https://github.com/laravel/framework/blob/v5.4.0/src/Illuminate/Routing/RouteCollection.php#L214. For use CORS middleware, enable it in Kernel::$middleware array. It is not good, but no other way.

For example, I use next middleware class for SPA and API, attention, it is not middleware 'cors' for routes

<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

/**
 * OptionsCorsResponse middleware - add CORS headers if request method OPTIONS
 */
class OptionsCorsResponse
{
    /**
     *
     * @param Request $request
     * @param Closure $next
     * @return Response
     */
    public function handle($request, Closure $next)
    {
        /* @var $response Response */
        $response = $next($request);
        if (!$request->isMethod('OPTIONS')) {
            return $response;
        }
        $allow = $response->headers->get('Allow'); // true list of allowed methods
        if (!$allow) {
            return $response;
        }
        $headers = [
            'Access-Control-Allow-Methods' => $allow,
            'Access-Control-Max-Age' => 3600,
            'Access-Control-Allow-Headers' => 'X-Requested-With, Origin, X-Csrftoken, Content-Type, Accept',
        ];
        return $response->withHeaders($headers);
    }
}

and enable it in App\Http\Kernel

protected $middleware = [
    // ...
    \App\Http\Middleware\OptionsCorsResponse::class,
];

Origin 'http :// ice . domain . uk' is therefore not allowed access. The response had HTTP status code 500.

Debug your code, because it generate some exception. Use any REST client with OPTIONS method.

5
votes

In the CORS, browser first send the OPTIONS request to the specified route.

In CORS, a preflight request with the OPTIONS method is sent, so that the server can respond whether it is acceptable to send the request with these parameters: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS

So Change your middleware like this:

public function handle($request, Closure $next)
    {
        if ($request->isMethod('OPTIONS')){
            $response = Response::make();
        } else {
            $response = $next($request);
        }
        return $response
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
            ->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With, Application');
    }

If you want to allow other headers to your routes, please add them in the 'Access-Control-Allow-Headers' header field.

4
votes

You can do it easily by adding headers in bootstrap/app.php

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: *');
header('Access-Control-Allow-Headers: *');
1
votes

I ran into a sudden CORS issue recently that was not caused by CORS header configuration, I discovered the following:

There are Red Herring scenarios that can also cause a CORS Cross Origin error to display and yet not have anything to do with CORS configuration, It is a result of when CORS is handled by middleware and something else prevents it from being triggered.

The following can indirectly cause the error to display in a browser response:

  • A PHP error in a Middleware class.
  • return $next($request); not being fired in middleware class method handle.
  • Route::middleware in web or api router configs reference a middleware that no longer exists or is miss spelt.
  • Same as above point but middleware specified in a Controller with $this->middleware();

Any of these can prevent a Cors middleware from ever being fired because the app exits too early and never sets the headers and thus results in a CORS error instead of a 500 Server Header error as a result of bad middleware files or bad references to middleware.

If you are certain you have configured CORS correctly then you should check your PHP error logs for Middleware errors.

1
votes

I am using Laravel 6 and up. This url helped me in solving my CORS issue: https://medium.com/@petehouston/allow-cors-in-laravel-2b574c51d0c1

Use this code instead of code in the url:

<?php

namespace App\Http\Middleware;

use Closure;

class Cors
{
    public function handle($request, Closure $next)
    {

          return $next($request)
              ->header('Access-Control-Allow-Origin', '*')
              ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
              ->header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type,X-Token-Auth, Authorization');
    }
}

Also, if you want to use middleware through the entire application then you need to make changes in Kernel.php:

protected $middleware = [
\App\Http\Middleware\Cors::class, //add this line to $middleware variable
]
1
votes

If none of this working, add cors on apache virtual host configuration (If you use virtual host).

Go to /etc/apache2/sites-available and add something like this gist

then sudo a2ensite example.conf and sudo service apache2 reload ;)

0
votes

I had a problem handling files using the withHeaders() method, so thanks to the tips below i came up with this working code:

/**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->isMethod('OPTIONS'))
        {
            return response()->json('{"method":"OPTIONS"}', 200, $headers);
        }

        $response = $next($request);
        $response->headers->set('Access-Control-Expose-Headers', 'Content-Disposition');
        $response->headers->set('Access-Control-Allow-Origin', 'http://localhost:8080','http://localhost','https://edu.pilateswien.org');
        $response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');

        //return $response->withHeaders($headers);
        return $response;
    }
0
votes

Sergei is right, the problem is caused because of the preflight request: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS#Preflighted_requests_in_CORS

Thus adding a middleware only to a group of endpoints won't work, because the preflight is using the OPTION method and not the GET method.

This package solves exactly this issue, by having a middleware that is put in your kernel for all routes, but then you filter the routes where you want to allow CORS in config/cors.php. Thus you can also handle the preflight request that come with the option method.

In short, install the package:

composer require fruitcake/laravel-cors

put the middleware in your middleware array:

protected $middleware = [
  \Fruitcake\Cors\HandleCors::class,
    // ...
];

publish the config file:

php artisan vendor:publish --tag="cors"

And specify in paths inside config/cors which routes (or only a single route) you want to allow:

'paths' => ['api/*'],

See also this blog post for more.

-2
votes

Just add this code on your routes

header('Access-Control-Allow-Origin: http://yourdomain.com/');