路由
路由
介绍
Masonent Routing 是一种极其简单但功能强大的路由系统,它至少需要一个url和一个控制器。Masonent 将采用此路由并将其与请求的路由进行匹配,并在匹配上之后执行控制器。
所有路由都在 routes / web.py
内部创建,并包含在 ROUTES
常量中。所有路由都包含某种形式的HTTP路由类型 (例如 Get()
或 Post()
)。最简单的路由如下所示:
routes/web.py
Get('/url/here', 'WelcomeController@show')
大多数路由都将由这样的结构组成。所有URI都应以 /
开头。仅仅在 Post 请求(如表单提交)的时候才会执行的路由跟下面的设置类似:
routes/web.py
Post('/url/here', 'WelcomeController@store')
注意此处的控制器是一个字符串。这是指定控制器的好方法,因为您不必将所有内容导入
web.py
文件。所有导入将在后端完成。稍后将详细介绍控制器。
如果你不想用字符串形式导入控制器,可以通过指定控制器并将引用传递到路由方法里。如下所示:
routes/web.py
...
from app.http.controllers.DashboardController import DashboardController
ROUTES = [
Get('/url/here', DashboardController.show)
]
一定要认识到我们没有初始化控制器或方法,实际上也没有调用。Masonite 在执行路由时通常可以通过自动解析依赖注入将参数传递给构造函数和方法。
路由选项
你可以使用下面的一些方法来增强你的路由。Masonite 通常使用 setter 方式(而非参数方式)进行构建,以拓展更多的功能。
HTTP 动作
路由可以使用以下 http 动作
routes/web.py
from masonite.routes import Get, Post, Put, Patch, Delete, Match, Options, Trace, Connect
Get(..)
Post(..)
Put(..)
Patch(..)
Delete(..)
Match(..)
Options(..)
Trace(..)
Connect(..)
路由组
可能有一些路由的功能非常相似。例如我们有一组路由,使用相同的中间件,以相同的前缀开头。在这种情况下,我们应该将这些路由分为一组,以便于维护。
通过以下方式添加一个路由组:
from masonite.routes import RouteGroup, Get
ROUTES = [
RouteGroup([
Get('/url1', ...),
Get('/url2', ...),
Get('/url3', ...),
]),
]
除此之外,还可以给路由组添加中间件:
ROUTES = [
RouteGroup([
Get('/url1', ...),
Get('/url2', ...),
Get('/url3', ...),
], middleware=('auth', 'jwt')),
]
如果只需要使用一个中间件,可以这样定义:
ROUTES = [
RouteGroup([
Get('/url1', ...),
Get('/url2', ...),
Get('/url3', ...),
], middleware=('auth',)),
]
最后一项 auth 的后面要写一个 “ ,” 这样可以确保我们传入的参数是一个元组,而非字符串数组。
在这个实例中,我们给路由组内所有的路由添加了两个中间件。我们还可以使用一些其他的参数。你可以随意组合使用以下参数。
ROUTES = [
RouteGroup([
Get('/url1', ...).name('create'),
Get('/url2', ...).name('update'),
Get('/url3', ...).name('delete'),
],
middleware=('auth', 'jwt'),
domain='subdomain',
prefix='/dashboard',
namespace='auth.',
name='post.',
add_methods=['OPTIONS']
),
]
prefix
参数将在路由组里所有的 url 前面加上前缀, name
参数也是。上面的代码将创建类似于 /dashboard/url1
的名字为 post.create
的路由,以及将域名和中间件添加到路由。
路由组中所有的选项都是以参数形式存在的,所以如果您认为在末尾添加组属性比较奇怪,那么您可以在开头指定这些参数并添加 routes
,如下:
RouteGroup(middleware=('auth', 'jwt'), name='post.', routes = [
Get('/url1', ...).name('create'),
Get('/url2', ...).name('update'),
Get('/url3', ...).name('delete'),
]),
路由组嵌套
嵌套路由组的能力更加出色:
ROUTES = [
RouteGroup([
Get('/url1', ...).name('create'),
Get('/url2', ...).name('update'),
Get('/url3', ...).name('delete'),
RouteGroup([
Get('/url4', ...).name('read'),
Get('/url5', ...).name('put'),
], prefix='/users', name='user.'),
], prefix='/dashboard', name='post.', middleware=('auth', 'jwt')),
]
这将进入每一层并从内到外生成一个路由表。针对下面一个真实的案例,我们重构路由:
ROUTES = [
Get().domain('www').route('/', 'WelcomeController@show').name('welcome'),
Post().domain('www').route('/invite', 'InvitationController@send').name('invite'),
Get().domain('www').route('/dashboard/apps', 'AppController@show').name('app.show').middleware('auth'),
Get().domain('www').route('/dashboard/apps/create', 'AppController@create').name('app.create').middleware('auth'),
Post().domain('www').route('/dashboard/apps/create', 'AppController@store').name('app.store'),
Post().domain('www').route('/dashboard/apps/delete', 'AppController@delete').name('app.delete'),
Get().domain('www').route('/dashboard/plans', 'PlanController@show').name('plans').middleware('auth'),
Post().domain('www').route('/dashboard/plans/subscribe', 'PlanController@subscribe').name('subscribe'),
Post().domain('www').route('/dashboard/plans/cancel', 'PlanController@cancel').name('cancel'),
Post().domain('www').route('/dashboard/plans/resume', 'PlanController@resume').name('resume'),
Post().domain('*').route('/invite', 'InvitationController@subdomain').name('invite.subdomain'),
Get().domain('*').route('/', 'WelcomeController@subdomain').name('welcome'),
]
ROUTES = ROUTES + [
Get().domain('www').route('/login', 'LoginController@show').name('login'),
Get().domain('www').route('/logout', 'LoginController@logout'),
Post().domain('www').route('/login', 'LoginController@store'),
Get().domain('www').route('/register', 'RegisterController@show'),
Post().domain('www').route('/register', 'RegisterController@store'),
Get().domain('www').route('/home', 'HomeController@show').name('home'),
]
重构成这样:
ROUTES = [
RouteGroup([
# Dashboard Routes
RouteGroup([
# App Routes
RouteGroup([
Get('', 'AppController@show').name('show'),
Get('/create', 'AppController@create').name('create'),
Post('/create', 'AppController@store').name('store'),
Post('/delete', 'AppController@delete').name('delete'),
], prefix='/apps', name='app.'),
Get('/plans', 'PlanController@show').name('plans'),
Post('/plans/subscribe', 'PlanController@subscribe').name('subscribe'),
Post('/plans/cancel', 'PlanController@cancel').name('cancel'),
Post('/plans/resume', 'PlanController@resume').name('resume'),
], prefix="/dashboard", middleware=('auth',)),
# Login and Register Routes
Get('/login', 'LoginController@show').name('login'),
Get('/logout', 'LoginController@logout'),
Post('/login', 'LoginController@store'),
Get('/register', 'RegisterController@show'),
Post('/register', 'RegisterController@store'),
Get('/home', 'HomeController@show').name('home'),
# Base Routes
Get('/', 'WelcomeController@show').name('welcome'),
Post('/invite', 'InvitationController@send').name('invite'),
], domain='www'),
# Subdomain invitation routes
Post().domain('*').route('/invite', 'InvitationController@subdomain').name('invite.subdomain'),
Get().domain('*').route('/', 'WelcomeController@subdomain').name('welcome'),
]
这可能是为应用程序构建路由的最常见方法。
View Routes
您还可以使用 view ,这只是普通路由类上的一种方法:
ROUTES = [
Get().view('/template', 'some/template', {'key': 'value'})
]
您可以将此视图方法与任何路由类一起使用。
重定向路由
您还可以使用 Redirect
路由类直接从路由列表进行重定向:
from masonite.routes import Redirect
ROUTES = [
Redirect('/old/route', '/new/route', status=302, methods=['GET', 'POST'])
]
您不必指定最后两个参数。默认值为对
GET
方法的302
响应。
匹配路由
上面您可能已经注意到我们有一个 Match
路由类。这可以匹配几种传入的请求方法。这对于同时匹配PUT
和PATCH
的路由很有用。
Match(['PUT', 'PATCH']).route(...)
请求方法不区分大小写。它们将在后端转换为大写。所以
['Put','Patch']
可以正常工作
命名路由
我们可以命名路由,以便以后在选择重定向到它们时可以使用这些名称。我们可以这样指定一个路由名称:
routes/web.py
Get('/dashboard', 'DashboardController@show').name('dashboard')
命名路由是一种好习惯,因为路由 URI 可以更改,但名称应始终保持不变。
路由中间件
中间件是在请求之前或之后执行类,任务或动作的好方法。在 config / middleware.py
文件中注册路由后,我们可以指定特定于路由的中间件,中间件文档 中有更详细的介绍。要添加路由中间件,我们可以使用中间件方法,如下所示:
routes/web.py
Get('/dashboard', 'DashboardController@show').middleware('auth', 'anothermiddleware')
该中间件将在执行路由之前或之后执行,具体取决于中间件。
有关中间件的使用请阅读 中间件文档 。
深目录控制器
所有控制器都位于 app / http / controllers
中,但有时您可能希望将控制器放在 controllers 目录内的不同模块中。例如,您可能希望将所有产品控制器放置在 app / http / controllers / products
中,或者将所有仪表板控制器放置在 app / http / controllers / users
中。为了在您的路线中访问这些控制器,我们可以简单地使用通常的点符号指定控制器:
routes/web.py
Get('/dashboard', 'users.DashboardController@show')
全局控制器
控制器默认为 app / http / controllers
目录,但您可能希望完全更改特定路由的目录。我们可以在控制器名称空间的开头使用 /
:
Get('/dashboard', '/thirdparty.package.users.DashboardController@show')
这可以使我们能够使用第三方程序包中的控制器。
您也可以直接导入该类并引用要使用的方法:
from app.controllers.SomeController import SomeController
Get('/dashboard', SomeController.show)
路由参数
通常,您需要在路由中指定参数,以便从 URI 中检索信息。这些参数可以是 id
,用于检索特定模型。在 Masonite 中指定路由参数非常简单,看起来就像:
routes/web.py
Get('/dashboard/@id', 'Controller@show')
这将在 Request
对象中创建一个词典,该词典可以在我们的控制器中找到。
为了从请求中检索参数,我们可以在 Request
对象上使用param
方法,如下所示:
app/http/controller/YourController.py
def show(self, request: Request):
request.param('id')
可选路由参数
有时,您可能需要匹配路由和路由参数。例如,您可能想将 / dashboard / user
和 / dashboard / user / settings
匹配到相同的控制器方法。在这种情况下,您可以使用可选参数,只需将 @
替换为 ?
:
routes/web.py
Get('/dashboard/user/?option', 'Controller@show')
如果未命中路由,您还可以设置默认值:
routes/web.py
Get('/dashboard/user/?option', 'Controller@show').default({
'option': 'settings'
})
路由编译器
有时,您将需要确定路由参数是哪种数据类型。例如,您可能要匹配 / dashboard / 1
之类的URI,但不匹配 / dashboard / joseph
的URI。为此,我们只需要将类型传递给我们的参数即可。如果不指定类型,则我们的参数将默认匹配所有字母数字和下划线字符。
routes/web.py
Get('/dashboard/@id:int', 'Controller@show')
这将匹配所有整数,但不匹配字符串。因此,例如,它将匹配 / dashboard / 10283
而不是 / dashboard / joseph
。
如果我们要匹配所有字符串而不是整数,则可以传递:
routes/web.py
Get('/dashboard/@id:string', 'Controller@show')
这将匹配 / dashboard / joseph
,而不匹配 / dashboard / 128372
。当前仅支持整数和字符串类型。
这些之所以称为“路由编译器”,是因为它们根据指定的内容对路由进行不同的编译。如果指定
:int
或:integer
,则将编译为与指定:string
时不同的正则表达式。
添加路由编译器
我们可以通过在服务提供者中指定路由编译器来将其添加到我们的项目中。
确保将它们添加到 wsgi
为 False
的服务提供者中。我们可以使用 compile
方法从容器中将它们添加到 Route 类上。一个完整的示例如下所示:
app/http/providers/RouteCompileProvider.py
from masonite.provider import ServiceProvider
from masonite.routes import Route
class RouteCompilerProvider(ServiceProvider):
wsgi = False
...
def boot(self, route: Route):
route.compile('year', r'([0-9]{4})')
我们只需要在 Route
类上调用 compile()
方法,并确保通过在字符串的开头加上 r
来指定正则表达式字符串。
您的正则表达式应封装在一个组中。如果您不熟悉regex,则基本上您的regex模式应该在括号内,如上例所示。
子域路由
您可能会根据不同的域名来指定路由。例如,您可能希望example.com/dashboard
路由到与 joseph.example.com/dashboard
不同的控制器。
默认这个功能处于关闭状态,无法直接使用。我们需要在 Request 类上添加一个调用,以激活子域路由功能。我们可以使用 wsgi = False 的服务提供者之一的 boot 方法来执行此操作:
app/providers/UserModelProvider.py
wsgi = False
...
def boot(self, request: Request):
request.activate_subdomains()
要使用子域,我们可以在路由上使用 .domain()
方法,如下所示:
routes/web.py
Get().domain('joseph').route('/dashboard', 'Controller@show')
该路由将与 joseph.example.com/dashboard
匹配,但与example.com/dashboard
或test.example.com/dashboard
不匹配。
匹配任何子域可能更为常见。为此,我们可以改用星号。
routes/web.py
Get().domain('*').route('/dashboard', 'Controller@show')
这将匹配所有子域,例如 test.example.com/dashboard
, joseph.example.com/dashboard
但不匹配 example.com/dashboard
。
如果找到匹配项,它还将在请求类中添加 subdomain
参数。我们可以这样检索当前子域:
app/http/controllers/YourController.py
def show(self, request: Request):
print(request.param('subdomain'))
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。