Sinatra — 轻量级 REST 微服务框架

Sinatra 是 Ruby 世界最著名的微框架。它相当于 Python 的 Flask/FastAPI——用最少的代码快速搭建 RESTful API。与 Rails 的"大而全"不同,Sinatra 只提供路由和请求/响应处理,让你从零开始组装自己的技术栈。

为什么选择 Sinatra?

  • 极简主义:一个文件、几行代码就能启动 HTTP 服务
  • 灵活性:不强制任何架构约定,你可以自由组合 ORM、模板引擎等
  • 微服务友好:轻量级,适合构建独立的小服务
  • 易于理解:没有 Rails 那样的魔法层,请求到响应的流程一目了然

运行示例

本章节的代码可以通过 CLI 运行完整演示:

hello awesome sinatra  # 查看 Sinatra REST API 完整演示

演示包含两部分:

  1. 真实 Sinatra REST API(Section 1):RealTaskAPI - Sinatra::Base + Sequel + Rack::Test,生产级用法参考(推荐)
  2. 内存模拟(Section 2):MemoryTaskStore + TaskApp 展示 REST API 内部实现模式,理解 Sinatra 路由原理

建议先运行演示代码,再看文档理解原理。

第一个 Sinatra 应用

require "sinatra"

get "/" do
  "Hello, Sinatra!"
end

get "/hello/:name" do
  "你好,#{params['name']}!"
end

post "/api/users" do
  request.body.rewind
  data = JSON.parse(request.body.read)
  content_type :json
  { user: data }.to_json
end

运行方式:

$ bundle exec ruby app.rb
# 默认启动 http://localhost:4567

路由系统

Sinatra 的路由非常直观,支持所有 HTTP 方法:

# GET 请求
get "/users" do
  # 列出所有用户
end

# POST 请求
post "/users" do
  # 创建用户
end

# PUT 请求
put "/users/:id" do
  # 更新用户
end

# DELETE 请求
delete "/users/:id" do
  # 删除用户
end

# 带参数的路由
get "/posts/:year/:month/:slug" do
  year  = params['year']
  month = params['month']
  slug  = params['slug']
end

# 正则表达式路由
get %r{/items/([\w]+)} do
  capture_groups[0]  # 访问正则匹配的第一个分组
end

# 带条件的路由
get "/api/v2/data", provides: :json do
  { version: "v2" }.to_json
end

请求处理

# 获取请求信息
get "/request-info" do
  request_method = request.request_method
  path           = request.path_info
  ip             = request.ip
  user_agent     = request.user_agent
  
  "Method: #{request_method}, Path: #{path}, IP: #{ip}"
end

# 处理 JSON 请求体
post "/api/data" do
  content_type :json
  request.body.rewind
  payload = JSON.parse(request.body.read)
  { status: "ok", received: payload }.to_json
end

# 重定向
get "/old-page" do
  redirect "/new-page"
end

# 停止执行
get "/forbidden" do
  halt 403, "你没有访问权限"
end

过滤器(Before/After)

# 请求前执行
before do
  content_type :json
  @start_time = Time.now
end

# 指定路径的过滤器
before "/api/*" do
  authenticate! unless request.path == "/api/health"
end

# 请求后执行
after do
  elapsed = Time.now - @start_time
  logger.info "请求耗时: #{elapsed}ms"
end

完整示例:REST API

require "sinatra"
require "json"

class App < Sinatra::Base
  set :bind, "0.0.0.0"
  set :port, 4567

  # 内存数据库(实际项目用 Sequel/ActiveRecord)
  @tasks = [
    { id: 1, title: "Learning Ruby", status: "in_progress" },
    { id: 2, title: "Building API",   status: "pending" }
  ]
  @next_id = 3

  # 列出所有任务
  get "/api/tasks" do
    { tasks: @tasks }.to_json
  end

  # 获取单个任务
  get "/api/tasks/:id" do
    task = @tasks.find { |t| t["id"] == params["id"].to_i }
    halt 404, { error: "Task not found" }.to_json unless task
    task.to_json
  end

  # 创建任务
  post "/api/tasks" do
    request.body.rewind
    body = JSON.parse(request.body.read)
    new_task = {
      "id" => (@next_id += 1),
      "title" => body["title"],
      "status" => "pending"
    }
    @tasks << new_task
    status 201
    new_task.to_json
  end

  # 更新任务
  put "/api/tasks/:id" do
    task = @tasks.find { |t| t["id"] == params["id"].to_i }
    halt 404, { error: "Task not found" }.to_json unless task
    request.body.rewind
    body = JSON.parse(request.body.read)
    task["title"] = body["title"] if body["title"]
    task["status"] = body["status"] if body["status"]
    task.to_json
  end

  # 删除任务
  delete "/api/tasks/:id" do
    task = @tasks.find { |t| t["id"] == params["id"].to_i }
    halt 404, { error: "Task not found" }.to_json unless task
    @tasks.delete(task)
    status 204
    ""
  end

  # 健康检查
  get "/health" do
    { status: "ok", version: "1.0.0" }.to_json
  end
end

App.run!

测试 API:

# 列出任务
$ curl http://localhost:4567/api/tasks

# 创建任务
$ curl -X POST http://localhost:4567/api/tasks \
  -H "Content-Type: application/json" \
  -d '{"title": "New Task"}'

部署

Sinatra 应用可通过多种方式部署:

  • Puma:推荐的生产服务器,支持多线程

    # config.ru
    require_relative "./app"
    run App
    
    $ bundle exec puma -p 3000
    
  • Passenger:Nginx 集成的部署方案

  • Docker:轻量级容器化部署,非常适合微服务

与 FastAPI 对比

特性Sinatra (Ruby)FastAPI (Python)
路由定义DSL get/post/put/delete装饰器 @app.get/post
请求参数params['name']函数参数 + Pydantic
JSON 响应手动 .to_json自动序列化
OpenAPI 文档需要 rswag gem自动生成
异步支持需要 async-sinatra原生 async/await
启动速度~1s~0.5s

本章要点

  • Sinatra 是 Ruby 微服务的首选,相当于 Python Flask/FastAPI
  • 路由系统直观,支持所有 HTTP 方法和参数模式
  • Before/After 过滤器实现中间件模式
  • 生产环境使用 Puma 作为服务器
  • 适合:小型服务、API 后端、原型快速验证
  • 不适合:需要完整 ORM、模板、邮件等功能的复杂 Web 应用 → 选择 Rails 或 Hanami

继续学习

💡 提示:Sinatra 的核心理念是"少即是多"。当你发现自己在 Sinatra 中手写越来越多的基础设施(数据库连接池、模板引擎、邮件发送),可能就是升级到 Rails 或 Hanami 的时候了。