类型安全的路由
所需依赖项: io.ktor:ktor-server-resources
代码示例: resource-routing
Ktor 提供了 Resources 插件,允许您实现类型安全的路由。为此,您需要创建一个类作为类型化路由,然后使用 @Resource
关键字注解此 class。请注意,@Resource
注解具有由 kotlinx.serialization 库提供的 @Serializable
行为。
Ktor 客户端提供了向服务器发出类型化请求的功能。
添加依赖项
添加 kotlinx.serialization
鉴于资源类应具有 @Serializable
行为,您需要按照 Setup 部分的描述添加 Kotlin serialization 插件。
添加 Resources 依赖项
要使用 Resources
,您需要在构建脚本中包含 ktor-server-resources
artifact:
安装 Resources
要在应用程序中安装 Resources
插件, 请将其传递给指定
install
函数。 以下代码片段展示了如何安装 Resources
... - ... 在
embeddedServer
函数调用内部。 - ... 在显式定义的
module
(它是Application
类的扩展函数) 内部。
创建资源类
每个资源类都应具有 @Resource
注解。 下面,我们将介绍资源类的几个示例——定义单个路径段、查询和路径形参等。
资源 URL
以下示例展示了如何定义 Articles
类,该类指定了响应 /articles
路径的资源。
import io.ktor.resources.*
@Resource("/articles")
class Articles()
带查询形参的资源
下面的 Articles
类具有 sort
字符串属性,该属性充当查询形参并允许您定义一个响应以下路径且带 sort
查询形参的资源:/articles?sort=new
。
@Resource("/articles")
class Articles(val sort: String? = "new")
带嵌套类的资源
您可以嵌套类来创建包含多个路径段的资源。请注意,在这种情况下,嵌套类应具有一个带有外部类类型的属性。 以下示例显示了响应 /articles/new
路径的资源。
@Resource("/articles")
class Articles() {
@Resource("new")
class New(val parent: Articles = Articles())
}
带路径形参的资源
以下示例演示了如何添加嵌套的 {id}
整型路径形参,该形参匹配一个路径段并将其捕获为名为 id
的形参。
@Resource("/articles")
class Articles() {
@Resource("{id}")
class Id(val parent: Articles = Articles(), val id: Long)
}
例如,此资源可用于响应 /articles/12
。
示例:用于 CRUD 操作的资源
让我们总结上述示例,并为 CRUD 操作创建 Articles
资源。
@Resource("/articles")
class Articles(val sort: String? = "new") {
@Resource("new")
class New(val parent: Articles = Articles())
@Resource("{id}")
class Id(val parent: Articles = Articles(), val id: Long) {
@Resource("edit")
class Edit(val parent: Id)
}
}
此资源可用于列出所有文章、发布新文章、编辑文章等。我们将在下一章中看到如何为此资源定义路由处理程序。
您可以在此处找到完整示例:resource-routing。
定义路由处理程序
要为类型化资源定义路由处理程序,您需要将资源类传递给动词函数(如 get
、post
、put
等)。 例如,下面的路由处理程序响应 /articles
路径。
@Resource("/articles")
class Articles()
fun Application.module() {
install(Resources)
routing {
get<Articles> { articles ->
// Get all articles ...
call.respondText("List of articles: $articles")
}
}
}
以下示例展示了如何为示例:用于 CRUD 操作的资源中创建的 Articles
资源定义路由处理程序。请注意,在路由处理程序内部,您可以将 Article
作为形参访问并获取其属性值。
fun Application.module() {
install(Resources)
routing {
get<Articles> { article ->
// Get all articles ...
call.respondText("List of articles sorted starting from ${article.sort}")
}
get<Articles.New> {
// Show a page with fields for creating a new article ...
call.respondText("Create a new article")
}
post<Articles> {
// Save an article ...
call.respondText("An article is saved", status = HttpStatusCode.Created)
}
get<Articles.Id> { article ->
// Show an article with id ${article.id} ...
call.respondText("An article with id ${article.id}", status = HttpStatusCode.OK)
}
get<Articles.Id.Edit> { article ->
// Show a page with fields for editing an article ...
call.respondText("Edit an article with id ${article.parent.id}", status = HttpStatusCode.OK)
}
put<Articles.Id> { article ->
// Update an article ...
call.respondText("An article with id ${article.id} updated", status = HttpStatusCode.OK)
}
delete<Articles.Id> { article ->
// Delete an article ...
call.respondText("An article with id ${article.id} deleted", status = HttpStatusCode.OK)
}
}
}
以下是处理每个 endpoint 请求的一些提示:
get<Articles>
此路由处理程序旨在根据
sort
查询形参返回所有文章。 例如,这可能是一个包含所有文章的 HTML 页面或 JSON 对象。get<Articles.New>
此 endpoint 响应一个网页表单,其中包含用于创建新文章的字段。
post<Articles>
post<Articles>
endpoint 旨在接收使用网页表单发送的形参。 Ktor 还允许您使用ContentNegotiation
插件以对象形式接收 JSON 数据。get<Articles.Id>
此路由处理程序旨在返回具有指定标识符的文章。 这可能是一个显示文章的 HTML 页面或一个包含文章数据的 JSON 对象。
get<Articles.Id.Edit>
此 endpoint 响应一个网页表单,其中包含用于编辑现有文章的字段。
put<Articles.Id>
与
post<Articles>
endpoint 类似,put
处理程序接收使用网页表单发送的表单形参。delete<Articles.Id>
此路由处理程序删除具有指定标识符的文章。
您可以在此处找到完整示例:resource-routing。
从资源构建链接
除了将资源定义用于路由,它们还可以用于构建链接。这有时被称为 反向路由。如果需要将这些链接添加到使用 HTML DSL 创建的 HTML 文档中,或者需要生成重定向响应,那么从资源构建链接可能会很有帮助。
Resources
插件使用重载的 href 方法扩展了 Application
,该方法允许您从 Resource
生成链接。例如,以下代码片段为上方定义的 Edit
资源创建了一个链接:
val link: String = href(Articles.Id.Edit(Articles.Id(id = 123)))
由于祖父 Articles
资源定义了默认值为 new
的 sort
查询形参,因此 link
变量包含:
/articles/123/edit?sort=new
要生成指定主机和协议的 URL,您可以向 href
方法提供 URLBuilder
。也可以使用 URLBuilder
指定额外的查询形参,如本示例所示:
val urlBuilder = URLBuilder(URLProtocol.HTTPS, "ktor.io", parameters = parametersOf("token", "123"))
href(Articles(sort = null), urlBuilder)
val link: String = urlBuilder.buildString()
随后 link
变量包含:
https://ktor.io/articles?token=123
示例
以下示例展示了如何将从资源构建的链接添加到 HTML 响应中:
get {
call.respondHtml {
body {
this@module.apply {
p {
val link: String = href(Articles())
a(link) { +"Get all articles" }
}
p {
val link: String = href(Articles.New())
a(link) { +"Create a new article" }
}
p {
val link: String = href(Articles.Id.Edit(Articles.Id(id = 123)))
a(link) { +"Edit an exising article" }
}
p {
val urlBuilder = URLBuilder(URLProtocol.HTTPS, "ktor.io", parameters = parametersOf("token", "123"))
href(Articles(sort = null), urlBuilder)
val link: String = urlBuilder.buildString()
i { a(link) { +link } }
}
}
}
}
}
您可以在此处找到完整示例:resource-routing。