为典型的REST实现嵌套Ktor路由匹配器和处理程序的正确方法是什么?

时间:2017-10-17 06:33:34

标签: kotlin url-routing ktor

我在理解使用Ktor的DSL request routing的正确方法时遇到了一些麻烦。 问题在于,当我测试我的API并尝试GET /nomenclature/articles/categories应该返回所有文章类别的列表时,我得到的是Invalid article specified,当我返回/nomenclature/articles/{articleId}路由的消息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 个答案:

答案 0 :(得分:3)

原因是您没有为GET /nomenclature/articles/categories指定任何处理程序。

您为/articles设置路由,然后为/categories设置路由,但内部没有处理程序。 里面唯一的东西是get("{categoryId?}"),它没有匹配,因为没有尾卡。

获取/nomenclature/articles/{articleId}的原因是,它首先尝试匹配/articles,但由于它无法匹配/categories(没有处理程序),它继续查找并最终查找最后一个路由,包含一个通配符参数和匹配项。

如果您想为该特定路线设置处理程序,请按以下步骤操作:

route("articles") {
    route("categories") {
        get("{categoryId?}") {
            ...
        }
        get {
            ... your code ...
        }
    }
}