FriendsOfSymfony / FOSRestBundle

This Bundle provides various tools to rapidly develop RESTful API's with Symfony
http://symfony.com/doc/master/bundles/FOSRestBundle/index.html
MIT License
2.79k stars 702 forks source link

how to use uri versioning with a controller implementing the ClassResourceInterface? #1529

Closed roelleor closed 6 years ago

roelleor commented 8 years ago

It doesn't seem possible to use uri versioning (/v1/users) with a controller implementing the ClassResourceInterface, is that correct? Or am I missing something? route:

users:
    type:     rest
    resource: AppBundle\Controller\UserController
    #prefix: {version} // doesn't work
GuilhemN commented 8 years ago

This should work as expected.

At first, you must uncomment the line prefix. And do you load this controller somewhere else ?

roelleor commented 8 years ago

Nope, not loading the controller somewhere else. Good that you're checking, but I used it uncommented :) My config:

fos_rest:
    routing_loader:
        default_format: json
    format_listener:
        enabled: true
    versioning:
        enabled: true
    view:
        view_response_listener: force
        formats:
            json: true
            jsonp: false
            xml: false
            rss: false
roelleor commented 8 years ago

to add: it does work well when using the version as a parameter

On 01 Jul 2016, at 13:16, Guilhem N notifications@github.com wrote:

This should works as expected. At first, you must uncomment the line prefix. Do you load this controller somewhere else ?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/FriendsOfSymfony/FOSRestBundle/issues/1529#issuecomment-229922098, or mute the thread https://github.com/notifications/unsubscribe/AC-MigxR-7lA68BOZ2SwS9EUIRUMsL1Tks5qRPb2gaJpZM4JC-xX.

GuilhemN commented 8 years ago

In fact, you don't need the versioning feature of this bundle if you only use a path parameter. What do you mean by not working ? You can't access your controller ?

roelleor commented 8 years ago

it gives a "No route found” error Perhaps you are right that one doesn’t strictly need it. One could just hard code the prefix. It’s not that much work, I’ll just go for that route then. FYI, this approach of having {version} as a parameter in the path is in the documentation. However, not in combination with the ClassResourceInterface.

On 01 Jul 2016, at 14:04, Guilhem N notifications@github.com wrote:

In fact, you don't need the versioning feature of this bundle if you use a path parameter. What do you mean by not working ? You can't access your controller ?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/FriendsOfSymfony/FOSRestBundle/issues/1529#issuecomment-229929837, or mute the thread https://github.com/notifications/unsubscribe/AC-MiuOvatI-4vVa0df_AtKKDW4ZtKH_ks5qRQJQgaJpZM4JC-xX.

tetele commented 8 years ago

Same issue here

# app/config/routing.yml
api:
    resource: "@ApiBundle/Resources/config/routes.yml"
    type:     rest
    prefix:   /api/{version}

and then

# src/ApiBundle/Resources/config/routes.yml
products:
    type:     rest
    resource: ApiBundle\Controller\ProductController
    name_prefix:  api_

shops:
    type:     rest
    resource: ApiBundle\Controller\ShopController
    name_prefix:  api_

When trying to access /api/v1/shops, a "no route found" exception is thrown. Note that this stopped working after I upgraded from 2.0.0-BETA to 2.0.0. Not to mention it's in the docs, like @roelleor said.

Lumbendil commented 8 years ago

Bumping this issue, as it's happening to me aswell, same case as @tetele

GuilhemN commented 8 years ago

Can you try finding the corresponding route with bin/console debug:router ?

Lumbendil commented 8 years ago

@Ener-Getick

vagrant@mymachine:/vagrant$ app/console debug:router get_categories
+--------------+--------------------------------------------------------------------+
| Property     | Value                                                              |
+--------------+--------------------------------------------------------------------+
| Route Name   | get_categories                                                     |
| Path         | /frontend/api/{tube}/{version}/categories                          |
| Path Regex   | #^/frontend/api/(?P<tube>[^/]++)/(?P<version>[^/]++)/categories$#s |
| Host         | ANY                                                                |
| Host Regex   |                                                                    |
| Scheme       | ANY                                                                |
| Method       | GET                                                                |
| Requirements | NO CUSTOM                                                          |
| Class        | Symfony\Component\Routing\Route                                    |
| Defaults     | _controller: ApiBundle:Frontend\V1\Category:cget                   |
|              | _format: json                                                      |
| Options      | compiler_class: Symfony\Component\Routing\RouteCompiler            |
+--------------+--------------------------------------------------------------------+

