如何在Plumber API中与`req$postBody`进行有效交互?
问题描述
我们团队正在用vetiver包将模型部署到生产环境,预测端点接收JSON请求的功能正常,但需要在请求发送到预测端点前,对请求体做检查和数据补充。目前的问题是没法和req$postBody有效交互。
我们试过在过滤器端点里用jsonlite::fromJSON(req$postBody)转换请求,也试过直接对req$postBody执行ncol()这类操作,代码示例如下:
library(plumber) library(vetiver) cars_lm <- lm(mpg ~ ., data = mtcars) v <- vetiver_model(cars_lm, "cars_linear") augment_data <- data.frame( 'extra_1' = 4, 'extra_2' = 2 ) pr() %>% pr_filter("/dim", function(req) {cat(ncol(req$postBody)); plumber::forward()}) %>% pr_filter("/augment", function(req) {req$postBody <- cbind(req$postBody, augment_data); plumber::forward()}) %>% vetiver_api(v) %>% pr_run()
解决方案
req$postBody本质是原始JSON字符串,不是数据框,所以直接用ncol()或cbind()这类数据框操作会失效。正确的处理流程是:解析JSON为数据框 → 检查/修改数据 → 重新序列化为JSON字符串。
修正后的代码如下:
library(plumber) library(vetiver) library(jsonlite) cars_lm <- lm(mpg ~ ., data = mtcars) v <- vetiver_model(cars_lm, "cars_linear") # 生成匹配请求行数的补充数据 get_augment_data <- function(n_rows) { data.frame( extra_1 = rep(4, n_rows), extra_2 = rep(2, n_rows) ) } pr() %>% # 检查请求体列数的过滤器 pr_filter("/predict", function(req) { # 解析JSON为数据结构 req_data <- fromJSON(req$postBody) # 统一转换为数据框(兼容单条/批量请求) if (is.list(req_data) && !is.data.frame(req_data)) { req_data <- as.data.frame(req_data) } cat("请求体列数:", ncol(req_data), "\n") plumber::forward() }) %>% # 补充数据的过滤器 pr_filter("/predict", function(req) { # 解析JSON为数据框 req_data <- fromJSON(req$postBody) # 处理单条请求的列表格式 if (is.list(req_data) && !is.data.frame(req_data)) { req_data <- as.data.frame(req_data) } # 拼接补充数据,保证行数匹配 augmented_data <- cbind(req_data, get_augment_data(nrow(req_data))) # 重新序列化为JSON,覆盖原请求体 req$postBody <- toJSON(augmented_data, auto_unbox = FALSE) plumber::forward() }) %>% vetiver_api(v) %>% pr_run(port = 8080)
关键注意点
- 兼容单条/批量请求:单条JSON请求解析后是列表,批量请求是数据框,需统一转换为数据框处理。
- 保证数据行数匹配:批量请求时,补充数据的行数要和请求数据一致,避免拼接报错。
- 序列化格式正确:用
auto_unbox = FALSE确保输出的JSON符合vetiver预测端点的格式要求。
内容的提问来源于stack exchange,提问作者robtimus_prime
相关产品推荐
相关产品推荐

