博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【二 HTTP编程】2. HTTP路由
阅读量:6953 次
发布时间:2019-06-27

本文共 6360 字,大约阅读时间需要 21 分钟。

  hot3.png

内置HTTP router

http router负责将HTTP请求翻译为Action。

MVC框架将每个HTTP请求视作一个事件。此事件包含两个主要部分:

  • 请求路径(e.g. /clients/1542, /photos/list)
  • HTTP方法(e.g. GET,POST,...)

路由在编译的 conf/routes 文件中定义。这意味着你将在浏览器中直接看到路由错误。

依赖注入

Play默认的路由生产器会创建一个路由类,产生的路由类在@Inject注解标记的构造函数中接受controller实例作为参数。这意味着它既能使用依赖注入,也能使用该构造函数来手动创建。

Play同时提供一个遗留的静态路由产生器,用于那些以object形式声明的controller。这并不是推荐的做法,因为它破坏了封装,让代码更加难以测试,而且也无法兼容Play的很多新API。

如果你需要使用静态controller,你可以通过在build.sbt中增加如下配置来启用静态路由器:

routesGenerator := StaticRoutesGenerator

Play文档中的例子假定你使用了注入式路由产生器。如果你想尝试静态路由,那么你可以在controller调用时增加@symbol注解前缀,或者直接将controller声明为object。

路由文件语法

conf/routes 是路由器的配置文件。此文件列出了所有应用需要使用的路由。每个路由由一个HTTP方法和一个URI组成,它们俩一起调用一个Action产生器。

下面是一个常见的路由定义:

GET   /clients/:id          controllers.Clients.show(id: Long)

每个路由开头是一个HTTP方法,然后是URI,最后是调用定义。你可以在前面增加注解,注解以 # 开头:

# Display a client.GET   /clients/:id          controllers.Clients.show(id: Long)

你可以通过 “->” 前缀来调用一些特殊的路由器:

->      /api                        api.MyRouter

这种方式在和结合使用时特别有用。如项目中包含子项目,且有多个路由文件时。

路由配置文件还有一种以+开头特殊配置。它可以用来改变Play组件的特定行为。如下面的 “nocsrf” 可以用来旁路:

+ nocsrfPOST  /api/new              controllers.Api.newThing

HTTP方法

支持HTTP协议本身支持的方法(GET,PATCH,POST,PUT,DELETE,HEAD)。

URI模式

URI模式定义了路由的请求路径。其中部分请求路径可以是动态的。

静态路径

举例来说,要精确匹配 GET /clients/all 请求,你可以定义如下路由:

GET   /clients/all          controllers.Clients.list()

动态部分

比如你想通过ID获取一个client,你可以在URI中增加如下动态部分:

GET   /clients/:id          controllers.Clients.show(id: Long)

注意:一个URI模式可以有多个动态部分。

默认的匹配策略由一个正则表达式定义 [^/]+,意味着任意的动态部分如 :id 将会被精确地匹配到一个URI路径段中。有别于其他模式类型,此路径段在传入你的controller前将自动URI解码,反向时也将自动编码。

跨多级 / 的动态路由

如果你希望一个动态部分可以匹配到多于一个的、由正斜杠分开的URI路径段,可以你用 * 通配符:

