内置HTTP路由器
router是用来将请求的http request 转到对应的Action处理的一个组件
一个http请求在mvc框架中被当做是一个事件,这个事件包含两个组成部分:
1、请求的url+参数
2、请求的类型post还是get
路由的跳转规则实在conf/routes文件中配置
##依赖注入
Play的路由构造器会创建一个构造器类,这个类通过 @Inject 在构造器中接受一个controller的实例,这说明我们既可以通过依赖注入实例化,也可以通过构造函数手动的实例化
Play还有一个遗留的静态路由生成器,他和被声明的controller共同工作,不过这是不推荐的方式,这个方式不但打破了封装,而且也不被一些新的Play的接口
如果你需要使用静态路由生成器,你可以通过在build.sbt配置文件添加以下配置来进行转换
routesGenerator := StaticRoutesGenerator
Play默认的认为你使用的注解的方式创建controller 如果不想使用这个模式,你可以去使用静态的路由生成器 ,或者通过用@符号预先声明在调用controller的地方,或者将每个controller声明为一个object(静态对象)而不是一个class
路由配置文件的配置方法
conf/routes是用于配置路由的文件,这个文件中罗列了所有在应用中被用到的路由。每一个路由由一个http请求方式和一个http请求的url构成,两者通过声明关联到对应的Action生成器上
# Display a client.(可以用#来编辑注释)
GET /clients/:id controllers.Clients.show(id: Long)
你也可以使用->来将路由指定到其他的配置文件
-> /api api.MyRouter
也可以在路由配置前用一个+开头,这可以改变以确定的Play组件的行为,这面这就是使用“nocsrf”来修饰,来绕过 CSRF filter
+ nocsrf
POST /api/new controllers.Api.newThing
Http请求
Play可以支持所有的请求类型:(GET, PATCH, POST, PUT, DELETE, HEAD)
URI部分
URI部分指定了路由请求的路径,而且可以动态的指定。而且支持正则表达式
静态的指定:
GET /clients/all controllers.Clients.list()
动态的指定:
GET /clients/:id controllers.Clients.show(id: Long)
动态部分使用多“/”
如果在动态部分去捕获多个urI路径(通过斜杠分开),可以使用*id,也就是使用正则表达式的通配符模式
GET /files/*name controllers.Application.download(name)
在上面的配置中,访问 /files/myproject/test.png ,在后面的controller里接收到的参数也就是/myproject/test.png
但是动态的路由跨越多个“/”,既不是由路由解码的也不是由反向路由编码的,校验所有用户输入的uri段是你的责任,反向路由只是做一些简单的串联工作,所以你要确保result的值是可靠的正确的,而不是像有多个前置的斜杠或者非assic字符
动态部分使用正则表达式
GET /items/$id<[0-9]+> controllers.Items.show(id: Long)
声明Action生成器方法
在配置的最后一部分是一个声明,这个部分必须定义一个返回 play.api.mvc.Action值的有效方法,这个方法通常是一个controller action
如果声明的方法没有定义任何参数,那只需要给一个完整的方法名就可以了
GET / controllers.Application.homePage()
如果你的action定义了一些参数,所有的参数都可以通过在请求url中抽出来去或者从查询字符串中获取
从请求uri中获取:
# Extract the page parameter from the path.
GET /:page controllers.Application.show(page)
# Extract the page parameter from the query string.
GET / controllers.Application.show(page)
与之对应的show方法的定义
def show(page: String) = Action {
loadContentFromDatabase(page).map { htmlContent =>
Ok(htmlContent).as("text/html")
}.getOrElse(NotFound)
}
如果参数是String的时候,参数的类型是可选的,如果你想去指定键入的参数为指定的Scala类型,你可以明确的在action声明处指定参数的类型
GET /clients/:id controllers.Clients.show(id: Long)
可以在路由中设置默认的参数值
# Extract the page parameter from the path, or fix the value for /
GET / controllers.Application.show(page = "home")
GET /:page controllers.Application.show(page)
但是上面的写法比较麻烦,我们可以用一条记录同时匹配有默认值和没有默认值的情况,也就是如果没有键入值的话就使用默认值,如果有键入值的话就使用键入值
# Pagination links, like /clients?page=3
GET /clients controllers.Clients.list(page: Int ?= 1)
我们也可以使用一个可选值的option去传参数,对参数没有限制
# The version parameter is optional. E.g. /api/list-all?version=3.0
GET /api/list-all controllers.Api.list(version: Option[String]
路由的优先级
很多时候我们在同意请求的时候会匹配到多个路由,这个时候会默认根据声明顺序选择第一个配置的路由
反向路由
路由生成器还可用于从Scala调用中生成URL。这样就可以将所有URI模式集中在一个配置文件中,这样您在重构应用程序时就会更加自信。
对于路径文件中使用的每个控制器,路由器将在routes包中生成一个“反向控制器” ,具有相同的操作方法,具有相同的签名,但返回一个 play.api.mvc.Call而不是a play.api.mvc.Action。
在play.api.mvc.Call定义了一个HTTP调用,并同时提供了HTTP方法和URI。
例如,如果您创建一个控制器,如:
package controllers
import javax.inject.Inject
import play.api._
import play.api.mvc._
class Application @Inject()(cc:ControllerComponents) extends AbstractController(cc) {
def hello(name: String) = Action {
Ok("Hello " + name + "!")
}
}
如果您将其映射到conf/routes文件中:
# Hello action
GET /hello/:name controllers.Application.hello(name)
然后,您可以hello使用controllers.routes.Application反向控制器将URL反转到操作方法:
// Redirect to /hello/Bob
def helloBob = Action {
Redirect(routes.Application.hello("Bob"))
}
注意:routes每个控制器包都有一个子包。因此,操作controllers.Application.hello可以通过反转controllers.routes.Application.hello(只要在路径文件中没有其他路径发生匹配生成的路径)。
反向操作方法非常简单:它接受您的参数并将它们替换回路径模式。在路径段(:foo)的情况下,在替换完成之前对值进行编码。对于正则表达式和通配符模式,字符串将以原始形式替换,因为该值可能跨越多个段。确保在将它们传递到反向路径时根据需要转义这些组件,并避免传递未经验证的用户输入。
相对路线
有些情况下返回相对路线而不是绝对路线可能是有用的。返回的路由play.mvc.Call始终是绝对的(它们带有一个 /),当HTTP代理,负载平衡器和API网关重写对Web应用程序的请求时,这可能会导致问题。使用相对路线有用的一些示例包括:
在Web网关后面托管应用程序,该应用程序在所有路径前面添加除conf/routes文件中配置的内容之外的其他内容,并将应用程序置于不期望的路径上。
在动态呈现样式表时,您需要资产链接是相对的,因为它们可能最终由CDN从不同的URL提供服务。
为了能够生成相对路线,您需要知道相对于(起始路线)的目标路线是什么。可以从当前检索起始路线RequestHeader。因此,要生成相对路径,您需要将当前RequestHeader路径或起始路径作为String参数传递。
例如,给定的控制器端点如:
package controllers
import javax.inject._
import play.api.mvc._
@Singleton
class Relative @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
def helloview() = Action { implicit request =>
Ok(views.html.hello("Bob"))
}
def hello(name: String) = Action {
Ok(s"Hello $name!")
}
}
注意:当前请求通过声明来隐式传递给视图模板implicit request
如果您将其映射到conf/routes文件中:
GET /foo/bar/hello controllers.Relative.helloview
GET /hello/:name controllers.Relative.hello(name)
然后,您可以像以前一样使用反向路由器定义相对路由,并包括对relative:
@(name: String)(implicit request: RequestHeader)
<h1>Hello @name</h1>
<a href="@routes.Relative.hello(name)">Absolute Link</a>
<a href="@routes.Relative.hello(name).relative">Relative Link</a>
注意:该Request从控制器传递被转换为RequestHeader与被标记implicit在视图参数。然后它会隐式传递给调用relative
请求/foo/bar/hello生成的HTML时,如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bob</title>
</head>
<body>
<a href="/hello/Bob">Absolute Link</a>
<a href="../../hello/Bob">Relative Link</a>
</body>
</html>
默认控制器
Play包括一个Default控制器,它提供了一些有用的动作。这些可以直接从routes文件中调用:
# Redirects to https://www.playframework.com/ with 303 See Other
GET /about controllers.Default.redirect(to = "https://www.playframework.com/")
# Responds with 404 Not Found
GET /orders controllers.Default.notFound
# Responds with 500 Internal Server Error
GET /clients controllers.Default.error
# Responds with 501 Not Implemented
GET /posts controllers.Default.todo
在此示例中,GET /about重定向到外部网站,但也可以重定向到另一个操作(例如/posts在上面的示例中)。
自定义路由
Play提供了一个用于定义嵌入式路由器的DSL,称为字符串插入路由DSL,或简称为sird。此DSL有许多用途,包括嵌入轻量级Play服务器,为常规Play应用程序提供自定义或更高级的路由功能,以及模拟REST服务以进行测试。
请参阅字符串插值路由DSL