feature #35782 [Routing] Add stateless route attribute (mtarld)
This PR was merged into the 5.1-dev branch.
Discussion
----------
[Routing] Add stateless route attribute
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Ticket | https://github.com/orgs/symfony/projects/1#card-30506005
| License | MIT
| Doc PR | TODO
On top of https://github.com/symfony/symfony/pull/35732
Add a stateless attribute for:
Routes in annotations
```
@Route(stateless=true)
```
Yaml
```yml
route:
stateless: true
```
Xml
```xml
<route stateless="true" />
```
PHP configurator
```php
$route->stateless(true);
```
That stateless attribute is a shortcut for setting `_stateless` default attribute in route.
Commits
-------
2da68bae8f
[Routing] Add stateless route attribute
This commit is contained in:
commit
146945ad8e
@ -72,6 +72,11 @@ class Route
|
||||
unset($data['utf8']);
|
||||
}
|
||||
|
||||
if (isset($data['stateless'])) {
|
||||
$data['defaults']['_stateless'] = filter_var($data['stateless'], FILTER_VALIDATE_BOOLEAN) ?: false;
|
||||
unset($data['stateless']);
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$method = 'set'.str_replace('_', '', $key);
|
||||
if (!method_exists($this, $method)) {
|
||||
|
@ -10,6 +10,7 @@ CHANGELOG
|
||||
* added argument `$priority` to `RouteCollection::add()`
|
||||
* deprecated the `RouteCompiler::REGEX_DELIMITER` constant
|
||||
* added `ExpressionLanguageProvider` to expose extra functions to route conditions
|
||||
* added support for a `stateless` keyword for configuring route stateless in PHP, YAML and XML configurations.
|
||||
|
||||
5.0.0
|
||||
-----
|
||||
|
@ -160,4 +160,16 @@ trait RouteTrait
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the "_stateless" entry to defaults.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function stateless(bool $stateless = true): self
|
||||
{
|
||||
$this->route->addDefaults(['_stateless' => $stateless]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\RouteCompiler;
|
||||
|
||||
/**
|
||||
* XmlFileLoader loads XML routing files.
|
||||
@ -300,6 +299,15 @@ class XmlFileLoader extends FileLoader
|
||||
if ($node->hasAttribute('utf8')) {
|
||||
$options['utf8'] = XmlUtils::phpize($node->getAttribute('utf8'));
|
||||
}
|
||||
if ($stateless = $node->getAttribute('stateless')) {
|
||||
if (isset($defaults['_stateless'])) {
|
||||
$name = $node->hasAttribute('id') ? sprintf('"%s"', $node->getAttribute('id')) : sprintf('the "%s" tag', $node->tagName);
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "stateless" attribute and the defaults key "_stateless" for %s.', $path, $name));
|
||||
}
|
||||
|
||||
$defaults['_stateless'] = XmlUtils::phpize($stateless);
|
||||
}
|
||||
|
||||
return [$defaults, $requirements, $options, $condition, $paths, $prefixes];
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\RouteCompiler;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Parser as YamlParser;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
@ -33,7 +32,7 @@ class YamlFileLoader extends FileLoader
|
||||
use PrefixTrait;
|
||||
|
||||
private static $availableKeys = [
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root', 'locale', 'format', 'utf8', 'exclude',
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root', 'locale', 'format', 'utf8', 'exclude', 'stateless',
|
||||
];
|
||||
private $yamlParser;
|
||||
|
||||
@ -134,6 +133,9 @@ class YamlFileLoader extends FileLoader
|
||||
if (isset($config['utf8'])) {
|
||||
$options['utf8'] = $config['utf8'];
|
||||
}
|
||||
if (isset($config['stateless'])) {
|
||||
$defaults['_stateless'] = $config['stateless'];
|
||||
}
|
||||
|
||||
$route = $this->createLocalizedRoute($collection, $name, $config['path']);
|
||||
$route->addDefaults($defaults);
|
||||
@ -179,6 +181,9 @@ class YamlFileLoader extends FileLoader
|
||||
if (isset($config['utf8'])) {
|
||||
$options['utf8'] = $config['utf8'];
|
||||
}
|
||||
if (isset($config['stateless'])) {
|
||||
$defaults['_stateless'] = $config['stateless'];
|
||||
}
|
||||
|
||||
$this->setCurrentDir(\dirname($path));
|
||||
|
||||
@ -245,5 +250,8 @@ class YamlFileLoader extends FileLoader
|
||||
if (isset($config['controller']) && isset($config['defaults']['_controller'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" key and the defaults key "_controller" for "%s".', $path, $name));
|
||||
}
|
||||
if (isset($config['stateless']) && isset($config['defaults']['_stateless'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "stateless" key and the defaults key "_stateless" for "%s".', $path, $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@
|
||||
<xsd:attribute name="locale" type="xsd:string" />
|
||||
<xsd:attribute name="format" type="xsd:string" />
|
||||
<xsd:attribute name="utf8" type="xsd:boolean" />
|
||||
<xsd:attribute name="stateless" type="xsd:boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="import">
|
||||
@ -76,6 +77,7 @@
|
||||
<xsd:attribute name="format" type="xsd:string" />
|
||||
<xsd:attribute name="trailing-slash-on-root" type="xsd:boolean" />
|
||||
<xsd:attribute name="utf8" type="xsd:boolean" />
|
||||
<xsd:attribute name="stateless" type="xsd:boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="default" mixed="true">
|
||||
|
@ -6,5 +6,6 @@ return function (RoutingConfigurator $routes) {
|
||||
$routes->add('defaults', '/defaults')
|
||||
->locale('en')
|
||||
->format('html')
|
||||
->stateless(true)
|
||||
;
|
||||
};
|
||||
|
@ -4,5 +4,5 @@
|
||||
xsi:schemaLocation="http://symfony.com/schema/routing
|
||||
https://symfony.com/schema/routing/routing-1.0.xsd">
|
||||
|
||||
<route id="defaults" path="/defaults" locale="en" format="html" />
|
||||
<route id="defaults" path="/defaults" locale="en" format="html" stateless="true" />
|
||||
</routes>
|
||||
|
@ -2,3 +2,4 @@ defaults:
|
||||
path: /defaults
|
||||
locale: en
|
||||
format: html
|
||||
stateless: true
|
||||
|
@ -7,5 +7,6 @@ return function (RoutingConfigurator $routes) {
|
||||
->prefix('/defaults')
|
||||
->locale('g_locale')
|
||||
->format('g_format')
|
||||
->stateless(true)
|
||||
;
|
||||
};
|
||||
|
@ -6,5 +6,6 @@
|
||||
|
||||
<import resource="imported-with-defaults.xml" prefix="/defaults"
|
||||
locale="g_locale"
|
||||
format="g_format" />
|
||||
format="g_format"
|
||||
stateless="true" />
|
||||
</routes>
|
||||
|
@ -3,3 +3,4 @@ defaults:
|
||||
prefix: /defaults
|
||||
locale: g_locale
|
||||
format: g_format
|
||||
stateless: true
|
||||
|
@ -9,7 +9,8 @@ return function (RoutingConfigurator $routes) {
|
||||
->condition('abc')
|
||||
->options(['utf8' => true])
|
||||
->add('buz', 'zub')
|
||||
->controller('foo:act');
|
||||
->controller('foo:act')
|
||||
->stateless(true);
|
||||
|
||||
$routes->import('php_dsl_sub.php')
|
||||
->prefix('/sub')
|
||||
|
@ -11,7 +11,8 @@ return new class() {
|
||||
->condition('abc')
|
||||
->options(['utf8' => true])
|
||||
->add('buz', 'zub')
|
||||
->controller('foo:act');
|
||||
->controller('foo:act')
|
||||
->stateless(true);
|
||||
|
||||
$routes->import('php_dsl_sub.php')
|
||||
->prefix('/sub')
|
||||
|
@ -6,7 +6,7 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
$collection = new RouteCollection();
|
||||
$collection->add('blog_show', new Route(
|
||||
'/blog/{slug}',
|
||||
['_controller' => 'MyBlogBundle:Blog:show'],
|
||||
['_controller' => 'MyBlogBundle:Blog:show', '_stateless' => true],
|
||||
['locale' => '\w+'],
|
||||
['compiler_class' => 'RouteCompiler'],
|
||||
'{locale}.example.com',
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
<route id="blog_show" path="/blog/{slug}" host="{locale}.example.com" methods="GET|POST put,OpTiOnS" schemes="hTTps">
|
||||
<default key="_controller">MyBundle:Blog:show</default>
|
||||
<default key="_stateless">
|
||||
<bool>true</bool>
|
||||
</default>
|
||||
<requirement key="locale">\w+</requirement>
|
||||
<option key="compiler_class">RouteCompiler</option>
|
||||
<condition>context.getMethod() == "GET"</condition>
|
||||
|
@ -1,6 +1,6 @@
|
||||
blog_show:
|
||||
path: /blog/{slug}
|
||||
defaults: { _controller: "MyBundle:Blog:show" }
|
||||
defaults: { _controller: "MyBundle:Blog:show", _stateless: true }
|
||||
host: "{locale}.example.com"
|
||||
requirements: { 'locale': '\w+' }
|
||||
methods: ['GET','POST','put','OpTiOnS']
|
||||
|
@ -43,6 +43,7 @@ class PhpFileLoaderTest extends TestCase
|
||||
foreach ($routes as $route) {
|
||||
$this->assertSame('/blog/{slug}', $route->getPath());
|
||||
$this->assertSame('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
|
||||
$this->assertTrue($route->getDefault('_stateless'));
|
||||
$this->assertSame('{locale}.example.com', $route->getHost());
|
||||
$this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
|
||||
$this->assertEquals(['GET', 'POST', 'PUT', 'OPTIONS'], $route->getMethods());
|
||||
@ -109,9 +110,11 @@ class PhpFileLoaderTest extends TestCase
|
||||
$expectedRoutes->add('one', $localeRoute = new Route('/defaults/one'));
|
||||
$localeRoute->setDefault('_locale', 'g_locale');
|
||||
$localeRoute->setDefault('_format', 'g_format');
|
||||
$localeRoute->setDefault('_stateless', true);
|
||||
$expectedRoutes->add('two', $formatRoute = new Route('/defaults/two'));
|
||||
$formatRoute->setDefault('_locale', 'g_locale');
|
||||
$formatRoute->setDefault('_format', 'g_format');
|
||||
$formatRoute->setDefault('_stateless', true);
|
||||
$formatRoute->setDefault('specific', 'imported');
|
||||
|
||||
$expectedRoutes->addResource(new FileResource(__DIR__.'/../Fixtures/imported-with-defaults.php'));
|
||||
@ -172,7 +175,7 @@ class PhpFileLoaderTest extends TestCase
|
||||
->setCondition('abc')
|
||||
);
|
||||
$expectedCollection->add('buz', (new Route('/zub'))
|
||||
->setDefaults(['_controller' => 'foo:act'])
|
||||
->setDefaults(['_controller' => 'foo:act', '_stateless' => true])
|
||||
);
|
||||
$expectedCollection->add('c_root', (new Route('/sub/pub/'))
|
||||
->setRequirements(['id' => '\d+'])
|
||||
|
@ -47,6 +47,7 @@ class XmlFileLoaderTest extends TestCase
|
||||
$this->assertEquals(['GET', 'POST', 'PUT', 'OPTIONS'], $route->getMethods());
|
||||
$this->assertEquals(['https'], $route->getSchemes());
|
||||
$this->assertEquals('context.getMethod() == "GET"', $route->getCondition());
|
||||
$this->assertTrue($route->getDefault('_stateless'));
|
||||
}
|
||||
|
||||
public function testLoadWithNamespacePrefix()
|
||||
@ -98,6 +99,7 @@ class XmlFileLoaderTest extends TestCase
|
||||
$this->assertSame('/defaults', $defaultsRoute->getPath());
|
||||
$this->assertSame('en', $defaultsRoute->getDefault('_locale'));
|
||||
$this->assertSame('html', $defaultsRoute->getDefault('_format'));
|
||||
$this->assertTrue($defaultsRoute->getDefault('_stateless'));
|
||||
}
|
||||
|
||||
public function testLoadingImportedRoutesWithDefaults()
|
||||
@ -111,9 +113,11 @@ class XmlFileLoaderTest extends TestCase
|
||||
$expectedRoutes->add('one', $localeRoute = new Route('/defaults/one'));
|
||||
$localeRoute->setDefault('_locale', 'g_locale');
|
||||
$localeRoute->setDefault('_format', 'g_format');
|
||||
$localeRoute->setDefault('_stateless', true);
|
||||
$expectedRoutes->add('two', $formatRoute = new Route('/defaults/two'));
|
||||
$formatRoute->setDefault('_locale', 'g_locale');
|
||||
$formatRoute->setDefault('_format', 'g_format');
|
||||
$formatRoute->setDefault('_stateless', true);
|
||||
$formatRoute->setDefault('specific', 'imported');
|
||||
|
||||
$expectedRoutes->addResource(new FileResource(__DIR__.'/../Fixtures/imported-with-defaults.xml'));
|
||||
|
@ -90,6 +90,7 @@ class YamlFileLoaderTest extends TestCase
|
||||
$this->assertEquals(['GET', 'POST', 'PUT', 'OPTIONS'], $route->getMethods());
|
||||
$this->assertEquals(['https'], $route->getSchemes());
|
||||
$this->assertEquals('context.getMethod() == "GET"', $route->getCondition());
|
||||
$this->assertTrue($route->getDefault('_stateless'));
|
||||
}
|
||||
|
||||
public function testLoadWithResource()
|
||||
@ -232,6 +233,7 @@ class YamlFileLoaderTest extends TestCase
|
||||
$this->assertSame('/defaults', $defaultsRoute->getPath());
|
||||
$this->assertSame('en', $defaultsRoute->getDefault('_locale'));
|
||||
$this->assertSame('html', $defaultsRoute->getDefault('_format'));
|
||||
$this->assertTrue($defaultsRoute->getDefault('_stateless'));
|
||||
}
|
||||
|
||||
public function testLoadingImportedRoutesWithDefaults()
|
||||
@ -245,9 +247,11 @@ class YamlFileLoaderTest extends TestCase
|
||||
$expectedRoutes->add('one', $localeRoute = new Route('/defaults/one'));
|
||||
$localeRoute->setDefault('_locale', 'g_locale');
|
||||
$localeRoute->setDefault('_format', 'g_format');
|
||||
$localeRoute->setDefault('_stateless', true);
|
||||
$expectedRoutes->add('two', $formatRoute = new Route('/defaults/two'));
|
||||
$formatRoute->setDefault('_locale', 'g_locale');
|
||||
$formatRoute->setDefault('_format', 'g_format');
|
||||
$formatRoute->setDefault('_stateless', true);
|
||||
$formatRoute->setDefault('specific', 'imported');
|
||||
|
||||
$expectedRoutes->addResource(new FileResource(__DIR__.'/../Fixtures/imported-with-defaults.yml'));
|
||||
|
Reference in New Issue
Block a user