Plumber 与 REST 入门
理解 REST 原则,并将 R 函数注释为 API 端点
Plumber 与 REST 入门 是 CoddyKit 上的免费 R Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 R Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 R Academy 课程共包含 4 节课。
什么是 REST API
REST(表述性状态转移)API 是一种通过 HTTP 提供数据和操作的 Web 服务。主要原则包括:
- 无状态:每个请求都包含所需的全部信息;服务器端不保存会话。
- 面向资源:端点表示资源(
/users、/predictions)。 - 标准 HTTP 动词:GET(读取)、POST(创建)、PUT(更新)、DELETE(删除)。
- JSON:请求体和响应体的标准数据格式。
# REST API concepts in HTTP terms:
# GET /api/model/predict?x=5 -> read a prediction
# POST /api/model/train -> create a new model
# GET /api/data/summary -> read data summary
# DELETE /api/cache/flush -> remove cached results
# Plumber maps R functions to these HTTP endpoints
cat('REST: stateless, resource-oriented, JSON responses')plumber 注释语法
plumber 使用以 #* 开头的特殊注释来定义 API 端点。请将注释放在处理该端点的 R 函数正上方。函数参数会映射到请求参数;返回值会成为 JSON 响应体。
# plumber.R
library(plumber)
#* @get /ping
function() {
list(status = 'ok', time = Sys.time())
}
#* @get /add
#* @param a:int First number
#* @param b:int Second number
function(a, b) {
list(result = as.integer(a) + as.integer(b))
}pr() — 创建 Plumber 路由器
pr('plumber.R') 会读取 plumber 文件,并创建一个注册所有带注释端点的路由器对象。路由器是您在启动前进行配置的中心对象(添加过滤器、序列化器等)。
library(plumber)
# Create a router from a plumber file
api <- pr('plumber.R')
# Inspect registered routes
print(api$routes)
# Alternatively, define inline without a file:
api <- pr() |>
pr_get('/ping', function() list(status = 'ok')) |>
pr_post('/echo', function(req) req$body)pr_run() — 启动服务器
pr_run(router, host, port) 会启动 plumber API 服务器。默认情况下,它绑定到 127.0.0.1:8000。将 host = '0.0.0.0' 设置为接受来自任何网络接口的连接(Docker 或远程访问所必需)。
library(plumber)
api <- pr('plumber.R')
# Start server on localhost port 8000
# pr_run(api, host = '127.0.0.1', port = 8000)
# For Docker/remote access, bind to all interfaces
# pr_run(api, host = '0.0.0.0', port = 8000)
# View auto-generated Swagger docs in browser
# (automatically available at /docs or /__docs__/ endpoint)
cat('Swagger UI auto-generated at http://localhost:8000/__docs__/')@get 注释
#* @get /path 注释会将 GET 请求映射到相应函数。查询字符串参数(例如 ?name=Alice)会自动作为 R 函数参数传入。如果没有提供转换注释,参数会以字符字符串形式传入。
# plumber.R
#* Greet a user by name
#* @param name:str The name to greet
#* @get /greet
function(name = 'World') {
list(
message = paste('Hello,', name),
timestamp = format(Sys.time(), '%Y-%m-%d %H:%M:%S')
)
}
# GET /greet?name=Alice
# -> {"message":"Hello, Alice","timestamp":"2026-01-01 12:00:00"}@post 注释
#* @post /path 注释会将 POST 请求映射到相应函数。请求体(通常为 JSON)可通过特殊的 req 参数访问,即 req$body(当请求体为 JSON 时,这是一个已解析的列表)。POST 用于创建资源或触发计算的操作。
# plumber.R
#* Run a linear model prediction
#* @post /predict
function(req) {
# req$body is already parsed from JSON
input_data <- as.data.frame(req$body)
# Run prediction with a pre-loaded model
predictions <- predict(trained_model, newdata = input_data)
list(
predictions = as.numeric(predictions),
n = nrow(input_data)
)
}JSON 序列化器
默认情况下,plumber 使用 jsonlite 将返回值序列化为 JSON。#* @serializer json 注释可明确指定这一点。您可以在注释中以 JSON 列表的形式指定选项,从而配置美化打印或空值处理等序列化器选项。
# Default: automatic JSON serialization
#* @get /data
function() {
list(values = 1:5, labels = c('a', 'b', 'c', 'd', 'e'))
}
# Explicit JSON serializer with options
#* @serializer json list(na = 'null', auto_unbox = TRUE)
#* @get /data_explicit
function() {
list(value = 42, missing = NA)
}
# With auto_unbox=TRUE: {"value":42} not {"value":[42]}HTTP 动词 — PUT、DELETE、PATCH
plumber 通过相应的注释支持所有标准 HTTP 动词:
#* @put /path:完整替换资源。#* @delete /path:删除资源。#* @patch /path:部分更新资源。#* @head /path:仅返回标头(无正文)。
# plumber.R — CRUD-style endpoints
#* Update a model configuration
#* @put /config/<model_id>
function(model_id, req) {
config <- req$body
save_config(model_id, config)
list(updated = model_id, config = config)
}
#* Remove cached results
#* @delete /cache/<key>
function(key) {
cache_env <- globalenv()$cache
rm(list = key, envir = cache_env)
list(deleted = key)
}路径参数
路径参数使用尖括号定义在路由中:/user/。plumber 会从 URL 中提取该值,并将其作为同名函数参数传入。这些参数不同于查询参数(查询参数出现在 ? 之后)。
# plumber.R
#* Get stats for a specific dataset
#* @param dataset_id:str The dataset identifier
#* @get /datasets/<dataset_id>/stats
function(dataset_id) {
if (!dataset_id %in% available_datasets()) {
stop(paste('Dataset not found:', dataset_id))
}
ds <- load_dataset(dataset_id)
list(
id = dataset_id,
rows = nrow(ds),
cols = ncol(ds),
names = names(ds)
)
}错误处理
当 R 函数抛出错误时,plumber 会捕获该错误,并返回包含 JSON 错误消息的 500 HTTP 响应。对于面向用户的 API,请使用 res$status 明确返回适当的 HTTP 状态码,并在验证错误时使用 stop()。
# plumber.R
#* Divide two numbers safely
#* @get /divide
function(a, b, res) {
a <- suppressWarnings(as.numeric(a))
b <- suppressWarnings(as.numeric(b))
if (is.na(a) || is.na(b)) {
res$status <- 400 # Bad Request
return(list(error = 'Both a and b must be numeric'))
}
if (b == 0) {
res$status <- 422 # Unprocessable Entity
return(list(error = 'Division by zero is not allowed'))
}
list(result = a / b)
}自动生成的 Swagger 文档
plumber 会根据您的注释自动生成交互式 Swagger UI 文档。服务器运行时,访问 /__docs__/ 即可查看所有端点及其参数,并在浏览器中尝试调用。使用 #* @tag 可按逻辑对端点分组。
# plumber.R with Swagger metadata
#* @apiTitle My ML Prediction API
#* @apiDescription Serves predictions from trained R models
#* @apiVersion 1.0.0
#* @tag model
#* @get /health
function() list(status = 'healthy')
#* Predict house price
#* @tag prediction
#* @param sqft:dbl Square footage
#* @param bedrooms:int Number of bedrooms
#* @get /predict
function(sqft = 1000, bedrooms = 3) {
pred <- predict(price_model, data.frame(sqft = as.numeric(sqft),
bedrooms = as.integer(bedrooms)))
list(predicted_price = round(as.numeric(pred), 2))
}快速检查
在 plumber 中,查询参数(例如 /greet?name=Alice)和路径参数(例如 /user/42)有什么区别?
plumber 与 REST 回顾
《plumber 与 REST 简介》的要点:
- REST:无状态、面向资源、使用标准 HTTP 动词,并返回 JSON。
- plumber 使用函数上方的
#*注释,将 R 函数映射到端点。 pr('file.R')会创建路由器;pr_run(api, host, port)会启动服务器。#* @get /path处理 GET;#* @post /path处理 POST。- 路径参数:
/user/;查询参数:/search?term=foo。 - 返回命名列表——plumber 会自动将其序列化为 JSON。
- Swagger UI 会根据您的注释在
/__docs__/自动生成。
# Complete minimal plumber API
library(plumber)
#* @apiTitle Simple Prediction API
#* Health check
#* @get /health
function() list(status = 'ok')
#* Predict mpg from weight
#* @param wt:dbl Car weight (1000 lbs)
#* @get /predict
function(wt = 3.0) {
pred <- predict(lm(mpg ~ wt, data = mtcars),
newdata = data.frame(wt = as.numeric(wt)))
list(wt = as.numeric(wt), predicted_mpg = round(pred, 2))
}
# Run:
# api <- pr('plumber.R')
# pr_run(api, port = 8000)常见问题解答
「Plumber 与 REST 入门」课时是免费的吗?
是的 — 「Plumber 与 REST 入门」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 R Academy 课程的其余内容,请升级到 CoddyKit PRO。 R Academy 课程共包含 4 节课。
「Plumber 与 REST 入门」这节课中我会学到什么?
理解 REST 原则,并将 R 函数注释为 API 端点 你通过在浏览器中直接运行的动手代码来练习 R Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 R Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 R Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「Plumber 与 REST 入门」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 R Academy 课中编写并运行代码吗?
能。每节 R Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- Plumber 与 REST 入门
- 创建 GET 和 POST 端点
- 身份验证与 API 安全
- 将 Plumber API 部署到生产环境