Should be noted aswell that in the profiler the symfony matcher complains about attributes.get('version') not being equal to v1 (my version).

xabbuh commented 8 years ago

@Lumbendil What is the exact path that you are trying? What output do you get when using the router:match command with that path?

Lumbendil commented 8 years ago

@xabbuh answer from memory.

The path I tried was /frontend/api/something/v1/categories, there was no match, and on symfony debug, when inspecting, it said something along "get_categories" route is a close match but version doesn't match the requirements (the route version was being overriden by the listener).

Lumbendil commented 8 years ago

It should be noted that adding ?version=v1 did make the route match.

ghost commented 7 years ago

I am sorry to bump this issue, but was this ever resolved or a hotfix/bypass found? I'm currently running into this problem myself, well the prefix part at least.

Observation: The reason this seems to go wrong because the Request given to match-function of the RedirectableUrlMatcher-class does not contain any attributes.

Reproduce: I tested this by accessing a route like /api/{version}/emails where {version} is v1 and dumped the incoming request in appDevDebugProjectContainerUrlMatcher. The request contained no attributes, request or query values, thus making the matching of a route with a prefix impossible.

Strange behaviour: When the Request is passed to one of my controllers, $request->attributes->get('version') does return v1, which seems to suggest that the URL hasn't been fully parsed when it's given to the class extending RedirectableUrlMatcher.

xabbuh commented 7 years ago

If someone of you could provide a small project that makes it possible to reproduce your issue, we could look into where the issue actually lies and try to solve it. That would be great. :)

ghost commented 7 years ago

@xabbuh I'll get a small example up and running after lunch. ;)

ghost commented 7 years ago

@xabbuh Here you go: https://github.com/hipio/FosRestBundleFoc



0 === strpos($pathinfo, '/api') && preg_match('#^/api/(?P[^/]++)/test(?:\.(?P<_format>json|xml|html))?$#s', $pathinfo, $matches)

Seems to work fine so it really is in_array($request->attributes->get("version"), array(0 => "v1")) that fails in appDevDebugProjectContainerUrlMatcher. Which brings me back to my previous comment stating that somehow the URL isn't fully parsed (if that makes sense) when it's passed to appDevDebugProjectContainerUrlMatcher.


I hope this helps a bit. When you find the issue, could you let me know?

juillerat commented 7 years ago

The problem is linked with priority of kernel.request event listeners

To resolve all these problems at the same time, I think we need to:

I will propose a PR that fix issues #1491 and #1529

crashev commented 7 years ago

Are there any workarounds for URI versioning? It still seems not to work, only query and header versioning works

osavchenko commented 6 years ago

Yes, still not working URI versioning. Why it happens and how I can help to fix this (maybe, we need new resolver for this)?

lgraubner commented 6 years ago

You can simply add your own onKernelRequest listener to work around the problem described by @juillerat.

namespace AppBundle\EventListener;

use FOS\RestBundle\View\ConfigurableViewHandlerInterface;
use FOS\RestBundle\View\ViewHandlerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class VersionListener
{
    private $viewHandler;

    public function __construct(ViewHandlerInterface $viewHandler)
    {
        $this->viewHandler = $viewHandler;
    }

    private function resolveVersion(Request $request)
    {
        $version = $request->attributes->get('version');

        return is_scalar($version) ? $version : strval($version);
    }

    public function onKernelRequest(GetResponseEvent $event)
    {

        $request = $event->getRequest();
        $version = str_replace('v', '', $this->resolveVersion($request));

        $request->attributes->set('version', $version);

        if ($this->viewHandler instanceof ConfigurableViewHandlerInterface) {
            $this->viewHandler->setExclusionStrategyVersion($version);
        }
    }
}
app.version_listener:
        class: Acme\AppBundle\EventListener\VersionListener
        tags:
          - { name: kernel.event_listener, event: kernel.request, priority: 30 }
bilel-noomene commented 6 years ago

+1

mhightower commented 6 years ago

Was this problem ever resolved? I'm running into the same issue as @roelleor. I'm going to try the workaround but would like to configure it as documented.

guilemos commented 6 years ago

+1

yeomi commented 6 years ago

+1

a-menshchikov commented 6 years ago

+1

GuilhemN commented 6 years ago

Fixed in https://github.com/FriendsOfSymfony/FOSRestBundle/pull/1937 🎉