入门
Sinatra 是一个用于在 Ruby 中快速创建 Web 应用程序的 DSL,只需最少的努力。
# myapp.rb
require 'sinatra'
get '/' do
'Hello world!'
end
安装所需的 gem
gem install sinatra
gem install rackup
gem install puma # or any other server (optional step)
并使用以下命令运行
ruby myapp.rb
您更改的代码只有在您重新启动服务器后才会生效。每次更改代码或使用代码重新加载器(如 rerun 或 rack-unreloader)时,请重新启动服务器。
建议您也运行 gem install puma
,Sinatra 会在可用时选择它。
路由
在 Sinatra 中,路由是 HTTP 方法与 URL 匹配模式的配对。每个路由都与一个代码块相关联。
get '/' do
.. show something ..
end
post '/' do
.. create something ..
end
put '/' do
.. replace something ..
end
patch '/' do
.. modify something ..
end
delete '/' do
.. annihilate something ..
end
options '/' do
.. appease something ..
end
link '/' do
.. affiliate something ..
end
unlink '/' do
.. separate something ..
end
路由按定义顺序匹配。第一个匹配请求的路由将被调用。
带有尾部斜杠的路由与没有尾部斜杠的路由不同。
get '/foo' do
# Does not match "GET /foo/"
end
路由模式可能包含命名参数,可以通过 params
哈希访问。
get '/hello/:name' do
# matches "GET /hello/foo" and "GET /hello/bar"
# params['name'] is 'foo' or 'bar'
"Hello #{params['name']}!"
end
您也可以通过代码块参数访问命名参数。
get '/hello/:name' do |n|
# matches "GET /hello/foo" and "GET /hello/bar"
# params['name'] is 'foo' or 'bar'
# n stores params['name']
"Hello #{n}!"
end
路由模式也可能包含 splat(或通配符)参数,可以通过 params['splat']
数组访问。
get '/say/*/to/*' do
# matches /say/hello/to/world
params['splat'] # => ["hello", "world"]
end
get '/download/*.*' do
# matches /download/path/to/file.xml
params['splat'] # => ["path/to/file", "xml"]
end
或使用代码块参数。
get '/download/*.*' do |path, ext|
[path, ext] # => ["path/to/file", "xml"]
end
使用正则表达式进行路由匹配
get /\/hello\/([\w]+)/ do
"Hello, #{params['captures'].first}!"
end
或使用代码块参数。
get %r{/hello/([\w]+)} do |c|
# Matches "GET /meta/hello/world", "GET /hello/world/1234" etc.
"Hello, #{c}!"
end
路由模式可能包含可选参数。
get '/posts/:format?' do
# matches "GET /posts/" and any extension "GET /posts/json", "GET /posts/xml" etc
end
路由也可以使用查询参数。
get '/posts' do
# matches "GET /posts?title=foo&author=bar"
title = params['title']
author = params['author']
# uses title and author variables; query is optional to the /posts route
end
顺便说一句,除非您禁用路径遍历攻击保护(请参阅 下方),否则请求路径可能会在与您的路由匹配之前被修改。
您可以通过传入一个 :mustermann_opts
哈希来自定义用于特定路由的 Mustermann 选项。
get '\A/posts\z', :mustermann_opts => { :type => :regexp, :check_anchors => false } do
# matches /posts exactly, with explicit anchoring
"If you match an anchored pattern clap your hands!"
end
它看起来像一个 条件,但它不是!这些选项将被合并到 下方 描述的全局 :mustermann_opts
哈希中。
条件
路由可能包含各种匹配条件,例如用户代理。
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
"You're using Songbird version #{params['agent'][0]}"
end
get '/foo' do
# Matches non-songbird browsers
end
其他可用的条件是 host_name
和 provides
。
get '/', :host_name => /^admin\./ do
"Admin Area, Access denied!"
end
get '/', :provides => 'html' do
haml :index
end
get '/', :provides => ['rss', 'atom', 'xml'] do
builder :feed
end
provides
搜索请求的 Accept 标头。
您可以轻松地定义自己的条件。
set(:probability) { |value| condition { rand <= value } }
get '/win_a_car', :probability => 0.1 do
"You won!"
end
get '/win_a_car' do
"Sorry, you lost."
end
对于接受多个值的条件,请使用 splat。
set(:auth) do |*roles| # <- notice the splat here
condition do
unless logged_in? && roles.any? {|role| current_user.in_role? role }
redirect "/login/", 303
end
end
end
get "/my/account/", :auth => [:user, :admin] do
"Your Account Details"
end
get "/only/admin/", :auth => :admin do
"Only admins are allowed here!"
end
返回值
路由代码块的返回值至少决定传递给 HTTP 客户端的响应主体,或者至少决定 Rack 堆栈中的下一个中间件。最常见的是,这是一个字符串,如上面的示例所示。但其他值也被接受。
您可以返回一个对象,该对象可以是有效的 Rack 响应、Rack 主体对象或 HTTP 状态代码。
- 包含三个元素的数组:
[状态 (整数),头 (哈希),响应主体 (响应 #each)]
- 包含两个元素的数组:
[状态 (整数),响应主体 (响应 #each)]
- 响应
#each
的对象,并且只将字符串传递给给定的块。 - 表示状态代码的整数。
这样,我们可以轻松地实现一个流式示例。
class Stream
def each
100.times { |i| yield "#{i}\n" }
end
end
get('/') { Stream.new }
您也可以使用 stream
辅助方法(见下文)来减少样板代码并将流式逻辑嵌入到路由中。
自定义路由匹配器
如上所示,Sinatra 内置支持使用字符串模式和正则表达式作为路由匹配器。但是,它并没有止步于此。您可以轻松地定义自己的匹配器。
class AllButPattern
def initialize(except)
@except = except
end
def to_pattern(options)
return self
end
def params(route)
return {} unless @except === route
end
end
def all_but(pattern)
AllButPattern.new(pattern)
end
get all_but("/index") do
# ...
end
请注意,上面的示例可能过于复杂,因为它也可以表示为
get /.*/ do
pass if request.path_info == "/index"
# ...
end
静态文件
静态文件从 ./public
目录提供服务。您可以通过设置 :public_folder
选项来指定不同的位置。
set :public_folder, __dir__ + '/static'
请注意,公共目录名称不包含在 URL 中。文件 ./public/css/style.css
可作为 http://example.com/css/style.css
使用。
使用 :static_cache_control
设置(见 下文)来添加 Cache-Control
头信息。
视图/模板
每种模板语言都通过其自己的渲染方法公开。这些方法只返回一个字符串。
get '/' do
erb :index
end
这将渲染 views/index.erb
。
除了模板名称,您也可以直接传入模板内容。
get '/' do
code = "<%= Time.now %>"
erb code
end
模板接受第二个参数,即选项哈希。
get '/' do
erb :index, :layout => :post
end
这将渲染嵌入在 views/post.erb
中的 views/index.erb
(默认情况下是 views/layout.erb
,如果存在)。
任何 Sinatra 不理解的选项都将传递给模板引擎。
get '/' do
haml :index, :format => :html5
end
您也可以为每种模板语言设置通用选项。
set :haml, :format => :html5
get '/' do
haml :index
end
传递给渲染方法的选项会覆盖通过 set
设置的选项。
可用选项
- locals
- 传递给文档的本地变量列表。对于部分内容很有用。示例:erb "<%= foo %>", :locals => {:foo => "bar"}
- default_encoding
- 如果无法确定,要使用的字符串编码。默认值为 settings.default_encoding。
- 视图
- 加载模板的视图文件夹。默认值为 settings.views。
- 布局
- 是否使用布局 (true 或 false)。如果它是一个符号,则指定要使用的模板。例如:erb :index, :layout => !request.xhr?
- 内容类型
- 模板生成的 Content-Type。默认值取决于模板语言。
- 范围
- 渲染模板的范围。默认值为应用程序实例。如果更改此设置,则实例变量和辅助方法将不可用。
- 布局引擎
- 用于渲染布局的模板引擎。对于不支持布局的其他语言很有用。默认值为用于模板的引擎。例如:set :rdoc, :layout_engine => :erb
- 布局选项
- 仅用于渲染布局的特殊选项。例如:set :rdoc, :layout_options => { :views => 'views/layouts' }
假设模板位于 ./views
目录下。要使用不同的视图目录
set :views, settings.root + '/templates'
要记住的一件重要的事情是,您始终必须使用符号引用模板,即使它们位于子目录中(在这种情况下,请使用::'subdir/template'
或 'subdir/template'.to_sym
)。您必须使用符号,否则渲染方法将直接渲染传递给它们的所有字符串。
文字模板
get '/' do
haml '%div.title Hello World'
end
渲染模板字符串。您可以选择指定 :path
和 :line
以获得更清晰的回溯,如果存在与该字符串关联的文件系统路径或行。
get '/' do
haml '%div.title Hello World', :path => 'examples/file.haml', :line => 3
end
可用的模板语言
某些语言有多种实现。要指定要使用的实现(并使其线程安全),您只需先要求它即可
require 'rdiscount'
get('/') { markdown :index }
Haml 模板
依赖项 | haml |
文件扩展名 | .haml |
示例 | haml :index, :format => :html5 |
Erb 模板
依赖项 | erubi 或 erb(包含在 Ruby 中) |
文件扩展名 | .erb、.rhtml 或 .erubi(仅限 Erubi) |
示例 | erb :index |
Builder 模板
依赖项 | builder |
文件扩展名 | .builder |
示例 | builder { |xml| xml.em "hi" } |
它还接受一个块用于内联模板(请参阅 示例)。
Nokogiri 模板
依赖项 | nokogiri |
文件扩展名 | .nokogiri |
示例 | nokogiri { |xml| xml.em "hi" } |
它还接受一个块用于内联模板(请参阅 示例)。
Sass 模板
依赖项 | sass-嵌入式 |
文件扩展名 | .sass |
示例 | sass :stylesheet, :style => :expanded |
Scss 模板
依赖项 | sass-嵌入式 |
文件扩展名 | .scss |
示例 | scss :stylesheet, :style => :expanded |
Liquid 模板
依赖项 | liquid |
文件扩展名 | .liquid |
示例 | liquid :index, :locals => { :key => 'value' } |
由于您无法从 Liquid 模板中调用 Ruby 方法(除了 yield
),因此您几乎总是希望将局部变量传递给它。
Markdown 模板
依赖项 | 以下任一:RDiscount,RedCarpet,kramdown,commonmarker pandoc |
文件扩展名 | .markdown,.mkd 和 .md |
示例 | markdown :index, :layout_engine => :erb |
无法从 Markdown 中调用方法,也无法向其传递局部变量。因此,您通常会将其与另一个渲染引擎结合使用。
erb :overview, :locals => { :text => markdown(:introduction) }
请注意,您也可以从其他模板中调用 markdown
方法。
%h1 Hello From Haml!
%p= markdown(:greetings)
由于您无法从 Markdown 中调用 Ruby,因此您无法使用用 Markdown 编写的布局。但是,可以通过传递 :layout_engine
选项,为模板使用与布局不同的渲染引擎。
RDoc 模板
依赖项 | RDoc |
文件扩展名 | .rdoc |
示例 | rdoc :README, :layout_engine => :erb |
无法从 RDoc 中调用方法,也无法向其传递局部变量。因此,您通常会将其与另一个渲染引擎结合使用。
erb :overview, :locals => { :text => rdoc(:introduction) }
请注意,您也可以从其他模板中调用 rdoc
方法。
%h1 Hello From Haml!
%p= rdoc(:greetings)
由于您无法从 RDoc 中调用 Ruby,因此您无法使用用 RDoc 编写的布局。但是,可以通过传递 :layout_engine
选项,为模板使用与布局不同的渲染引擎。
AsciiDoc 模板
依赖项 | Asciidoctor |
文件扩展名 | .asciidoc,.adoc 和 .ad |
示例 | asciidoc :README, :layout_engine => :erb |
由于您无法直接从 AsciiDoc 模板中调用 Ruby 方法,因此您几乎总是希望将局部变量传递给它。
Markaby 模板
依赖项 | Markaby |
文件扩展名 | .mab |
示例 | markaby { h1 "Welcome!" } |
它还接受一个块用于内联模板(请参阅 示例)。
RABL 模板
依赖项 | Rabl |
文件扩展名 | .rabl |
示例 | rabl :index |
Slim 模板
依赖项 | Slim 语言 |
文件扩展名 | .slim |
示例 | slim :index |
Yajl 模板
依赖项 | yajl-ruby |
文件扩展名 | .yajl |
示例 | yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource' |
模板源代码被评估为 Ruby 字符串,生成的 json 变量使用 #to_json
转换。
json = { :foo => 'bar' }
json[:baz] = key
:callback
和 :variable
选项可用于装饰渲染后的对象。
var resource = {"foo":"bar","baz":"qux"};
present(resource);
在模板中访问变量
模板在与路由处理程序相同的上下文中进行评估。在路由处理程序中设置的实例变量可以直接被模板访问。
get '/:id' do
@foo = Foo.find(params['id'])
haml '%h1= @foo.name'
end
或者,指定一个显式的局部变量哈希。
get '/:id' do
foo = Foo.find(params['id'])
haml '%h1= bar.name', :locals => { :bar => foo }
end
这通常用于在其他模板中渲染模板作为部分。
带有 yield
和嵌套布局的模板
布局通常只是一个调用yield
的模板。这种模板可以通过上面描述的:template
选项使用,也可以通过以下方式使用块渲染。
erb :post, :layout => false do
erb :index
end
这段代码与erb :index, :layout => :post
基本等效。
将块传递给渲染方法对于创建嵌套布局最为有用。
erb :main_layout, :layout => false do
erb :admin_layout do
erb :user
end
end
这也可以用更少的代码行来完成。
erb :admin_layout, :layout => :main_layout do
erb :user
end
目前,以下渲染方法接受块:erb
、haml
、liquid
、slim
。此外,通用render
方法也接受块。
内联模板
模板可以在源文件末尾定义。
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
!= yield
@@ index
%div.title Hello world.
注意:在需要 Sinatra 的源文件中定义的内联模板会自动加载。如果您在其他源文件中使用内联模板,请显式调用enable :inline_templates
。
命名模板
模板也可以使用顶层template
方法定义。
template :layout do
"%html\n =yield\n"
end
template :index do
'%div.title Hello World!'
end
get '/' do
haml :index
end
如果存在名为“layout”的模板,则每次渲染模板时都会使用它。您可以通过传递:layout => false
来单独禁用布局,或者通过set :haml, :layout => false
来默认禁用布局。
get '/' do
haml :index, :layout => !request.xhr?
end
关联文件扩展名
要将文件扩展名与模板引擎关联,请使用Tilt.register
。例如,如果您想使用tt
文件扩展名来表示 Haml 模板,您可以执行以下操作。
Tilt.register Tilt[:haml], :tt
添加您自己的模板引擎
首先,使用 Tilt 注册您的引擎,然后创建一个渲染方法。
Tilt.register MyAwesomeTemplateEngine, :myat
helpers do
def myat(*args) render(:myat, *args) end
end
get '/' do
myat :index
end
渲染./views/index.myat
。了解更多关于Tilt的信息。
使用自定义逻辑进行模板查找
要实现您自己的模板查找机制,您可以编写您自己的#find_template
方法。
configure do
set :views, [ './views/a', './views/b' ]
end
def find_template(views, name, engine, &block)
Array(views).each do |v|
super(v, name, engine, &block)
end
end
过滤器
在过滤器在与路由相同的上下文中评估每个请求之前,可以修改请求和响应。在过滤器中设置的实例变量可以被路由和模板访问。
before do
@note = 'Hi!'
request.path_info = '/foo/bar/baz'
end
get '/foo/*' do
@note #=> 'Hi!'
params['splat'] #=> 'bar/baz'
end
在过滤器在与路由相同的上下文中评估每个请求之后,也可以修改请求和响应。在之前过滤器和路由中设置的实例变量可以被之后过滤器访问。
after do
puts response.status
end
注意:除非您使用body
方法而不是仅仅从路由返回字符串,否则在之后过滤器中将无法获得主体,因为它是在之后生成的。
过滤器可以选择性地接受一个模式,使其仅在请求路径与该模式匹配时才被评估。
before '/protected/*' do
authenticate!
end
after '/create/:slug' do |slug|
session[:last_slug] = slug
end
与路由类似,过滤器也接受条件。
before :agent => /Songbird/ do
# ...
end
after '/blog/*', :host_name => 'example.com' do
# ...
end
助手
使用顶层helpers
方法定义辅助方法,供路由处理程序和模板使用
helpers do
def bar(name)
"#{name}bar"
end
end
get '/:name' do
bar(params['name'])
end
或者,辅助方法可以在模块中单独定义
module FooUtils
def foo(name) "#{name}foo" end
end
module BarUtils
def bar(name) "#{name}bar" end
end
helpers FooUtils, BarUtils
效果与在应用程序类中包含模块相同。
使用会话
会话用于在请求期间保持状态。如果激活,每个用户会话将拥有一个会话哈希
enable :sessions
get '/' do
"value = " << session[:value].inspect
end
get '/:value' do
session['value'] = params['value']
end
会话密钥安全性
为了提高安全性,cookie 中的会话数据使用HMAC-SHA1
用会话密钥签名。此会话密钥最优地应为适当长度的加密安全随机值,对于HMAC-SHA1
,该长度大于或等于 64 字节(512 位,128 个十六进制字符)。建议不要使用小于 32 字节随机数(256 位,64 个十六进制字符)的密钥。因此,**非常重要**的是不要随意设置密钥,而是使用安全的随机数生成器来创建它。人类在生成随机值方面非常糟糕。
默认情况下,Sinatra 会为您生成一个 32 字节的安全随机会话密钥,但它会在每次重新启动应用程序时更改。如果您有多个应用程序实例,并且让 Sinatra 生成密钥,那么每个实例将拥有不同的会话密钥,这可能不是您想要的。
为了提高安全性,建议您生成一个安全的随机密钥并将其存储在运行应用程序的每个主机上的环境变量中,以便所有应用程序实例共享同一个密钥。您应该定期将此会话密钥轮换到一个新值。以下是一些关于如何创建 64 字节密钥并设置它的示例
会话密钥生成
$ ruby -e "require 'securerandom'; puts SecureRandom.hex(64)"
99ae8af...snip...ec0f262ac
会话密钥环境变量
为 Sinatra 设置一个SESSION_SECRET
环境变量,使其值为您生成的密钥。使此值在主机重启后保持持久。由于执行此操作的方法因系统而异,因此仅供说明。
# echo "export SESSION_SECRET=99ae8af...snip...ec0f262ac" >> ~/.bashrc
会话密钥应用程序配置
设置您的应用程序配置,如果SESSION_SECRET
环境变量不可用,则安全地回退到一个安全的随机密钥
require 'securerandom'
set :session_secret, ENV.fetch('SESSION_SECRET') { SecureRandom.hex(64) }
会话配置
如果您想进一步配置它,您也可以在sessions
设置中存储一个包含选项的哈希值。
set :sessions, :domain => 'foo.com'
要将您的会话共享到 foo.com 子域上的其他应用程序,请在域名前面加上一个.,例如:
set :sessions, :domain => '.foo.com'
选择您自己的会话中间件
请注意,enable :sessions
实际上将所有数据存储在 cookie 中。这可能并不总是您想要的(例如,存储大量数据会增加您的流量)。您可以使用任何 Rack 会话中间件来做到这一点,可以使用以下方法之一。
enable :sessions
set :session_store, Rack::Session::Pool
或者使用包含选项的哈希值设置会话。
set :sessions, :expire_after => 2592000
set :session_store, Rack::Session::Pool
另一个选择是不调用enable :sessions
,而是像使用其他中间件一样引入您选择的中间件。
重要的是要注意,使用此方法时,基于会话的保护默认情况下不会启用。
还需要添加执行此操作的 Rack 中间件。
use Rack::Session::Pool, :expire_after => 2592000
use Rack::Protection::RemoteToken
use Rack::Protection::SessionHijacking
有关更多信息,请参阅“配置攻击保护”。
停止
要在过滤器或路由中立即停止请求,请使用
halt
您也可以在停止时指定状态
halt 410
或主体
halt 'this will be the body'
或两者
halt 401, 'go away!'
带有标题
halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
当然可以将模板与halt
结合使用。
halt erb(:error)
传递
路由可以使用pass
将处理传递给下一个匹配的路由。
get '/guess/:who' do
pass unless params['who'] == 'Frank'
'You got me!'
end
get '/guess/*' do
'You missed!'
end
路由块立即退出,控制权继续传递给下一个匹配的路由。如果找不到匹配的路由,则返回 404。
触发另一个路由
有时pass
不是您想要的,相反,您希望获得调用另一个路由的结果。只需使用call
即可实现这一点。
get '/foo' do
status, headers, body = call env.merge("PATH_INFO" => '/bar')
[status, headers, body.map(&:upcase)]
end
get '/bar' do
"bar"
end
请注意,在上面的示例中,您可以通过简单地将"bar"
移动到/foo
和/bar
都使用的帮助程序中来简化测试并提高性能。
如果您希望请求发送到同一个应用程序实例而不是副本,请使用call!
而不是call
。
如果您想了解更多关于call
的信息,请查看 Rack 规范。
设置主体、状态代码和标题
可以使用路由块的返回值来设置状态代码和响应主体,这是一种可行且推荐的做法。但是,在某些情况下,您可能希望在执行流程中的任意点设置主体。您可以使用body
帮助程序方法来做到这一点。如果您这样做,您可以从那时起使用该方法来访问主体。
get '/foo' do
body "bar"
end
after do
puts body
end
也可以将一个代码块传递给body
,该代码块将由 Rack 处理程序执行(这可用于实现流式传输,参见“返回值”)。
与 body 类似,您还可以设置状态代码和标头
get '/foo' do
status 418
headers \
"Allow" => "BREW, POST, GET, PROPFIND, WHEN",
"Refresh" => "Refresh: 20; https://ietf.org/rfc/rfc2324.txt"
body "I'm a teapot!"
end
与body
类似,不带参数的headers
和status
可用于访问其当前值。
流式响应
有时您希望在生成响应主体的一部分时开始发送数据。在极端情况下,您希望不断发送数据,直到客户端关闭连接。您可以使用stream
辅助函数来避免创建自己的包装器
get '/' do
stream do |out|
out << "It's gonna be legen -\n"
sleep 0.5
out << " (wait for it) \n"
sleep 1
out << "- dary!\n"
end
end
这允许您实现流式 API、服务器发送事件,并且可以用作WebSocket的基础。如果某些内容(但并非所有内容)依赖于缓慢的资源,它也可以用于提高吞吐量。
请注意,流式行为(尤其是并发请求的数量)高度依赖于用于提供应用程序的 Web 服务器。某些服务器甚至可能根本不支持流式传输。如果服务器不支持流式传输,则在传递给stream
的代码块执行完毕后,将一次性发送整个主体。流式传输在 Shotgun 中根本不起作用。
如果将可选参数设置为keep_open
,它将不会在流对象上调用close
,允许您在执行流程中的任何时间点关闭它。
您可以查看聊天示例
客户端在尝试写入套接字时也可能关闭连接。因此,建议在尝试写入之前检查out.closed?
。
日志记录
在请求范围内,logger
辅助函数公开了一个Logger
实例
get '/' do
logger.info "loading data"
# ...
end
此记录器将自动考虑您的 Rack 处理程序的记录设置。如果禁用记录,此方法将返回一个虚拟对象,因此您无需在路由和过滤器中担心它。
请注意,默认情况下,仅为Sinatra::Application
启用记录,因此如果您从Sinatra::Base
继承,您可能希望自己启用它
class MyApp < Sinatra::Base
configure :production, :development do
enable :logging
end
end
要避免设置任何日志记录中间件,请将logging
选项设置为nil
。但是,请记住,在这种情况下,logger
将返回nil
。一个常见的用例是您想设置自己的日志记录器。Sinatra 将使用它在env['rack.logger']
中找到的任何内容。
MIME 类型
当使用send_file
或静态文件时,您可能遇到 Sinatra 不理解的 MIME 类型。使用mime_type
通过文件扩展名注册它们。
configure do
mime_type :foo, 'text/foo'
end
您也可以将其与content_type
辅助方法一起使用。
get '/' do
content_type :foo
"foo foo foo"
end
生成 URL
要生成 URL,您应该使用url
辅助方法,例如,在 Haml 中。
%a{:href => url('/foo')} foo
它会考虑反向代理和 Rack 路由器 - 如果存在。
此方法也被别名为to
(有关示例,请参见下方)。
浏览器重定向
您可以使用redirect
辅助方法触发浏览器重定向。
get '/foo' do
redirect to('/bar')
end
任何其他参数都将像传递给halt
的参数一样处理。
redirect to('/bar'), 303
redirect 'http://www.google.com/', 'wrong place, buddy'
您也可以轻松地重定向回用户来自的页面,方法是使用redirect back
。
get '/foo' do
"<a href='/bar'>do something</a>"
end
get '/bar' do
do_something
redirect back
end
要使用重定向传递参数,请将其添加到查询中。
redirect to('/bar?sum=42')
或使用会话。
enable :sessions
get '/foo' do
session[:secret] = 'foo'
redirect to('/bar')
end
get '/bar' do
session[:secret]
end
缓存控制
正确设置您的标头是实现正确 HTTP 缓存的基础。
您可以像这样轻松地设置 Cache-Control 标头。
get '/' do
cache_control :public
"cache it!"
end
专业提示:在 before 过滤器中设置缓存。
before do
cache_control :public, :must_revalidate, :max_age => 60
end
如果您使用expires
辅助方法来设置相应的标头,Cache-Control
将自动为您设置。
before do
expires 500, :public, :must_revalidate
end
要正确使用缓存,您应该考虑使用etag
或last_modified
。建议在执行任何繁重操作之前调用这些辅助方法,因为如果客户端在其缓存中已经拥有当前版本,它们将立即刷新响应。
get "/article/:id" do
@article = Article.find params['id']
last_modified @article.updated_at
etag @article.sha1
erb :article
end
也可以使用弱 ETag
etag @article.sha1, :weak
这些辅助方法不会为您执行任何缓存操作,而是将必要的信息提供给您的缓存。如果您正在寻找快速的反向代理缓存解决方案,请尝试使用rack-cache
require "rack/cache"
require "sinatra"
use Rack::Cache
get '/' do
cache_control :public, :max_age => 36000
sleep 5
"hello"
end
使用:static_cache_control
设置(参见下方)将Cache-Control
标头信息添加到静态文件。
根据 RFC 2616,您的应用程序在设置 If-Match 或 If-None-Match 标头为*
时,应该根据请求的资源是否存在而表现出不同的行为。Sinatra 假设安全(如 get)和幂等(如 put)请求的资源已经存在,而其他资源(例如 post 请求)被视为新资源。您可以通过传递:new_resource
选项来更改此行为。
get '/create' do
etag '', :new_resource => true
Article.create
erb :new_article
end
如果您仍然想使用弱 ETag,请传递:kind
选项。
etag '', :new_resource => true, :kind => :weak
发送文件
要将文件的內容作为响应返回,您可以使用send_file
辅助方法。
get '/' do
send_file 'foo.png'
end
它也接受选项。
send_file 'foo.png', :type => :jpg
选项是
- filename
- 响应中使用的文件名,默认为真实文件名。
- last_modified
- Last-Modified 标头的值,默认为文件的 mtime。
- type
- Content-Type 标头的值,如果缺少,则从文件扩展名推断。
- disposition
- Content-Disposition 标头的值,可能的值:nil(默认)、:attachment 和 :inline
- length
- Content-Length 标头的值,默认为文件大小。
- status
- 要发送的状态代码。在将静态文件作为错误页面发送时很有用。如果 Rack 处理程序支持,将使用除从 Ruby 进程流式传输之外的其他方法。如果您使用此辅助方法,Sinatra 将自动处理范围请求。
访问请求对象
可以通过request
方法从请求级别(过滤器、路由、错误处理程序)访问传入的请求对象。
# app running on http://example.com/example
get '/foo' do
t = %w[text/css text/html application/javascript]
request.accept # ['text/html', '*/*']
request.accept? 'text/xml' # true
request.preferred_type(t) # 'text/html'
request.body # request body sent by the client (see below)
request.scheme # "http"
request.script_name # "/example"
request.path_info # "/foo"
request.port # 80
request.request_method # "GET"
request.query_string # ""
request.content_length # length of request.body
request.media_type # media type of request.body
request.host # "example.com"
request.get? # true (similar methods for other verbs)
request.form_data? # false
request["some_param"] # value of some_param parameter. [] is a shortcut to the params hash.
request.referrer # the referrer of the client or '/'
request.user_agent # user agent (used by :agent condition)
request.cookies # hash of browser cookies
request.xhr? # is this an ajax request?
request.url # "http://example.com/example/foo"
request.path # "/example/foo"
request.ip # client IP address
request.secure? # false (would be true over ssl)
request.forwarded? # true (if running behind a reverse proxy)
request.env # raw env hash handed in by Rack
end
一些选项,如script_name
或path_info
,也可以写入。
before { request.path_info = "/" }
get "/" do
"all requests end up here"
end
request.body
是一个 IO 或 StringIO 对象。
post "/api" do
request.body.rewind # in case someone already read it
data = JSON.parse request.body.read
"Hello #{data['name']}!"
end
附件
您可以使用attachment
辅助方法告诉浏览器响应应该存储在磁盘上而不是在浏览器中显示。
get '/' do
attachment
"store it!"
end
您也可以传递文件名。
get '/' do
attachment "info.txt"
"store it!"
end
处理日期和时间
Sinatra 提供了一个time_for
辅助方法,它从给定值生成一个 Time 对象。它还能够转换DateTime
、Date
和类似的类。
get '/' do
pass if Time.now > time_for('Dec 23, 2016')
"still time"
end
此方法在内部由expires
、last_modified
等使用。因此,您可以通过在应用程序中覆盖time_for
来轻松扩展这些方法的行为。
helpers do
def time_for(value)
case value
when :yesterday then Time.now - 24*60*60
when :tomorrow then Time.now + 24*60*60
else super
end
end
end
get '/' do
last_modified :yesterday
expires :tomorrow
"hello"
end
查找模板文件
find_template
辅助方法用于查找用于渲染的模板文件。
find_template settings.views, 'foo', Tilt[:haml] do |file|
puts "could be #{file}"
end
这并没有什么实际用处。但你可以重写这个方法来接入你自己的查找机制,这很有用。例如,如果你想使用多个视图目录。
set :views, ['views', 'templates']
helpers do
def find_template(views, name, engine, &block)
Array(views).each { |v| super(v, name, engine, &block) }
end
end
另一个例子是为不同的引擎使用不同的目录。
set :views, :haml => 'templates', :default => 'views'
helpers do
def find_template(views, name, engine, &block)
_, folder = views.detect { |k,v| engine == Tilt[k] }
folder ||= views[:default]
super(folder, name, engine, &block)
end
end
你也可以轻松地将它封装成一个扩展并与他人分享!
请注意,find_template
不会检查文件是否存在,而是对所有可能的路径调用给定的块。这不是性能问题,因为 render
会在找到文件后立即使用 break
。此外,如果你没有在开发模式下运行,模板位置(和内容)将被缓存。如果你写了一个非常疯狂的方法,你应该记住这一点。
配置
在任何环境中启动时运行一次。
configure do
# setting one option
set :option, 'value'
# setting multiple options
set :a => 1, :b => 2
# same as `set :option, true`
enable :option
# same as `set :option, false`
disable :option
# you can also have dynamic settings with blocks
set(:css_dir) { File.join(views, 'css') }
end
仅当环境(APP_ENV
环境变量)设置为 :production
时运行。
configure :production do
...
end
当环境设置为 :production
或 :test
时运行。
configure :production, :test do
...
end
你可以通过 settings
访问这些选项。
configure do
set :foo, 'bar'
end
get '/' do
settings.foo? # => true
settings.foo # => 'bar'
...
end
配置攻击防护
Sinatra 使用 Rack::Protection 来保护你的应用程序免受常见的、机会主义的攻击。你可以轻松地禁用此行为(这将使你的应用程序容易受到大量常见漏洞的攻击)。
disable :protection
要跳过单个防御层,将 protection
设置为选项哈希。
set :protection, :except => :path_traversal
你也可以传入一个数组来禁用一组防护。
set :protection, :except => [:path_traversal, :remote_token]
默认情况下,Sinatra 只有在启用了 :sessions
时才会设置基于会话的防护。请参阅“使用会话”。有时你可能希望在 Sinatra 应用程序“外部”设置会话,例如在 config.ru 中或使用单独的 Rack::Builder
实例。在这种情况下,你仍然可以通过传递 :session
选项来设置基于会话的防护。
set :protection, :session => true
可用设置
- absolute_redirects
- 如果禁用,Sinatra 将允许相对重定向,但是,Sinatra 将不再符合 RFC 2616(HTTP 1.1),该规范只允许绝对重定向。
- 如果你的应用程序运行在未正确设置的反向代理后面,请启用此选项。请注意,url 帮助程序仍然会生成绝对 URL,除非你将 false 作为第二个参数传入。
- 默认情况下禁用。
- add_charset
- content_type 帮助程序将自动添加字符集信息的 MIME 类型。你应该添加而不是覆盖此选项:settings.add_charset << "application/foobar"
- app_file
- 主应用程序文件路径,用于检测项目根目录、视图和公共文件夹以及内联模板。
- bind
- 要绑定的 IP 地址(默认:0.0.0.0 或 localhost 如果您的 `environment` 设置为开发)。仅用于内置服务器。
- default_content_type
- 如果未知,则假设的 Content-Type(默认为 "text/html")。设置为 nil 不会在每个响应中设置默认 Content-Type;当这样配置时,您必须在发出内容时手动设置 Content-Type,否则用户代理将不得不嗅探它(或者,如果在 Rack::Protection::XSSHeader 中启用了 nosniff,则假设 application/octet-stream)。
- default_encoding
- 如果未知,则假设的编码(默认为 "utf-8")。
- dump_errors
- 在日志中显示错误。默认情况下启用,除非环境为“测试”。
- environment
- 当前环境。默认为 ENV['APP_ENV'],如果不可用,则默认为 "development"。
- logging
- 使用记录器。
- lock
- 在每个请求周围放置一个锁,仅在每个 Ruby 进程并发地处理一个请求。
- 如果您的应用程序不是线程安全的,则启用。默认情况下禁用。
- method_override
- 使用 _method 魔法允许在不支持它的浏览器中使用 put/delete 表单。
- mustermann_opts
- 传递给 Mustermann.new 的默认选项哈希,用于编译路由路径。
- port
- 要监听的端口。仅用于内置服务器。
- prefixed_redirects
- 是否在没有给出绝对路径的情况下将 request.script_name 插入重定向。这样 redirect '/foo' 就会像 redirect to('/foo') 一样工作。默认情况下禁用。
- protection
- 是否启用 Web 攻击防护。请参阅上面的防护部分。
- public_dir
- public_folder 的别名。见下文。
- public_folder
- 提供公共文件的文件夹路径。仅在启用静态文件服务时使用(请参阅下面的 static 设置)。如果未设置,则从 app_file 设置推断。
- quiet
- 禁用 Sinatra 的启动和停止命令生成的日志。默认情况下为 false。
- reload_templates
- 是否在请求之间重新加载模板。在开发模式下启用。
- root
- 项目根文件夹的路径。如果未设置,则从 app_file 设置推断。
- raise_errors
- 引发未处理的错误(将停止应用程序)。默认情况下,当 environment 设置为 "test" 时启用,否则禁用。
- 任何明确定义的错误处理程序始终会覆盖此设置。请参阅下面的“错误”部分。
- run
- 如果启用,Sinatra 将处理启动 Web 服务器。如果使用 rackup 或其他方法,请勿启用。
- running
- 内置服务器现在是否正在运行?请勿更改此设置!
- server
- 用于内置服务器的服务器或服务器列表。顺序表示优先级,默认值取决于 Ruby 实现。
- server_settings
- 如果您使用的是 WEBrick Web 服务器(可能用于您的开发环境),则可以将选项的哈希传递给 server_settings,例如 SSLEnable 或 SSLVerifyClient。但是,Puma 等 Web 服务器不支持此功能,因此您可以通过在调用 configure 时将其定义为方法来设置 server_settings。
- sessions
- 使用 Rack::Session::Cookie 启用基于 Cookie 的会话支持。有关更多信息,请参阅“使用会话”部分。
- session_store
- 使用的 Rack 会话中间件。默认为 Rack::Session::Cookie。有关更多信息,请参阅“使用会话”部分。
- show_exceptions
- 当发生异常时,在浏览器中显示堆栈跟踪。当 environment 设置为 "development" 时默认启用,否则禁用。
- 也可以设置为 :after_handler,以便在浏览器中显示堆栈跟踪之前触发应用程序指定的错误处理。
- static
- Sinatra 是否应该处理静态文件的提供。
- 当使用能够独立执行此操作的服务器时禁用。
- 禁用将提高性能。
- 在经典风格中默认启用,在模块化应用程序中禁用。
- static_cache_control
- 当 Sinatra 提供静态文件时,将其设置为将 Cache-Control 标头添加到响应中。使用 cache_control 助手。默认情况下禁用。
- 在设置多个值时使用显式数组:set :static_cache_control, [:public, :max_age => 300]
- threaded
- 如果设置为 true,将告诉服务器使用 EventMachine.defer 处理请求。
- traps
- Sinatra 是否应该处理系统信号。
- 视图
- 视图文件夹的路径。如果没有设置,则从 app_file 设置推断。
- x_cascade
- 如果没有任何路由匹配,是否设置 X-Cascade 标头。默认为 true。
生命周期事件
Sinatra 目前公开了 2 个生命周期事件。一个是在服务器启动时,另一个是在服务器停止时。
它们可以像这样使用
on_start do
puts "===== Booting up ====="
end
on_stop do
puts "===== Shutting down ====="
end
请注意,这些回调仅在使用 Sinatra 启动 Web 服务器时才有效。
环境
有三个预定义的 environments
:"development"
、"production"
和 "test"
。环境可以通过 APP_ENV
环境变量设置。默认值为 "development"
。在 "development"
环境中,所有模板都在请求之间重新加载,并且特殊的 not_found
和 error
处理程序在您的浏览器中显示堆栈跟踪。在 "production"
和 "test"
环境中,模板默认情况下被缓存。
要运行不同的环境,请设置 APP_ENV
环境变量
APP_ENV=production ruby my_app.rb
您可以使用预定义的方法:development?
、test?
和 production?
来检查当前环境设置
get '/' do
if settings.development?
"development!"
else
"not development!"
end
end
错误处理
错误处理程序在与路由和前置过滤器相同的上下文中运行,这意味着您可以使用它提供的所有功能,例如haml
、erb
、halt
等。
未找到
当引发Sinatra::NotFound
异常或响应的 状态码为 404 时,将调用not_found
处理程序。
not_found do
'This is nowhere to be found.'
end
错误
error
处理程序在从路由块或过滤器引发异常时被调用。但请注意,在开发环境中,只有在将 show exceptions 选项设置为:after_handler
时才会运行。
set :show_exceptions, :after_handler
可以使用error
和一个块来定义一个通用的错误处理程序。
error do
'Sorry there was a nasty error'
end
可以通过sinatra.error
Rack 变量获取异常对象。
error do
'Sorry there was a nasty error - ' + env['sinatra.error'].message
end
将错误类作为参数传递以创建自定义错误的处理程序。
error MyCustomError do
'So what happened was...' + env['sinatra.error'].message
end
然后,如果发生这种情况
get '/' do
raise MyCustomError, 'something bad'
end
您将得到这个
So what happened was... something bad
或者,您可以为状态码安装错误处理程序。
error 403 do
'Access forbidden'
end
get '/secret' do
403
end
或一个范围。
error 400..510 do
'Boom'
end
Sinatra 在开发环境下运行时会安装特殊的not_found
和error
处理程序,以便在浏览器中显示漂亮的堆栈跟踪和额外的调试信息。
raise_errors
选项的行为
当raise_errors
选项为true
时,未处理的错误将在应用程序外部引发。此外,任何原本会被通用错误处理程序捕获的错误都会被引发。
例如,考虑以下配置
# First handler
error MyCustomError do
'A custom message'
end
# Second handler
error do
'A catch-all message'
end
如果raise_errors
为false
- 当引发
MyCustomError
或其子类时,将调用第一个处理程序。HTTP 响应主体将包含"A custom message"
。 - 当引发任何其他错误时,将调用第二个处理程序。HTTP 响应主体将包含
"A catch-all message"
。
如果raise_errors
为true
- 当引发
MyCustomError
或其子类时,行为与raise_errors
为false
时相同,如上所述。 - 当引发任何其他错误时,不会调用第二个处理程序,并且错误将在应用程序外部引发。
- 如果环境为
production
,HTTP 响应主体将包含一个通用的错误消息,例如"An unhandled lowlevel error occurred. The application logs may have details."
。 - 如果环境不是
production
,HTTP 响应主体将包含详细的错误回溯。 - 无论环境如何,如果
show_exceptions
设置为:after_handler
,HTTP 响应主体将包含详细的错误回溯。
- 如果环境为
在test
环境中,raise_errors
默认设置为true
。这意味着为了编写通用错误处理程序的测试,必须为该特定测试暂时将raise_errors
设置为false
。
Rack 中间件
Sinatra 运行在 Rack 之上,Rack 是一个用于 Ruby Web 框架的最小标准接口。Rack 最有趣的特性之一是为应用程序开发者提供了对“中间件”的支持,中间件是位于服务器和应用程序之间的组件,用于监控和/或操作 HTTP 请求/响应,以提供各种类型的通用功能。
Sinatra 通过顶层的 use
方法使构建 Rack 中间件管道变得轻而易举。
require 'sinatra'
require 'my_custom_middleware'
use Rack::Lint
use MyCustomMiddleware
get '/hello' do
'Hello World'
end
use
的语义与 Rack::Builder DSL 中定义的语义相同(最常用于 rackup 文件)。例如,use
方法接受多个/可变参数以及代码块。
use Rack::Auth::Basic do |username, password|
username == 'admin' && password == 'secret'
end
Rack 附带了各种标准中间件,用于日志记录、调试、URL 路由、身份验证和会话处理。Sinatra 根据配置自动使用其中许多组件,因此您通常不必显式地 use
它们。
您可以在 rack、rack-contrib 或 Rack wiki 中找到有用的中间件。
测试
Sinatra 测试可以使用任何基于 Rack 的测试库或框架编写。推荐使用 Rack::Test
require 'my_sinatra_app'
require 'minitest/autorun'
require 'rack/test'
class MyAppTest < Minitest::Test
include Rack::Test::Methods
def app
Sinatra::Application
end
def test_my_default
get '/'
assert_equal 'Hello World!', last_response.body
end
def test_with_params
get '/meet', :name => 'Frank'
assert_equal 'Hello Frank!', last_response.body
end
def test_with_user_agent
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
assert_equal "You're using Songbird!", last_response.body
end
end
注意:如果您在模块化风格中使用 Sinatra,请将上面的 Sinatra::Application
替换为您的应用程序的类名。
Sinatra::Base - 中间件、库和模块化应用程序
在顶层定义您的应用程序对于微型应用程序来说很有效,但在构建可重用组件(如 Rack 中间件、Rails metal、带有服务器组件的简单库,甚至 Sinatra 扩展)时存在相当大的缺点。顶层假设了一种微型应用程序风格的配置(例如,单个应用程序文件、./public
和 ./views
目录、日志记录、异常详细信息页面等)。这就是 Sinatra::Base
发挥作用的地方。
require 'sinatra/base'
class MyApp < Sinatra::Base
set :sessions, true
set :foo, 'bar'
get '/' do
'Hello world!'
end
end
Sinatra::Base
子类可用的方法与通过顶层 DSL 可用的方法完全相同。大多数顶层应用程序可以通过两个修改转换为 Sinatra::Base
组件。
- 您的文件应该需要
sinatra/base
而不是sinatra
;否则,所有 Sinatra 的 DSL 方法将被导入到主命名空间。 - 将您的应用程序的路由、错误处理程序、过滤器和选项放在
Sinatra::Base
的子类中。
Sinatra::Base
是一个空白画布。默认情况下,大多数选项都被禁用,包括内置服务器。有关可用选项及其行为的详细信息,请参阅 配置设置。如果您想要的行为更类似于在顶层定义应用程序时(也称为经典风格),您可以子类化 Sinatra::Application
require 'sinatra/base'
class MyApp < Sinatra::Application
get '/' do
'Hello world!'
end
end
模块化与经典风格
与普遍认知相反,经典风格并没有什么问题。如果它适合您的应用程序,您无需切换到模块化应用程序。
使用经典风格而不是模块化风格的主要缺点是,您每个 Ruby 进程只有一个 Sinatra 应用程序。如果您计划使用多个应用程序,请切换到模块化风格。您无需将模块化风格和经典风格混合使用。
如果您从一种风格切换到另一种风格,您应该注意一些略微不同的默认设置。
设置 | 经典 | 模块化 | 模块化 |
---|---|---|---|
app_file | 文件加载 sinatra | 文件子类化 Sinatra::Base | 文件子类化 Sinatra::Application |
run | $0 == app_file | false | false |
logging | true | false | true |
method_override | true | false | true |
inline_templates | true | false | true |
static | true | File.exist?(public_folder) | true |
运行模块化应用程序
启动模块化应用程序有两种常见方法,一种是使用 run!
积极启动。
# my_app.rb
require 'sinatra/base'
class MyApp < Sinatra::Base
# ... app code here ...
# start the server if ruby file executed directly
run! if app_file == $0
end
使用
ruby my_app.rb
或者使用 config.ru
文件,它允许使用任何 Rack 处理程序。
# config.ru (run with rackup)
require './my_app'
run MyApp
运行
rackup -p 4567
使用经典风格应用程序和 config.ru
编写您的应用程序文件
# app.rb
require 'sinatra'
get '/' do
'Hello world!'
end
以及相应的 config.ru
require './app'
run Sinatra::Application
何时使用 config.ru?
如果以下情况,建议使用 config.ru
文件
- 您想使用不同的 Rack 处理程序(Passenger、Unicorn、Heroku 等)进行部署。
- 您想使用多个
Sinatra::Base
子类。 - 您想将 Sinatra 仅用作中间件,而不是用作端点。
您无需仅仅因为切换到模块化风格就切换到 config.ru
,并且您无需使用模块化风格来运行 config.ru
。
使用 Sinatra 作为中间件
Sinatra 不仅能够使用其他 Rack 中间件,而且任何 Sinatra 应用程序都可以反过来作为中间件添加到任何 Rack 端点之前。这个端点可以是另一个 Sinatra 应用程序,也可以是任何其他基于 Rack 的应用程序(Rails/Hanami/Roda/…)。
require 'sinatra/base'
class LoginScreen < Sinatra::Base
enable :sessions
get('/login') { haml :login }
post('/login') do
if params['name'] == 'admin' && params['password'] == 'admin'
session['user_name'] = params['name']
else
redirect '/login'
end
end
end
class MyApp < Sinatra::Base
# middleware will run before filters
use LoginScreen
before do
unless session['user_name']
halt "Access denied, please <a href='/login'>login</a>."
end
end
get('/') { "Hello #{session['user_name']}." }
end
动态应用程序创建
有时您想在运行时创建新的应用程序,而无需将它们分配给常量。您可以使用 Sinatra.new
来实现。
require 'sinatra/base'
my_app = Sinatra.new { get('/') { "hi" } }
my_app.run!
它将应用程序作为可选参数继承。
# config.ru (run with rackup)
require 'sinatra/base'
controller = Sinatra.new do
enable :logging
helpers MyHelpers
end
map('/a') do
run Sinatra.new(controller) { get('/') { 'a' } }
end
map('/b') do
run Sinatra.new(controller) { get('/') { 'b' } }
end
这对于测试 Sinatra 扩展或在自己的库中使用 Sinatra 特别有用。
这也使得将 Sinatra 用作中间件变得非常容易。
require 'sinatra/base'
use Sinatra do
get('/') { ... }
end
run RailsProject::Application
作用域和绑定
您当前所在的范围决定了哪些方法和变量可用。
应用程序/类范围
每个 Sinatra 应用程序对应于 Sinatra::Base
的一个子类。如果您使用的是顶级 DSL(require 'sinatra'
),那么这个类是 Sinatra::Application
,否则它就是您显式创建的子类。在类级别,您拥有 get
或 before
之类的方法,但您无法访问 request
或 session
对象,因为所有请求只有一个应用程序类。
通过 set
创建的选项是类级别的函数。
class MyApp < Sinatra::Base
# Hey, I'm in the application scope!
set :foo, 42
foo # => 42
get '/foo' do
# Hey, I'm no longer in the application scope!
end
end
您在应用程序类主体内部拥有应用程序范围绑定
- 您的应用程序类主体
- 扩展定义的方法
- 传递给
helpers
的代码块 - 用作
set
值的 Proc/代码块 - 传递给
Sinatra.new
的代码块
您可以像这样访问范围对象(类)
- 通过传递给配置块的对象(
configure { |c| ... }
) -
来自请求范围内的
settings
请求/实例范围
对于每个传入的请求,都会创建一个应用程序类的新的实例,并且所有处理程序块都在该范围内运行。从该范围内,您可以访问request
和session
对象,或调用渲染方法,例如erb
或haml
。您可以通过settings
帮助程序从请求范围内访问应用程序范围
class MyApp < Sinatra::Base
# Hey, I'm in the application scope!
get '/define_route/:name' do
# Request scope for '/define_route/:name'
@value = 42
settings.get("/#{params['name']}") do
# Request scope for "/#{params['name']}"
@value # => nil (not the same request)
end
"Route defined!"
end
end
您在内部拥有请求范围绑定
- get、head、post、put、delete、options、patch、link和unlink块
- before和after过滤器
- 帮助程序方法
- 模板/视图
委托范围
委托范围只是将方法转发到类范围。但是,它的行为与类范围不完全相同,因为您没有类绑定。只有明确标记为委托的方法可用,并且您不与类范围共享变量/状态(阅读:您有不同的self
)。您可以通过调用Sinatra::Delegator.delegate :method_name
显式添加方法委托。
您在内部拥有委托范围绑定
- 如果您执行了
require "sinatra"
,则为顶层绑定 - 使用
Sinatra::Delegator
mixin扩展的对象
自己看看代码:这是Sinatra::Delegator mixin正在扩展主对象。
命令行
Sinatra应用程序可以直接运行
ruby myapp.rb [-h] [-x] [-q] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
选项是
-h # help
-p # set the port (default is 4567)
-o # set the host (default is 0.0.0.0)
-e # set the environment (default is development)
-s # specify rack server/handler (default is puma)
-q # turn on quiet mode for server (default is off)
-x # turn on the mutex lock (default is off)
多线程
改写自Konstantin的这个StackOverflow答案
Sinatra不强加任何并发模型,而是将其留给底层的Rack处理程序(服务器),例如Puma或WEBrick。Sinatra本身是线程安全的,因此如果Rack处理程序使用线程并发模型,就不会有任何问题。
要求
正式支持以下Ruby版本
- Ruby
- 稳定版本完全受支持,建议使用。
- TruffleRuby
- 支持TruffleRuby的最新稳定版本。
- JRuby
- 支持JRuby的最新稳定版本。不建议在JRuby中使用C扩展。
从Sinatra 4.0.0开始,不再支持Ruby 2.7.8之前的版本。
Sinatra 应该可以在任何受选定 Ruby 实现支持的操作系统上运行。
在未正式支持的 Ruby 版本上运行 Sinatra 意味着,如果问题只在那里出现,我们假设这不是我们的问题,而是他们的问题。
最前沿
如果您想使用 Sinatra 的最新最前沿代码,请随时针对主分支运行您的应用程序,它应该相当稳定。
我们还会不时推出预发布 gem,因此您可以执行以下操作:
gem install sinatra --pre
以获取一些最新功能。
使用 Bundler
如果您想使用最新的 Sinatra 运行您的应用程序,建议使用 Bundler。
首先,安装 bundler,如果您还没有安装
gem install bundler
然后,在您的项目目录中,创建一个 Gemfile
source 'https://rubygems.org.cn'
gem 'sinatra', :github => 'sinatra/sinatra'
# other dependencies
gem 'haml' # for instance, if you use haml
请注意,您需要在 Gemfile
中列出所有应用程序的依赖项。但是,Sinatra 的直接依赖项(Rack 和 Tilt)将由 Bundler 自动获取和添加。
现在您可以像这样运行您的应用程序
bundle exec ruby myapp.rb
版本控制
Sinatra 遵循 语义版本控制,包括 SemVer 和 SemVerTag。
进一步阅读
- 项目网站 - 额外的文档、新闻以及指向其他资源的链接。
- 贡献 - 发现错误?需要帮助?有补丁吗?
- 问题跟踪器
- 推特
- 邮件列表
- IRC: #sinatra 在 Freenode 上
- Sinatra & Friends 在 Discord 上
- Sinatra 书籍 - 食谱教程
- Sinatra 食谱 - 社区贡献的食谱
- 针对 最新版本 或 当前 HEAD 的 API 文档,请访问 RubyDoc
- CI 操作