GET   /files/*name          controllers.Application.download(name)

比如一个 GET /files/images/logo.png 请求,name的值将匹配为 images/logo.png。

注意这种多段匹配中的 / 将不会被路由器编解码。需要你自己来做原始的URI段校验。反向的路由器只是简单地将多个字符串拼接起来,因此你需要保证生成的结果是有效的,不包含斜杠及非ASCII字符。

自定义正则

你可以在动态部分中自定义正则,使用 $id<regex> 语法:

GET   /items/$id<[0-9]+>    controllers.Items.show(id: Long)

正如通配路由一样,参数将不会变自动编解码。校验交由开发者自己进行。

调用Action生成器方法

路由定义的最后是方法调用。一个正确定义的方法调用需要返回一个 play.api.mvc.Action 值,通常情况下这是一个 controller方法。

如果方法没有定义任何参数,直接给出全限定方法名就好:

GET   /                     controllers.Application.homePage()

如果方法定义了参数,这些参数将在URI请求中查找,包括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)

 相应 controllers.Application 中的show方法如下:

def show(page: String) = Action {  loadContentFromDatabase(page).map { htmlContent =>    Ok(htmlContent).as("text/html")  }.getOrElse(NotFound)}

参数类型

String类型的参数类型标记是可选的。如果希望Scala将输出的参数转换为特定的Scala类型,你可以如下显式定义:

GET   /clients/:id          controllers.Clients.show(id: Long)

相应 controllers.Clients 控制器中的show方法定义如下:

def show(id: Long) = Action {  Client.findById(id).map { client =>    Ok(views.html.Clients.display(client))  }.getOrElse(NotFound)}

固定值的参数

有些时候需要使用固定值的参数:

# Extract the page parameter from the path, or fix the value for /GET   /                     controllers.Application.show(page = "home")GET   /:page                controllers.Application.show(page)

默认值的参数

在请求中没有找到参数值时,Play可以使用你提供的默认值:

# Pagination links, like /clients?page=3GET   /clients              controllers.Clients.list(page: Int ?= 1)

可选参数

可以按照如下方式定义可选参数:

# The version parameter is optional. E.g. /api/list-all?version=3.0GET   /api/list-all         controllers.Api.list(version: Option[String])

路由优先级

多个路由可以匹配到同一个请求。如果遇到路由冲突的情况,第一个声明的路由将被使用。

反向路由

路由器也可以在Scala的方法调用中用来生成URL。它使得你可以将所有URI规则集中到一个配置文件中,这使得你在重构应用时更加清晰。

对于route文件中的每个controller,都会在此 routes 包中生成一个 ‘反向controller’,它们拥有同样的action方法,同样的签名,但是返回一个play.api.mvc.Call,而不是play.api.mvc.Action。

play.api.mvc.Call 定义一个HTTP调用,提供了HTTP方法和URI。

举例来说,如果你的controller长这样:

package controllersimport javax.inject.Injectimport 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 actionGET   /hello/:name          controllers.Application.hello(name)

你可以通过 controllers.routes.Application 反向控制器将URL反向为一个hello action方法:

// Redirect to /hello/Bobdef helloBob = Action {  Redirect(routes.Application.hello("Bob"))}

注意:每个controller包下都有一个routes子包。 故 action controllers.Application.hello 可以通过 controllers.routes.Application.hello 来反向。

反向action的工作很简单:获取参数,并将它们替换入路由模板。比如对于(:foo)形式的路由段来说,它的值在替换前会被编码。对于正则和通配模式来说,字符串会以原始形式来替换,因为可能会跨多个路由段。因此在将参数传递到反向路由时,需要确保按照要求来escape,并过滤掉用户无效的输入。

相对路由

有些时候,相对路由比绝对路由更有用。play.mvc.Call 返回的路由永远是绝对的(既由 / 开始),这在访问你的web应用的请求被HTTP代理、负载均衡器、或者API网关重写时会导致问题。一些有用的相对路由例子如下:

  • 应用程序托管在一个web网关之后,它会为所有路由加上 conf/routes 文件中没有配置的前缀内容,将你的应用置于不期望的路由上。
  • 动态渲染样式表,你必须使用相对链接,因为它们可能通过CDN最终从不同的URL来提供服务。

为了生成相对路由,你需要知道相对于绝对路由的路径(start route)。你可以从当前的 RequestHeader中获取start route。因此,生成相对路由需要传入当前的RequestHeader或者String形式的start route参数。

以下面的controller endpoint为例:

package controllersimport javax.inject._import play.api.mvc._@Singletonclass 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!")  }}

注意:当前request被隐式地传递给视图模板。

conf/routes 文件如下配置:

GET     /foo/bar/hello              controllers.Relative.helloviewGET     /hello/:name                controllers.Relative.hello(name)

你可以使用前面的反向路由,并包含一个额外的 relative 调用:

@(name: String)(implicit request: RequestHeader)

Hello @name

Absolute LinkRelative Link

注意:从 controller 传入的 Request 被转换为 RequestHeader,并在视图中被标记为implicit。然后被隐式传给相对调用

当请求 /foo/bar/hello 时,生成的HTML看起来像下面这样:

            Bob              Absolute Link      Relative Link    

默认控制器

Play包含一个默认的控制器,提供了一些常用的工具。它们可以在route 文件中被直接引用:

# Redirects to https://www.playframework.com/ with 303 See OtherGET   /about      controllers.Default.redirect(to = "https://www.playframework.com/")# Responds with 404 Not FoundGET   /orders     controllers.Default.notFound# Responds with 500 Internal Server ErrorGET   /clients    controllers.Default.error# Responds with 501 Not ImplementedGET   /posts      controllers.Default.todo

上面的例子中, GET /about  重定向到一个外部页面,当然它也可以重定向到其它的action(如上面的 /posts例子)

自定义路由

Play提供了一个DSL用来定义内置的路由器,被称作String Interpolating Routing DSL,简写为sird。它有很多用途,如内置轻量级的Play server,提供自定义及更多高级路由功能,或者是用来mocking REST服务用来测试。

转载于:https://my.oschina.net/landas/blog/1863611

你可能感兴趣的文章
端口集&协议集
查看>>
'ascii' codec can't encode characters in position 1-5: ordinal not in range(128)
查看>>
Zabbix实战企业监控Zabbix-Proxy
查看>>
DNS轮询实现Web Server共享静态资源(多图)
查看>>
实战MHDD检测硬盘
查看>>
CCNP课堂练习六:路由器热备份(HSRP)
查看>>
Cisco交换机QOS(限速)详解
查看>>
MySQL的三种安装方式
查看>>
Windows 7用BCDEdit添加、删除XP引导项
查看>>
【Excel技巧】如何批量去掉身份证号前的单引号
查看>>
Cisco IP Phone 7912使用方法简介
查看>>
ip cef 命令介绍及CEF技术浅析
查看>>
Working with System Permissions
查看>>
《CSS权威指南》基础复习+查漏补缺
查看>>
【.NET版月经问题】之二【引用类型参数就是按引用传递吗?】
查看>>
数据结构基础(20) --图的存储结构
查看>>
Metasploit相关视频
查看>>
Failed deleting my ephemeral node
查看>>
Oracle Database字符集(2)--基本概念
查看>>
fdupes 删除指定目录下重复文件
查看>>