nelmio / NelmioCorsBundle

Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application
https://symfony.com/bundles/NelmioCorsBundle/
MIT License
1.89k stars 108 forks source link

Allow OPTIONS method for any resource #52

Closed DmitryVarennikov closed 1 year ago

DmitryVarennikov commented 8 years ago

Is there a way to configure OPTIONS method for every request to avoid MethodNotAllowedException?

Seldaek commented 8 years ago

Sorry but I don't really understand what you're asking here. Can you explain more specifically?

DmitryVarennikov commented 8 years ago

Sure. Browser makes an OPTIONS request before the main one every time. So backend should support it and answer accordingly which is perfectly done by CorsListener except for one thing. I use FOSRestBundle where one has to explicitly specify an acceptable HTTP method for every controller method, i.e. GET, POST, etc (you may also specify it implicitly by following naming convention but this is irrelevant for this matter).

default:
    type: rest
    prefix: api
    resource: AppBundle\Controller\DefaultController
    methods:  [GET, POST, OPTIONS]

What would be nice is to skip OPTIONS configuration for each route and to have a listener which intercepts incoming OPTIONS requests and tells Symfony core not to raise MethodNotAllowedException.

Does it make sense now?

Seldaek commented 8 years ago

Sorry but no I don't get it.. what is throwing the MethodNotAllowedException? The OPTIONS request should be handled by the CorsListener and then symfony should just return that response if it's valid. The FOSRest stuff should not be called AFAIK, but if it is it sounds like maybe a listener priority bug?!

michaelfeinbier commented 8 years ago

The Bundle behaves correctly. Yes in some special cases the browser does a preflight OPTIONS Request. But within this request the clients sends the desired "real" method within an Access-Control-Request-Method: PUT Header. So the listener has to check if the desired Method is within the allow_methods: ['POST', 'PUT', 'GET', 'DELETE'] Array of _nelmio_cors:_ Configuration. It has nothing to do with the FOSRestuBundle.

In other words: In the preflight-OPTIONS Request the client asks the server "Hey, am I allowed to do a PUT-Request to your endpoint?" And the server looks up the allowed methods (PUT is allowed) and says yes! (Status 200) or no! (Status 405 - MethodNotAllowed)

Here is a nice explanation http://www.html5rocks.com/en/tutorials/cors/

DmitryVarennikov commented 8 years ago

That's the thing. If OPTIONS method isn't configured explicitly (not sure about ANY as I use FOSRestuBundle) CorsListener is never triggered instead MethodNotAllowedException is thrown somewhere before.

I'll give my example. I take advantage of implicit resource name definition and implement ClassResourceInterface in my controllers. I have to add 2 empty methods for OPTIONS requests to satisfy browser, e.g. optionAction($id) and coptionAction(). Yes they are empty as CorsListener takes care of OPTIONS requests but they still have to be in place.

Seldaek commented 8 years ago

Then that's a bug in whatever other bundle you are using, it must be registering a listener on kernel.request with a priority that is too high and makes it take priority over this bundle.

teohhanhui commented 8 years ago

The CorsListener has an extremely high priority (10000) while FosRestBundle's AllowedMethodsListener has default priority, so I don't see how that can be the case...

martinbertinat commented 8 years ago

Hi guys, I think I have the same problem.

If i put the method in my route configuration I get: 400 Method not allowed. If I remove the "Method Put" declaration it works ok.

    /**
     * @Route("/tareas/modificar", name="api_tareas_modificar")
     * @Method("PUT")
     */
mvrhov commented 8 years ago

I figured this one out. Your api is not doing it right: There is a case where CorsListener returns 405 if the Access-Control-Request-Method is missing in the request or the method on the list is not listed in allowed-methods configuration

ChristianVermeulen commented 4 years ago

@mvrhov Since version 2.0.0 the priority has been lowered to 28. Now it produces this problem again. When debugging the request listeners it looks like this:

"kernel.request" event
----------------------

 ------- ------------------------------------------------------------------------------------------------- ----------
  Order   Callable                                                                                          Priority
 ------- ------------------------------------------------------------------------------------------------- ----------
  #1      Symfony\Component\HttpKernel\EventListener\DebugHandlersListener::configure()                     2048
  #2      Symfony\Component\HttpKernel\EventListener\ValidateRequestListener::onKernelRequest()             256
  #3      Symfony\Component\HttpKernel\EventListener\SessionListener::onKernelRequest()                     128
  #4      Symfony\Component\HttpKernel\EventListener\LocaleListener::setDefaultLocale()                     100
  #5      Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest()                      32
  #6      Nelmio\CorsBundle\EventListener\CorsListener::onKernelRequest()                                   28
  #7      Symfony\Bundle\FrameworkBundle\EventListener\ResolveControllerNameSubscriber::onKernelRequest()   24
  #8      Symfony\Component\HttpKernel\EventListener\LocaleListener::onKernelRequest()                      16
  #9      Symfony\Component\HttpKernel\EventListener\LocaleAwareListener::onKernelRequest()                 15
  #10     Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener::configureLogoutUrlGenerator()      8
  #11     Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener::onKernelRequest()                  8
  #12     Sentry\SentryBundle\EventListener\RequestListener::onKernelRequest()                              1
  #13     Sentry\SentryBundle\EventListener\SubRequestListener::onKernelRequest()                           1
 ------- ------------------------------------------------------------------------------------------------- ----------

As you can see, it now ends up below the routerlistener. This means the routerlistener already captures the request and looks at the configured methods for that route.

I changed the priority to 34 instead of the current 28 and it seems to fix this problem.

Seldaek commented 1 year ago

This was fixed in 2.0.1