为典型的 REST 实现嵌套 Ktor 路由匹配器和处理程序的正确方法是啥?
Posted
技术标签:
【中文标题】为典型的 REST 实现嵌套 Ktor 路由匹配器和处理程序的正确方法是啥?【英文标题】:What's the correct way to nest Ktor route matchers and handlers for a typical REST implementation?为典型的 REST 实现嵌套 Ktor 路由匹配器和处理程序的正确方法是什么? 【发布时间】:2018-03-28 18:34:44 【问题描述】:我在理解将 Ktor 的 DSL 用于request routing 的正确方法时遇到了一些麻烦。
问题是,当我测试我的 API 并尝试 GET /nomenclature/articles/categories
应该返回所有文章类别的列表时,我得到的是 Invalid article specified
当 articleId
参数无效时我为 /nomenclature/articles/articleId
路由返回的消息是无效的.
这是我的代码:
route("/nomenclature")
method(HttpMethod.Get)
handle call.respondText("The resource you accessed is not a valid REST resource, but a parent node. Children nodes include articles, categories, subcategories and so on.")
route("articles")
route("categories")
get("categoryId?")
val categoryIdParam = call.parameters["categoryId"]
if (categoryIdParam != null)
val categoryId = categoryIdParam.toIntOrNull()
if (categoryId != null)
val category = articlesDAO.findCategoryById(categoryId)
if (category != null) call.respond(category)
else call.respondText("Category not found", status = HttpStatusCode.NotFound)
else call.respondText("Invalid category ID specified", status = HttpStatusCode.BadRequest)
else
val categories = articlesDAO.getCategories()
if (categories != null) call.respond(categories)
else call.respondText("No categories found", status = HttpStatusCode.NotFound)
route("subcategories")
get("subcategoryId?")
val subcategoryIdParam = call.parameters["subcategoryId"]
if (subcategoryIdParam != null)
val subcategoryId = subcategoryIdParam.toIntOrNull()
if (subcategoryId != null)
val subcategory = articlesDAO.findSubcategoryById(subcategoryId)
if (subcategory != null) call.respond(subcategory)
else call.respondText("Subcategory not found", status = HttpStatusCode.NotFound)
else call.respondText("Invalid subcategory ID specified", status = HttpStatusCode.BadRequest)
else
val subcategories = articlesDAO.getCategories()
if (subcategories != null) call.respond(subcategories)
else call.respondText("No subcategories found", status = HttpStatusCode.NotFound)
get("types")
val articleTypes = articlesDAO.getArticleTypes()
if (articleTypes != null) call.respond(articleTypes)
else call.respondText("No article types found", status = HttpStatusCode.NotFound)
get("wholePackagings")
val wholePackagings = articlesDAO.getWholePackagings()
if (wholePackagings != null) call.respond(wholePackagings)
else call.respondText("No whole packagings found", status = HttpStatusCode.NotFound)
get("fractionsPackagings")
val fractionsPackagings = articlesDAO.getFractionPackagings()
if (fractionsPackagings != null) call.respond(fractionsPackagings)
else call.respondText("No fractions packagings found", status = HttpStatusCode.NotFound)
get("articleId?")
val articleIdParam = call.parameters["articleId"]
if (articleIdParam != null)
val articleId = articleIdParam.toIntOrNull()
if (articleId != null)
val article = articlesDAO.findArticleById(articleId)
if (article != null) call.respond(article) else call.respondText("No article found", status = HttpStatusCode.NotFound)
else call.respondText("Invalid article ID specified", status = HttpStatusCode.BadRequest)
else
val articles = articlesDAO.getArticles()
if (articles != null) call.respond(articles) else call.respondText("No articles found", status = HttpStatusCode.NotFound)
如果有人能帮助我,提供一个基本但比较全面的示例,说明如何使用所有 Ktor 路由匹配器和处理程序(包括以嵌套资源方式),那就太好了。
编辑:我已经使用不同的方法重写了路由,但我仍然想知道为什么我以前的版本没有按预期工作。 这是我的第二种方法,似乎可以按预期工作(我已经测试过了):
routing
route("/v1")
route("stock")
get
val stock = stockDAO.getAllStock()
if (stock != null) call.respond(stock) else call.respondText("No stock found", status = HttpStatusCode.NotFound)
get("locationId")
val locationIdParam = call.parameters["locationId"]
if (locationIdParam != null)
val locationId = locationIdParam.toIntOrNull()
if (locationId != null)
val stock = stockDAO.getStockByLocationId(locationId)
if (stock != null) call.respond(stock) else call.respondText("No stock found", status = HttpStatusCode.NotFound)
else call.respondText("ERROR: Invalid location ID specified.", status = HttpStatusCode.BadRequest)
route("nomenclature")
get
call.respondText("The resource you accessed is not a valid REST resource, but a parent node. Children nodes include articles, categories, subcategories and so on.")
route("articles")
get
val articles = articlesDAO.getArticles()
if (articles != null) call.respond(articles) else call.respondText("No articles found", status = HttpStatusCode.NotFound)
get("articleId")
val articleIdParam = call.parameters["articleId"]
if (articleIdParam != null)
val articleId = articleIdParam.toIntOrNull()
if (articleId != null)
val article = articlesDAO.findArticleById(articleId)
if (article != null) call.respond(article) else call.respondText("No article found", status = HttpStatusCode.NotFound)
else call.respondText("Invalid article ID specified", status = HttpStatusCode.BadRequest)
route("categories")
get
val categories = articlesDAO.getCategories()
if (categories != null) call.respond(categories)
else call.respondText("No categories found", status = HttpStatusCode.NotFound)
get("categoryId")
val categoryIdParam = call.parameters["categoryId"]
if (categoryIdParam != null)
val categoryId = categoryIdParam.toIntOrNull()
if (categoryId != null)
val category = articlesDAO.findCategoryById(categoryId)
if (category != null) call.respond(category)
else call.respondText("Category not found", status = HttpStatusCode.NotFound)
else call.respondText("Invalid category ID specified", status = HttpStatusCode.BadRequest)
route("subcategories")
get
val subcategories = articlesDAO.getSubcategories()
if (subcategories != null) call.respond(subcategories)
else call.respondText("No subcategories found", status = HttpStatusCode.NotFound)
get("subcategoryId")
val subcategoryIdParam = call.parameters["subcategoryId"]
if (subcategoryIdParam != null)
val subcategoryId = subcategoryIdParam.toIntOrNull()
if (subcategoryId != null)
val subcategory = articlesDAO.findSubcategoryById(subcategoryId)
if (subcategory != null) call.respond(subcategory)
else call.respondText("Subcategory not found", status = HttpStatusCode.NotFound)
else call.respondText("Invalid subcategory ID specified", status = HttpStatusCode.BadRequest)
get("types")
val articleTypes = articlesDAO.getArticleTypes()
if (articleTypes != null) call.respond(articleTypes)
else call.respondText("No article types found", status = HttpStatusCode.NotFound)
get("wholePackagings")
val wholePackagings = articlesDAO.getWholePackagings()
if (wholePackagings != null) call.respond(wholePackagings)
else call.respondText("No whole packagings found", status = HttpStatusCode.NotFound)
get("fractionsPackagings")
val fractionsPackagings = articlesDAO.getFractionPackagings()
if (fractionsPackagings != null) call.respond(fractionsPackagings)
else call.respondText("No fractions packagings found", status = HttpStatusCode.NotFound)
【问题讨论】:
【参考方案1】:原因是您没有为GET /nomenclature/articles/categories
指定任何处理程序。
您为/articles
设置路由,然后为/categories
设置路由,但内部没有处理程序。
里面唯一的就是get("categoryId?")
,不匹配,因为没有tailcard。
得到/nomenclature/articles/articleId
的原因是,它首先尝试匹配/articles
,但由于它无法匹配/categories
(没有处理程序),它继续寻找,最后找到最后一条路线,其中包含通配符参数和匹配项。
如果您想为该特定路由设置处理程序,方法如下:
route("articles")
route("categories")
get("categoryId?")
...
get
... your code ...
【讨论】:
以上是关于为典型的 REST 实现嵌套 Ktor 路由匹配器和处理程序的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
没有 PK 的 Django REST Framework 嵌套路由
ZF_react react-router 使用正则匹配路由,Switch路由,嵌套路由的实现 路由保护 NavLink withRouter