Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support defining routes on the actions via annotation #2059

Closed
albe opened this issue Jul 21, 2020 · 1 comment · Fixed by #3325 · May be fixed by #2421
Closed

Support defining routes on the actions via annotation #2059

albe opened this issue Jul 21, 2020 · 1 comment · Fixed by #3325 · May be fixed by #2421

Comments

@albe
Copy link
Member

albe commented Jul 21, 2020

See slack thread: https://neos-project.slack.com/archives/C050KKBEB/p1593764548080600

Some excerpt quotes:

@mficzel: I noticed that quite often people struggle with the configuration of routes and policies that are required to actually use a flow controller. Made even worse by the need to write a setting for the route to be actually included.
Symfony offers configuration via annotation for that and i think @flow\Route() or @flow\Policy˜ could be really helpful and easy to get when placed directly at the controller action. Offcourse you loose some flexibility. Is there a reason why we do’nt offer this as an additional option?

@bwaidelich: Originally I thought that it would be a good idea to separate routes completely from the controllers so that you can for example change the URLs of a 3rd party package.
And I still think that this is a valid point..
But for the 90+% applications this is probably not a requirement at all and comes with needless complexity..
So I’d be all in for a Route Annotation (maybe even a way to specify the URL directly, so that no Routes.yaml file is required!?
As for Permissions I personally think that it is not a good idea to target controller actions at all since they should just delegate I/O..
But what I would like to see was an easy way to make a Domain Service restricted by default and then whitelist public methods via annotation to certain roles

@albe: dotnet framework has a similar approach to defining routes and it works relatively well.
Basically: @HttpPost("route/to/action/{with}/{parameters}")

@mficzel: Configuration should override annotation and for generic packages Routes.yaml should still be used.

@radmiraal if you hit conflicts having the same route routing to different controllers based on loading order has a certain smell 🙂

@albe: I'd rather go the other route: annotation > configuration
If you clearly specify the route an action is supposed to be reachable at, that should not be overridden through some configuration somewhere

@albe albe added the Feature label Jan 11, 2021
@sorenmalling sorenmalling linked a pull request Mar 8, 2021 that will close this issue
4 tasks
@sorenmalling sorenmalling self-assigned this Aug 30, 2021
mficzel added a commit to mficzel/flow-development-collection that referenced this issue Mar 3, 2024
The `Flow\Route` attribute allows to define routes directly on the affected method.
This allows to avoid dealing with Routes.yaml in projects in simple cases where is sometimes is annoying to look up the exact syntax for that.

Hint: While this is a very convenient way to add routes in project code it should not be used in libraries/packages that expect to be configured for the outside.
In such cases the Routes.yaml is still preferred as it is easier to overwrite.

Usage:

```php
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }

    #[Flow\Route(uriPattern:'my/other/b-path', defaults: ['test' => 'b'])]
    #[Flow\Route(uriPattern:'my/other/c-path', defaults: ['test' => 'c'])]
    public function otherAction(): void
    {
    }
}
```

The package: `WebSupply.RouteAnnotation` by @sorenmalling implemented similar ideas earlier.

Resolves: neos#2059
mficzel added a commit to mficzel/flow-development-collection that referenced this issue Mar 3, 2024
The `Flow\Route` attribute allows to define routes directly on the affected method.
This allows to avoid dealing with Routes.yaml in projects in simple cases where is sometimes is annoying to look up the exact syntax for that.

Hint: While this is a very convenient way to add routes in project code it should not be used in libraries/packages that expect to be configured for the outside.
In such cases the Routes.yaml is still preferred as it is easier to overwrite.

Usage:

```php
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }

    #[Flow\Route(uriPattern:'my/other/b-path', defaults: ['test' => 'b'])]
    #[Flow\Route(uriPattern:'my/other/c-path', defaults: ['test' => 'c'])]
    public function otherAction(): void
    {
    }
}
```

The package: `WebSupply.RouteAnnotation` by @sorenmalling implemented similar ideas earlier.

Resolves: neos#2059
mficzel added a commit to mficzel/flow-development-collection that referenced this issue Mar 3, 2024
The `Flow\Route` attribute allows to define routes directly on the affected method.
This allows to avoid dealing with Routes.yaml in projects in simple cases where is sometimes is annoying to look up the exact syntax for that.

Hint: While this is a very convenient way to add routes in project code it should not be used in libraries/packages that expect to be configured for the outside.
In such cases the Routes.yaml is still preferred as it is easier to overwrite.

Usage:

```php
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }

    #[Flow\Route(uriPattern:'my/other/b-path', defaults: ['test' => 'b'])]
    #[Flow\Route(uriPattern:'my/other/c-path', defaults: ['test' => 'c'])]
    public function otherAction(): void
    {
    }
}
```

The package: `WebSupply.RouteAnnotation` by @sorenmalling implemented similar ideas earlier.

Resolves: neos#2059
mficzel added a commit to mficzel/flow-development-collection that referenced this issue Mar 3, 2024
The `Flow\Route` attribute allows to define routes directly on the affected method.
This allows to avoid dealing with Routes.yaml in projects in simple cases where is sometimes is annoying to look up the exact syntax for that.

Hint: While this is a very convenient way to add routes in project code it should not be used in libraries/packages that expect to be configured for the outside.
In such cases the Routes.yaml is still preferred as it is easier to overwrite.

Usage:

```php
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }

    #[Flow\Route(uriPattern:'my/other/b-path', defaults: ['test' => 'b'])]
    #[Flow\Route(uriPattern:'my/other/c-path', defaults: ['test' => 'c'])]
    public function otherAction(): void
    {
    }
}
```

The package: `WebSupply.RouteAnnotation` by @sorenmalling implemented similar ideas earlier.

Resolves: neos#2059
mficzel added a commit to mficzel/flow-development-collection that referenced this issue Mar 3, 2024
The `Flow\Route` attribute allows to define routes directly on the affected method.
This allows to avoid dealing with Routes.yaml in projects in simple cases where is sometimes is annoying to look up the exact syntax for that.

Hint: While this is a very convenient way to add routes in project code it should not be used in libraries/packages that expect to be configured for the outside.
In such cases the Routes.yaml is still preferred as it is easier to overwrite.

Usage:

```php
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }

    #[Flow\Route(uriPattern:'my/other/b-path', defaults: ['test' => 'b'])]
    #[Flow\Route(uriPattern:'my/other/c-path', defaults: ['test' => 'c'])]
    public function otherAction(): void
    {
    }
}
```

The package: `WebSupply.RouteAnnotation` by @sorenmalling implemented similar ideas earlier.

Resolves: neos#2059
@sorenmalling sorenmalling removed their assignment Mar 4, 2024
mficzel added a commit to mficzel/flow-development-collection that referenced this issue Mar 13, 2024
The `Flow\Route` attribute allows to define routes directly on the affected method.
This allows to avoid dealing with Routes.yaml in projects in simple cases where is sometimes is annoying to look up the exact syntax for that.

Hint: While this is a very convenient way to add routes in project code it should not be used in libraries/packages that expect to be configured for the outside.
In such cases the Routes.yaml is still preferred as it is easier to overwrite.

Usage:

```php
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }

    #[Flow\Route(uriPattern:'my/other/b-path', defaults: ['test' => 'b'])]
    #[Flow\Route(uriPattern:'my/other/c-path', defaults: ['test' => 'c'])]
    public function otherAction(): void
    {
    }
}
```

The package: `WebSupply.RouteAnnotation` by @sorenmalling implemented similar ideas earlier.

Resolves: neos#2059
mficzel added a commit to mficzel/flow-development-collection that referenced this issue Mar 14, 2024
The `Flow\Route` attribute allows to define routes directly on the affected method.
This allows to avoid dealing with Routes.yaml in projects in simple cases where is sometimes is annoying to look up the exact syntax for that.

Hint: While this is a very convenient way to add routes in project code it should not be used in libraries/packages that expect to be configured for the outside.
In such cases the Routes.yaml is still preferred as it is easier to overwrite.

Usage:

```php
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }

    #[Flow\Route(uriPattern:'my/other/b-path', defaults: ['test' => 'b'])]
    #[Flow\Route(uriPattern:'my/other/c-path', defaults: ['test' => 'c'])]
    public function otherAction(): void
    {
    }
}
```

The package: `WebSupply.RouteAnnotation` by @sorenmalling implemented similar ideas earlier.

Resolves: neos#2059
mficzel added a commit to mficzel/flow-development-collection that referenced this issue Mar 15, 2024
The `Flow\Route` attribute allows to define routes directly on the affected method.
This allows to avoid dealing with Routes.yaml in projects in simple cases where is sometimes is annoying to look up the exact syntax for that.

Hint: While this is a very convenient way to add routes in project code it should not be used in libraries/packages that expect to be configured for the outside.
In such cases the Routes.yaml is still preferred as it is easier to overwrite.

Usage:

```php
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Annotations as Flow;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }

    #[Flow\Route(uriPattern:'my/other/b-path', defaults: ['test' => 'b'])]
    #[Flow\Route(uriPattern:'my/other/c-path', defaults: ['test' => 'c'])]
    public function otherAction(): void
    {
    }
}
```

The package: `WebSupply.RouteAnnotation` by @sorenmalling implemented similar ideas earlier.

Resolves: neos#2059
@mhsdesign
Copy link
Member

Flows routing configuration is declared via @package, @subpackage, @controller and @action (as well as the format @format)
The first three options will resolve to a fully qualified class name \Neos\Flow\Mvc\ActionRequest::getControllerObjectName()
which is instantiated in the dispatcher \Neos\Flow\Mvc\Dispatcher::dispatch()

The latter @action option will be treated internally by each controller.
By convention and implementation of the default ActionController inside processRequest
\Neos\Flow\Mvc\Controller\ActionController::callActionMethod() will be used to concatenate the "Action" suffix
to the action name and invoke it internally with prepared method arguments.
The @action is just another routing value while the doest not really know about "actions" from the "outside" (dispatcher).

Creating routes by annotation must make a few assumptions to work.
As not every FQ class name is representable via the routing configuration (e.g. the class has to end with "Controller"),
only classes can be annotated that reside in a correct location and have the correct suffix.
Otherwise, an exception will be thrown as the class is not discoverable by the dispatcher.

The routing annotation is placed at methods.
It is validated that the annotated method ends with "Action" and a routing value with the suffix trimmed will be generated.
Using the annotations on any controller makes the assumption that the controller will delegate the request to the dedicate
action by depending "Action".
This thesis is true for the ActionController.

The current implementation of martins pr looks like:

$controllerPackageKey = $this->objectManager->getPackageKeyByObjectName($controllerObjectName);
$controllerPackageNamespace = str_replace('.', '\\', $controllerPackageKey);
if (!str_ends_with($className, 'Controller')) {
    throw new InvalidControllerException('Only for controller classes');
}
$localClassName = substr($className, strlen($controllerPackageNamespace) + 1);
$controllerName = substr($localClassName, 11);
if (!str_ends_with($methodName, 'Action')) {
    throw new \InvalidActionNameException('Only for action methods');
}
$defaults = [
    '@package' => $controllerPackageKey,
    '@controller' => substr($controllerName, 0, -10),
    '@action' => substr($methodName, 0, -6),
    '@format' => 'html'
];

In https://discuss.neos.io/t/rfc-future-of-routing-mvc-in-flow/6535 we discussed that @controller should for simplicity contain the whole FQN, so it can be easier generated without strong restrictions.

The question is how to cleanly resolve the @action magic?
In https://discuss.neos.io/t/rfc-future-of-routing-mvc-in-flow/6535/11?u=marc i wrote:

That’s still possible. An annotated method should not specify the @action default anyways (and IMO the corresponding implementation should throw if it is)

Okay interesting, if we were to introduce another routing way instead of generating the @action from the annotation this would solve the dilemma.

Currently martins pr would generate for

namespace My\Package\Controller;

class ExampleController extends ActionController
{
    #[Flow\Route(uriPattern:'my/path', httpMethods: ['get'])]
    public function someAction(): void
    {
    }
}

the following routing values (which i originally disliked as it leads to coupling of things that are currently not coupled)

@package: 'My.Package'
@controller: 'Example'
@action: 'some'

but if we would just generate the following value

@package: 'My.Package'
@controller: 'Example'

or later

@controller: 'My\Package\Controller\ExampleController'

and check inside the controller which annotated methods exist and based on that and their uriPattern and the uriPattern of the current request we could resolve the correct method.
Is that what you meant?

Or should we introduce with martins pr a separate @annotatedActionName value?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
3 participants