内容简介:Laravel HTTP—— RESTFul 风格路由的使用与源码分析
前言
本文 GitBook 地址: https://www.gitbook.com/book/leoyang90/laravel-source-analysis
我们在前面的文章已经讲了整个路由与控制器的源码,我们今天这个文章开始向大家介绍在 laravel 中创建 RESTFul 风格的控制器。
关于什么是RESTFul风格及其规范可参考这篇文章:理解RESTful架构。
关于 laravel 中 RESTFul 风格控制器的创建简要介绍 : HTTP控制器实例教程 —— 创建 RESTFul 风格控制器实现文章增删改查
创建 RESTFul 风格控制器
要想在 laravel 中创建 RESTFul 风格控制器,只需要一句:
Route::resource('post','PostController');
该路由包含了指向多个动作的子路由:
| 方法 | 路径 | 动作 | 路由名称 |
|---|---|---|---|
| GET | /post | index | post.index |
| GET | /post/create | create | post.create |
| POST | /post | store | post.store |
| GET | /post/{post} | show | post.show |
| GET | /post/{post}/edit | edit | post.edit |
| PUT/PATCH | /post/{post} | update | post.update |
| DELETE | /post/{post} | destroy | post.destroy |
这种用法既简单又方便,接下来,我们将会说一下 laravel 为我们提供的更加灵活的用法。
前缀 RESTFul 路由
可以为 RESTFul 路由定义前缀:
$router->resource('prefix/foos', 'FooController');
$this->assertEquals('prefix/foos/{foo}', $routes[3]->uri());
多参数 RESTFul 路由
laravel 允许定义拥有多个参数的 RESTFul 路由:
$router->resource('foos.bars', 'FooController');
$this->assertEquals('foos/{foo}/bars/{bar}', $routes[3]->uri());
参数自定义命名
一般来说,RESTFul 路由的参数命名规则是路由单数,符号 - 转为 _,例如下面例子中 bars,和 foo-baz。
$router->resource('foos', 'FooController');
$this->assertEquals('foos/{foo}', $routes[3]->uri());
$router->resource('foo-bar.foo-baz', 'FooController', ['only' => ['show']]);
$this->assertEquals('foo-bar/{foo_bar}/foo-baz/{foo_baz}', $routes[0]->uri());
我们可以利用 parameters 强制这种单数模式:
$router->resource('foos', 'FooController', ['parameters' => 'singular']);
$this->assertEquals('foos/{foo}', $routes[3]->uri());
我们也可以利用 singularParameters 来强制:
ResourceRegistrar::singularParameters(true);
$router->resource('foos', 'FooController', ['parameters' => 'singular']);
$this->assertEquals('foos/{foo}', $routes[3]->uri());
我们还可以不使用单数,利用 parameters 用自己自定义的名字来定义参数:
$router->resource('bars.foos.bazs', 'FooController', ['parameters' => ['foos' => 'oof', 'bazs' => 'b']]);
$this->assertEquals('bars/{bar}/foos/{oof}/bazs/{b}', $routes[3]->uri());
同时,我们仍然可以利用 setParameters 函数来自定义参数命名:
ResourceRegistrar::setParameters(['foos' => 'oof', 'bazs' => 'b']);
$router->resource('bars.foos.bazs', 'FooController');
$this->assertEquals('bars/{bar}/foos/{oof}/bazs/{b}', $routes[3]->uri());
RESTFul 路由动词控制
laravel 为 RESTFul 路由生成了两个带有动词的路由: create 、 edit,分别用于加载订单的创建页面与编辑页面,这两个动词 laravel 是允许修改的:
ResourceRegistrar::verbs([
'create' => 'ajouter',
'edit' => 'modifier',
]);
$router->resource('foo', 'FooController');
$routes = $router->getRoutes();
$this->assertEquals('foo/ajouter', $routes->getByName('foo.create')->uri());
$this->assertEquals('foo/{foo}/modifier', $routes->getByName('foo.edit')->uri());
控制器方法约束
一般情况下,我们都会一次性想要上面所生成的七个路由,然而,有时候,我们只需要其中几个,或者不想要其中几个。这时候就可以利用 only 或者 except:
$router = $this->getRouter();
$router->resource('foo', 'FooController', ['only' => ['show', 'destroy']]);
$routes = $router->getRoutes();
$this->assertCount(2, $routes);
$router = $this->getRouter();
$router->resource('foo', 'FooController', ['except' => ['show', 'destroy']]);
$routes = $router->getRoutes();
$this->assertCount(5, $routes);
RESTFul 路由名称自定义
RESTFul 路由的每个路由都要自己默认的路由名称,laravel 允许我们对路由名称进行修改:
我们可以用 as 来为路由名称添加前缀:
$router->resource('foo-bars', 'FooController', ['only' => ['show'], 'as' => 'prefix']);
$this->assertEquals('prefix.foo-bars.show', $routes[0]->getName());
当有多个路由参数的时候,路由参数默认添加到了路由名称中:
$router->resource('prefix/foo.bar', 'FooController');
$this->assertTrue($router->getRoutes()->hasNamedRoute('foo.bar.index'));
可以利用 names 为单个路由来命名:
$router->resource('foo', 'FooController', ['names' => [
'index' => 'foo',
'show' => 'bar',
]]);
$this->assertTrue($router->getRoutes()->hasNamedRoute('foo'));
$this->assertTrue($router->getRoutes()->hasNamedRoute('bar'));
还可以利用 names 为所有路由来命名:
$router->resource('foo', 'FooController', ['names' => 'bar']);
$this->assertTrue($router->getRoutes()->hasNamedRoute('bar.index'));
RESTFul 路由源码分析
RESTFul 路由的创建工作由类 ResourceRegistrar 负责,这个类为默认为用户创建七个路由,函数方法 register 是创建路由的主函数:
class ResourceRegistrar
{
public function register($name, $controller, array $options = [])
{
if (isset($options['parameters']) && ! isset($this->parameters)) {
$this->parameters = $options['parameters'];
}
if (Str::contains($name, '/')) {
$this->prefixedResource($name, $controller, $options);
return;
}
$base = $this->getResourceWildcard(last(explode('.', $name)));
$defaults = $this->resourceDefaults;
foreach ($this->getResourceMethods($defaults, $options) as $m) {
$this->{'addResource'.ucfirst($m)}($name, $base, $controller, $options);
}
}
}
这个函数主要流程分为三段:
- 判断是否由前缀
- 获取路由的基础参数
- 添加路由
拥有前缀的 RESTFul 路由
如果我们为 RESTFul 路由添加了前缀,那么 laravel 将会以 group 的形式添加路由:
protected function prefixedResource($name, $controller, array $options)
{
list($name, $prefix) = $this->getResourcePrefix($name);
$callback = function ($me) use ($name, $controller, $options) {
$me->resource($name, $controller, $options);
};
return $this->router->group(compact('prefix'), $callback);
}
protected function getResourcePrefix($name)
{
$segments = explode('/', $name);
$prefix = implode('/', array_slice($segments, 0, -1));
return [end($segments), $prefix];
}
获取基础 RESTFul 路由参数
在添加各种路由之前,我们需要先获取路由的基础参数,也就是当存在多参数情况下,最后的参数。获取参数后,如果用户有自定义命名,则获取自定义命名:
public function getResourceWildcard($value)
{
if (isset($this->parameters[$value])) {
$value = $this->parameters[$value];
} elseif (isset(static::$parameterMap[$value])) {
$value = static::$parameterMap[$value];
} elseif ($this->parameters === 'singular' || static::$singularParameters) {
$value = Str::singular($value);
}
return str_replace('-', '_', $value);
}
添加各种路由
添加路由主要有三个步骤:
- 计算路由
uri - 获取路由属性
- 创建路由
protected function addResourceIndex($name, $base, $controller, $options)
{
$uri = $this->getResourceUri($name);
$action = $this->getResourceAction($name, $controller, 'index', $options);
return $this->router->get($uri, $action);
}
当计算路由 uri 时,由于存在多参数的情况,需要循环计算路由参数:
public function getResourceUri($resource)
{
if (! Str::contains($resource, '.')) {
return $resource;
}
$segments = explode('.', $resource);
$uri = $this->getNestedResourceUri($segments);
return str_replace('/{'.$this->getResourceWildcard(end($segments)).'}', '', $uri);
}
protected function getNestedResourceUri(array $segments)
{
return implode('/', array_map(function ($s) {
return $s.'/{'.$this->getResourceWildcard($s).'}';
}, $segments));
}
当计算路由的属性时,最重要的是获取路由的名字,路由的名字可以是默认,也可以是用户利用 names 或者 as 属性来自定义:
protected function getResourceAction($resource, $controller, $method, $options)
{
$name = $this->getResourceRouteName($resource, $method, $options);
$action = ['as' => $name, 'uses' => $controller.'@'.$method];
if (isset($options['middleware'])) {
$action['middleware'] = $options['middleware'];
}
return $action;
}
protected function getResourceRouteName($resource, $method, $options)
{
$name = $resource;
if (isset($options['names'])) {
if (is_string($options['names'])) {
$name = $options['names'];
} elseif (isset($options['names'][$method])) {
return $options['names'][$method];
}
}
$prefix = isset($options['as']) ? $options['as'].'.' : '';
return trim(sprintf('%s%s.%s', $prefix, $name, $method), '.');
}
值得注意的是,如果单独为某一个方法命名,那么直接回返回命名,而不会受 as 和方法名 'method' 的影响。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- gin 源码阅读(二)-- 路由和路由组
- express源码分析-路由
- flask 源码解析3:路由
- RocketMQ源码分析之路由中心
- Laravel HTTP——添加路由源码分析
- RocketMQ 源码分析之路由中心(NameServer)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Seasoned Schemer
Daniel P. Friedman、Matthias Felleisen / The MIT Press / 1995-12-21 / USD 38.00
drawings by Duane Bibbyforeword and afterword by Guy L. Steele Jr.The notion that "thinking about computing is one of the most exciting things the human mind can do" sets both The Little Schemer (form......一起来看看 《The Seasoned Schemer》 这本书的介绍吧!