最近在看Sinatra 1.3.5的源码。在Sinatra::Helpers模块中看到一个名为uri
的方法,用来将一个相对路径转换为绝对路径。在该函数的代码中看到分别针对request.script_name和request.path_info进行了处理。但是并不清楚这两个参数的区别,于是做了一些reseach。
二者在RFC3875中有如下定义:
4.1.13. SCRIPT_NAME
The SCRIPT_NAME variable MUST be set to a URI path (not URL-encoded) which could identify the CGI script (rather than the script's output). The syntax is the same as for PATH_INFO (section 4.1.5)
SCRIPT_NAME = "" | ( "/" path )
The leading "/" is not part of the path. It is optional if the path is NULL; however, the variable MUST still be set in that case.
The SCRIPT_NAME string forms some leading part of the path component of the Script-URI derived in some implementation-defined manner. No PATH_IN
4.1.5. PATH_INFO
The PATH_INFO variable specifies a path to be interpreted by the CGI script. It identifies the resource or sub-resource to be returned by the CGI script, and is derived from the portion of the URI path hierarchy following the part that identifies the script itself. Unlike a URI path, the PATH_INFO is not URL-encoded, and cannot contain path-segment parameters. A PATH_INFO of "/" represents a single void path segment.
根据定义可以简单的理解为script_name指定了CGI script,而path_info则会作为参数传递给script_name。举个例子,考虑如下的URI
此时script_name应该是/dir/script.cgi而path_info则是/a/b/c。但是在Sinatra中我们的路由设计并不会像CGI一样,URL会对应到文件系统中的某个script。那这时候script_name和path_info又分别保存了什么信息呢?
首先写个简单的脚本看看: test.rb代码如下:
require 'sinatra/base'
class Test < Sinatra::Base
get '/*' do
puts "request.script_name: #{request.script_name}"
puts "request.path_info : #{request.path_info}"
end
end
config.ru代码如下
require File.expand_path("../test", __FILE__)
run Test
执行rackup config.ru
启动测试application,在浏览器中输入http://127.0.0.1:9292/a/b/c 看到console中有输出:
=> rackup config.ru
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:9292, CTRL+C to stop
request.script_name:
request.path_info : /a/b/c
可以看到script_name是一个空字符串,而path_info中包含了URL中除主机名外的剩余部分。
Sinatra中真的没有类似CGI那样URL对应系统中某个scirpt的映射方式么?
有,但是以一种比较有趣的方式实现的映射——将某个路径映射到一个合法的Sinatra application。假设我们将/test映射到一个名为App1的application,则当我们访问/test/a/b的时候实际上访问的是App1中匹配的/a/b路径的路由项。那么这时,script_name保存的应该就是/test了。到底是不是这样,一试便知。首先将config.ru修改为如下内容:
require File.expand_path("../test", __FILE__)
map('/example') { run Test }
再次rackup config.ru
启动server,此时访问http://127.0.0.1:9292/example/a/b/c 可以看到console中有如下输出:
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:9292, CTRL+C to stop
request.script_name: /example
request.path_info : /a/b/c
不过在通常情况下很少遇到需要使用map在路径和application间映射的情况,因此通常request.script_name中只包含一个空字符串。