Symfony 2 - Allow OPTIONS requests

7.4k views Asked by At

I have a symfony 2 backend and I have installed it on a server. My frontend is an ionic PWA, so it runs in browser and it is also installed on that server, but with an other subdomain. When I send a request from the web app to the backend, I'm getting this error:

OPTIONS https://api.example.com/api/login.json

XMLHttpRequest cannot load https://api.example.com/api/login.json. Response for preflight has invalid HTTP status code 405

This is my code for the login action:

/**
 * Login via username and password or via API key
 *
 * @Doc\ApiDoc(
 *  section="Security",
 *  description="Login",
 *  views={"default", "security"}
 * )
 *
 * @param ParamFetcher $params
 *
 * @Rest\RequestParam(name="username", nullable=true, description="Username")
 * @Rest\RequestParam(name="password", nullable=true, description="Password")
 * @Rest\RequestParam(name="apikey", nullable=true, description="API key (alternative to username + password)")
 *
 * @Rest\Post("/login", name="api_security_login", options={"method_prefix" = false})
 *
 * @return Response
 */
public function loginAction(ParamFetcher $params)
{
    //...do some stuff here...//

    $data = array(
        'user'   => $userValue,
        'apikey' => $user->getApiKey(),
    );

    $groups = array('default', 'private');

    return $this->createAPIResponse(self::STATUS_OK, $data, $groups);
}

This is header from the response:

Access-Control-Allow-Methods:GET,POST,OPTIONS,DELETE,PUT
Access-Control-Allow-Origin:*
Allow:POST
Cache-Control:no-cache
Connection:Keep-Alive
Content-Length:54
Content-Type:application/json
Date:Tue, 29 Aug 2017 08:33:26 GMT
Keep-Alive:timeout=5, max=100
Server:Apache

This is the error message in the prod.log file:

request.ERROR: Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException: "No route found for "OPTIONS /api/login.json": Method Not Allowed (Allow: POST)" at /var/www/example.com/api/htdocs/symfony/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php line 163 {"exception":"[object] (Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException(code: 0): No route found for \"OPTIONS /api/login.json\": Method Not Allowed (Allow: POST) at /var/www/example.com/api/htdocs/symfony/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php:163, Symfony\Component\Routing\Exception\MethodNotAllowedException(code: 0): at /var/www/example.com/api/htdocs/symfony/app/cache/prod/appProdUrlMatcher.php:855)"} []

So it seems like the OPTIONS request, which is send because CORS, is not allowed, because of the "Allow" header, where only "POST" is allowed. So what is the best way to fix that for all routes?

2

There are 2 answers

8
Nicolai Fröhlich On

There is no route that accepts the options http method.

No route found for "OPTIONS /api/login.json"

You need to define it first:

/**
 * @Rest\Route(
 *   "/login", 
 *   name="api_security_login",
 *   methods = { 
 *     Request::METHOD_POST,
 *     Request::METHOD_OPTIONS,
 *   }
 * )
 */

Clear your cache afterwards and verify that the route is active i.e. using:

bin/console debug:router | grep -i api_security_login
1
Olivier 'Ölbaum' Scherler On

You only have a route on your action for the POST method:

@Rest\Post("/login", name="api_security_login", options={"method_prefix" = false})

If you want to answer OPTIONS requests, you have to define an Options route using the @Rest\Options annotation.

You can either have several annotations on the same action and test the method inside the action (typically the case with GET and POST for a form action), or define two distinct actions with the same path but different methods.

http://symfony.com/doc/master/bundles/FOSRestBundle/7-manual-route-definition.html