如何动态地将 curl 变量发送到水管工函数?

How to send curl variables to plumber function dynamically?

我想根据任意数量的输入变量动态调用管道工 API。我需要将 curl 输入映射到函数名称的输入。例如,如果函数有一个输入 hi,那么 curl -s --data 'hi=2' 意味着 hi=2 应该作为输入参数传递给函数。这可以直接在 R 中使用 match.call() 完成,但是在通过管道工 API.

调用它时失败了

取函数

#' @post /API
#' @serializer unboxedJSON
tmp <- function(hi) {

  out <- list(hi=hi)

  out <- toJSON(out, pretty = TRUE, auto_unbox = TRUE)

  return(out)

}

tmp(hi=2)
out: {hi:2}

然后

curl -s --data 'hi=10' http://127.0.0.1/8081/API
out: {\n  \"hi\": \"2\"\n}

一切看起来都很好。但是,取函数

#' @post /API
#' @serializer unboxedJSON
tmp <- function(...) {

  out <- match.call() %>%
         as.list() %>%
         .[2:length(.)] # %>%

  out <- toJSON(out, pretty = TRUE, auto_unbox = TRUE)

  return(out)

}
tmp(hi=2)
out: {hi:2}

然后

curl -s --data 'hi=10' http://127.0.0.1/8081/API
out: {"error":"500 - Internal server error","message":"Error: No method asJSON S3 class: R6\n"}

在实践中,我真正想做的是加载我的 ML 模型来预测水管工的分数 API。例如

model <- readRDS('model.rds') # Load model as a global variable

predict_score <- function(...) {

    df_in <- match.call() %>%
        as.list() %>%
        .[2:length(.)] %>%
        as.data.frame()

    json_out <- list(
        score_out = predict(model, df_in) %>%
        toJSON(., pretty = T, auto_unbox = T)

    return(json_out)
}

此函数在 运行 本地时按预期工作,但 运行 通过 API 通过 curl -s --data 'var1=1&var2=2...etc' http://listen_address

我收到以下错误:{"error":"500 - Internal server error","message":"Error in as.data.frame.default(x[[i]], optional = TRUE): cannot coerce class \"c(\"PlumberResponse\", \"R6\")\" to a data.frame\n"}

内部管道工将您请求中的参数与函数中的参数名称进行匹配。您可以使用 special 个参数来探索请求中的所有 args 个参数。如果您有一个名为 req 的参数,它将为您提供一个包含整个请求元数据的环境,其中之一是 req$args。然后你可以解析。前两个参数是对特殊参数 reqres 的自引用。它们是环境,不应序列化。我不建议在任何生产代码中执行此处显示的操作,因为它会打开 api 滥用。

model <- readRDS('model.rds') # Load model as a global variable

#' @post /API
#' @serializer unboxedJSON
predict_score <- function(req) {

    df_in <- as.data.frame(req$args[-(1:2)])

    json_out <- list(
        score_out = predict(model, df_in)

    return(json_out)
}

但对于您的用例,我实际上建议使用一个名为 df_in 的参数。以下是您将如何设置它。

model <- readRDS('model.rds') # Load model as a global variable

#' @post /API
#' @param df_in
#' @serializer unboxedJSON
predict_score <- function(df_in) {

    json_out <- list(
        score_out = predict(model, df_in)

    return(json_out)
}

然后用 curl

curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"df_in":{"hi":2, "othercrap":4}}' \
  http://listen_address

当请求主体以“{”开头时,管道工将使用jsonlite:fromJSON解析主体内容,并使用解析对象的名称映射到函数中的参数。

目前 github 上的 CRAN 和 master 分支都不能通过 swagger api 正确处理这个问题,但它可以通过 curl 或其他直接调用方法正常工作。下一个管道工版本将处理所有这些,我相信更多。

在此处查看对此问题的类似答案:https://github.com/rstudio/plumber/issues/512#issuecomment-605735332