Rack相关知识梳理(三)

Rack为编写Web应用以及Web框架提供了很多便利的工具,那么这一节,我们实现一个最简单的Web框架。
 
一、Web框架应该具备什么功能
  • 对request和response的存取
  • 路由:根据不同URL执行不同程序
  • 能够处理cookies
  • 能够存取session
  • 能够生成日志
  • ......
 
看上去挺麻烦的,是吧?其实,则不然
前面我们已经知道Rack::Request和Rack::Response这两个类可以用来处理request和 response,那么我们该如何实现路由呢?Rack提供Rack::URLMap类来处理路由,我们这里可以不直接使用这个类,如果大家仔细看Rack::Builder的实现,大家会发现这个类提供了一个map方法以及generate_map方法:
map
def map(path, &block) 
  @map ||= {} 
  @map[path] = block
end
generate_map
def generate_map(default_app, mapping)
  mapped = default_app ? {'/' => default_app} : {}
  mapping.each { |r,b| 
    mapped[r] = self.class.new(default_app, &b).to_app 
  } 
  URLMap.new(mapped)
end
路由的作用其实就是根据URL请求的路径去调用对应的程序,因此map方法它接受路径path和 对应的程序&block,然后用hash保存其对应关系generate_map这个方法是在to_app中调用的,它主要通过URLMap建立起路由和所执行应用程序的关系。
def to_app
  app = @map ? generate_map(@run, @map) : @run 
  fail "missing run or map statement" unless app
  app = @use.reverse.inject(app) { |a,e| e[a] } 
  @warmup.call(app) if @warmup
  app
end
前面我们知道to_app方法是为了组合中间件以及应用程序的,通过这段代码我们可以看出来, 在组合中间件之前需要先根据路由找到真正的run,然后再组装对应的中间件。
 
二、一个简单的Web框架
下面是一个简单的web框架,具备对request、response的处理并且具有路由、日志功能。
require 'rack'
app = Rack::Builder.new { 
  use Rack::ContentLength 
  map '/hello' do
    use Rack::CommonLogger 
    map '/user1' do
      run lambda {|env| 
        [200, {"Content-type" => "text/html"}, ["from user1", "SCRIPT_NAME=#{env["SCRIPT_NAME"]}", "PATH_INFO=#{env["PATH_INFO"]}"]]
      }
    end

    map '/everyone' do
      run lambda {|env| [200, {"Content-type" => "text/html"},
["from everyone", "SCRIPT_NAME=#{env["SCRIPT_NAME"]}", "PATH_INFO=#{env["PATH_INFO"]}"]]
      }
    end

    map '/' do
      run lambda {|env| [200, {"Content-type" => "text/html"},
["from hello catch all", "SCRIPT_NAME=#{env["SCRIPT_NAME"]}", "PATH_INFO=#{env["PATH_INFO"]}"]]
      }
    end 
  end

  map '/' do
    run lambda {|env| [200, {"Content-type" => "text/html"}, ["root"]]
    }
  end
}.to_app
Rack::Handler::WEBrick.run app, :Port => 3000
通过上面代码可以看出来:
  • map是可以嵌套的
  • 可以对不同的路由使用不同的中间件组合

 

三、rackup

前面我们的代码中最后一行总是这样写的:

Rack::Handler::WEBrick.run xxx, :Port => 3000

这样做写死了所要用的服务器,不灵活。

Rack提供了rackup命令,允许我们用一个配置文件去执行我们的应用程序,rackup使用很简单,我们只需要提供一个后缀为.ru的配置文件即可,然后运行 rackup xxx.ru就ok了,我们把 前面代码改为:

map '/hello' do 
  map '/user1' do
    run lambda {|env| [200, {"Content-type" => "text/html"}, ["from user1",
"SCRIPT_NAME=#{env["SCRIPT_NAME"]}", "PATH_INFO=#{env["PATH_INFO"]}"]]
    }
  end

  map '/everyone' do
    run lambda {|env| [200, {"Content-type" => "text/html"},
["from everyone", "SCRIPT_NAME=#{env["SCRIPT_NAME"]}", "PATH_INFO=#{env["PATH_INFO"]}"]]
    }
  end

  map '/' do
    run lambda {|env| [200, {"Content-type" => "text/html"},
["from hello catch all", "SCRIPT_NAME=#{env["SCRIPT_NAME"]}", "PATH_INFO=#{env["PATH_INFO"]}"]]
    }
  end 
end

map '/' do
  run lambda {|env| [200, {"Content-type" => "text/html"}, ["root"]]
  }
end
保存上述代码到config.ru文件然后运行 rackup config.ru即可。
posted @ 2022-05-12 00:24  阿拉懒神灯  阅读(267)  评论(0编辑  收藏  举报