Framework: Route Class doesn't not support "OPTIONS" method

Created on 27 Feb 2013  Â·  18Comments  Â·  Source: laravel/framework

I'm using enyojs to send POST request via Ajax. Enyojs always sends OPTIONS request first to web service and it always failed because the route class doesn't support OPTIONS method

Most helpful comment

Here is what I used, since I got an error when I tried to use callback functions in cached routes:

Routes.php

Route::options('{all}', 'HomeController@options')->where('all', '.*');

HomeController.php

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

class HomeController extends Controller
{
    public function options(Request $request, Response $response)
    {
        $origin = $request->header('origin') ?: $request->url();

        $response->header('Access-Control-Allow-Origin', $origin);
        $response->header('Access-Control-Allow-Headers', 'origin, content-type, accept');
        $response->header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS, POST, PATCH, DELETE');

        return $response;
    }
}

All 18 comments

The browser sends OPTIONS requests to determine the cross-ajax eligibility among other things. It just needs to return a 200 response. Here's how I handled it:

App::before(function($request)
{
    // Sent by the browser since request come in as cross-site AJAX
    // The cross-site headers are sent via .htaccess
    if ($request->getMethod() == "OPTIONS")
        return new SuccessResponse();
});

And to send the cross-ajax headers, I added this to my BaseResponse class (which is the parent of SuccessResponse):

class BaseResponse extends \Illuminate\Http\Response
{
    public function __construct($content = '', $status = 200, $headers = array())
    {
        $headers['Access-Control-Allow-Origin'] = '*';
        $headers['Access-Control-Allow-Methods'] = 'GET, PUT, POST, DELETE, HEAD, OPTIONS';
        $headers['Access-Control-Allow-Headers'] = 'X-Requested-With, Origin, X-Csrftoken, Content-Type, Accept';

        parent::__construct($content, $status, $headers);
    }
}

Thanks @bunchjesse for your solution, but I think it would be nice if there would be a more "built in" solution to this issues. This would also solve a lot of CORS problems with Laravel :+1:

The most straight forward solution would be (in my opinion) a Route::options() method.

I'm not really familiar with this stuff, but can easily add a Route::options method. What else do you guys need to make your lives easier with this stuff?

Taylor,

I think that would be a nice addition and would remove the need for my code in the before event. 
—
Sent from my iPhone

On Wed, Mar 13, 2013 at 7:44 PM, Taylor Otwell [email protected]
wrote:

I'm not really familiar with this stuff, but can easily add a Route::options method. What else do you guys need to make your lives easier with this stuff?

Reply to this email directly or view it on GitHub:
https://github.com/laravel/framework/issues/447#issuecomment-14877866

It's like that: If you do a cross domain request (even with subdomains) it's a security policy to check if the server "accepts" this origin. Therefore the browser sends before the acutal PUT/POST/... request an so called OPTION request. If the response to that request was ok, the PUT/... request will be executed.

What else do you guys need to make your lives easier with this stuff?

Actually not more than for other request methods.

So where does the OPTIONS request get sent to? The root of the domain,
or the URI you are trying to access?

To the URI you are trying to access.

Done. Route::options is now available.

Awesome! I'll try it out right now :+1:

Edit: Works great!

schickling, how did you set up your pattern for Route::options? I tried setting pattern to match the same path as the Route::get however, I never get a match.

@Comanche I used a "wider" regex rule since want the options route to match a bunch of GET/POST/... routes.

But currently I use a reverse proxy instead of the OPTIONS routes, since sometimes they don't seem to work properly.

Could we get some documentation on usage of Route::options? I'm guessing it looks something like Route::options('*', array('Access-Control-Allow-Methods' => 'GET, PUT, POST, DELETE, HEAD, OPTIONS') );

I tried guessing different parameters with no luck sadly.

I believe it's something like this:

Route::options('*', function () {
    $response = Response::make('');
    $response->header('Access-Control-Allow-Origin', '*');
    $response->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
    $response->header('Access-Control-Allow-Headers', 'X-Requested-With');
    return $response;
});

An OPTIONS request can be called on any route ( test.com or test.com/item/1 ) before the actual request.

I'm a bit confused on how to set the URI parameter to match all routes, or if it is even actually needed. So far I've tried an array list using wildcards, various regex, null, as well as (:any)/(:all?) as suggested at Stackoverflow.

How should Route::options be used? Do we need to build it out for a bit greater support or flexibility?

Route::options('{all}', function ()
{
    // response
})
->where('all', '.*');

@TheBox193 This works for me.

We are using the following for our routes and it works for us =)

Route::options('{all}', function () {
    $response = Response::make('');

    if(!empty($_SERVER['HTTP_ORIGIN']) && in_array($_SERVER['HTTP_ORIGIN'], ['http://localhost:8090'])) {
        $response->header('Access-Control-Allow-Origin', $_SERVER['HTTP_ORIGIN']);
    } else {
        $response->header('Access-Control-Allow-Origin', url()->current());
    }
    $response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, Authorization');
    $response->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE');
    $response->header('Access-Control-Allow-Credentials', 'true');
    $response->header('X-Content-Type-Options', 'nosniff');

    return $response;
});

Here is what I used, since I got an error when I tried to use callback functions in cached routes:

Routes.php

Route::options('{all}', 'HomeController@options')->where('all', '.*');

HomeController.php

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

class HomeController extends Controller
{
    public function options(Request $request, Response $response)
    {
        $origin = $request->header('origin') ?: $request->url();

        $response->header('Access-Control-Allow-Origin', $origin);
        $response->header('Access-Control-Allow-Headers', 'origin, content-type, accept');
        $response->header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS, POST, PATCH, DELETE');

        return $response;
    }
}

Thanks for the suggestions here, these really helped
After a lot of searching this is what worked finally for me for the options request.

Route::options('{all}', function () { $response = Response::make(''); if(!empty($_SERVER['HTTP_ORIGIN']) && in_array($_SERVER['HTTP_ORIGIN'], ['http://localhost:3000'])) { $response->header('Access-Control-Allow-Origin', $_SERVER['HTTP_ORIGIN']); } else { $response->header('Access-Control-Allow-Origin', url()->current()); } $response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, Authorization'); $response->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE'); $response->header('Access-Control-Allow-Credentials', 'true'); $response->header('X-Content-Type-Options', 'nosniff'); return $response; })->where('all', '.*');

Was this page helpful?
0 / 5 - 0 ratings