ruby轻量级web框架sinatra

前言

sinatra是一个基于ruby语言,能够快速构建web应用的轻量级框架。它既可以构建一个完整的web应用,也可以当作一个中间件 来使用,在cloud foundry中大量地使用sinatra来处理组件之间的HTTP交互,就是将sinatra作为一个中间件来实现的。

DSL

  • 路由

sinatra的路由机制看起来非常的简单直观,一般说路由都会从最简单的sinatra应用说起:

    require 'sinatra'

    get '/' do
      'Hello world!'
   end

get表示是http的请求形式,它是基于REST的消息机制,一般http的请求形式包括get(获取),put(更新), post(创建),delete(删除)等常用的四种形式。‘/’表示是路由选项,它通过匹配url来选择执行,block 里面的“hello world!”是返回的字符串。

参数:sinatra支持正则表达式匹配路由,如:

   get '/hello/:name' do
      # matches "GET /hello/foo" and "GET /hello/bar"
      # params[:name] is 'foo' or 'bar'
      "Hello #{params[:name]}!"
   end

   get '/say/*/to/*' do
       # 匹配 /say/hello/to/world
       params[:splat] # => ["hello", "world"]
   end

   get %r{/hello/([\w]+)} do
      "Hello, #{params[:captures].first}!"
   end

当然上述参数也可以通过给block传入参数来实现。

条件:所谓condition就是sinatra相应请求的条件,你可以为其指定agent(浏览器),host_name(访问 主机)等路由匹配条件。

返回值:一个合理的Rack响应都是被支持的,但是一般都是使用String来做返回值。来看一个Array返 回的例子:

    class Stream
      def each
        100.times { |i| yield "#{i}\n" }
      end
   end

   get('/') { Stream.new }
  • 模板

如果是静态的网页放置在./public目录中,如果想要修改可以通过set指令来修改:public_folder,而模板 则功能强大得多,它能够支持在模板中插入变量的形式来定制模板,template位于./views中。当然也可以 通过set来修改:views从而制定模板文件夹,来看一则erb的模板文件:

    require 'erb'

    get '/' do
      erb :index
   end

它能够返回./views/index.erb文件。

下面我们来看如何在模板中嵌入变量:

    get '/' do
      @index = 0
      erb :template,:locals => {:index => @index}
   end

上面会把template上面的index变量替换成具体的值。

  • 过滤器

顾名思义,过滤器就是在路由请求之前或者之后对请求进行修改,一般有两种形式:before和after

    before do
      @note = 'Hi!'
      request.path_info = '/foo/bar/baz'
   end

   after do
      body “#{response.status}”
   end

在before中设置的变量是在request的整个生命周期都是可用的。

  • 辅助方法

可以使用顶层的helpers来定义方法,这些方法在路由处理的过程中可用

    helpers do
      def bar(name)
        "#{name}bar"
      end
   end

   get '/:name' do
      bar(params[:name])
   end

上例中就可以在get中使用helpers定义的方法。

  • 重定位

当request匹配到一个路由时,有时需要将其重定位到另外一个路由处理器中,有多种方法可以实现:

   get '/guess/:who' do
      pass unless params[:who] == 'Frank'
      'You got me!'
   end

   get '/foo' do
      status, headers, body = call env.merge("PATH_INFO" => '/bar')
      [status, headers, body.map(&:upcase)]
   end

   get '/foo' do
      redirect to('/bar')
   end

当然你也可以在before中就指定路径

  • request对象

    request.body              # 被客户端设定的请求体(见下)
    request.scheme            # "http"
    request.script_name       # "/example"
    request.path_info         # "/foo"
    request.port              # 80
    request.request_method    # "GET"
    request.query_string      # ""
    request.content_length    # request.body的长度
    request.media_type        # request.body的媒体类型
    request.host              # "example.com"
    request.get?              # true (其他动词也具有类似方法)
    request.form_data?        # false
    request["SOME_HEADER"]    # SOME_HEADER header的值
    request.referrer          # 客户端的referrer 或者 '/'
    request.user_agent        # user agent (被 :agent 条件使用)
    request.cookies           # 浏览器 cookies 哈希
    request.xhr?              # 这是否是ajax请求?
    request.url               # "http://example.com/example/foo"
    request.path              # "/example/foo"
    request.ip                # 客户端IP地址
    request.secure?           # false(如果是ssl则为true)
    request.forwarded?        # true (如果是运行在反向代理之后)
    request.env               # Rack中使用的未处理的env哈希
    

中间件

我们有时候并不需要全部的sinatra框架来帮我们完成一些无需界面的交互过程,这个时候使用Sinatra: Base就是最佳的选择。我们先来看一个实例:

    require 'sinatra/base'

    class LoginScreen < Sinatra::Base
      enable :sessions

     get('/login') { haml :login }

     post('/login') do
       if params[:name] = 'admin' and params[:password] = 'admin'
         session['user_name'] = params[:name]
       else
         redirect '/login'
       end
     end  
   end

   class MyApp < Sinatra::Base
      # 在前置过滤器前运行中间件
      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

因为每个请求进入都会有相应的应用实例被创建,而set是类层面的方法,所以在使用变量的时候得注意 请求变量作用域,一般在本请求中可见。

在使用sinatra作为中间件的时候得注意区分应用变量域与请求变量域,应用变量域是类层面的,请求变 量域是实例层面的,这与ruby中类和多个实例的作用域类似,需要理解它才能在使用的时候不犯错误。



Previous     Next
zhing /
Published under (CC) BY-NC-SA in categories ruby  tagged with ruby