CGI 类

概述

通用网关接口 (CGI) 是一种简单的协议,用于将 HTTP 请求从 Web 服务器传递到独立程序,并将输出返回到 Web 浏览器。基本上,CGI 程序在环境 (GET) 中或通过 $stdin (POST) 传递的请求参数调用,并且它打印到 $stdout 的所有内容都将返回给客户端。

此文件包含 CGI 类。此类提供检索 HTTP 请求参数、管理 cookie 和生成 HTML 输出的功能。

文件 CGI::Session 提供会话管理功能;有关更多详细信息,请参阅该类。

有关 CGI 协议的更多信息,请参阅 www.w3.org/CGI/。

简介

CGI 是一个大型类,提供多种方法,其中许多是从其他模块混合而来的。一些文档在此类中,一些文档在模块 CGI::QueryExtensionCGI::HtmlExtension 中。有关处理 cookie 的特定信息,请参阅 CGI::Cookie,有关会话的信息,请参阅 cgi/session.rb (CGI::Session)。

对于查询,CGI 提供方法来访问环境变量、参数、cookie 和多部分请求数据。对于响应,CGI 提供用于写入输出和生成 HTML 的方法。

阅读更多详细信息。底部提供了示例。

查询

CGI 类动态地混合了参数和 cookie 解析功能、环境变量访问以及对从 CGI::QueryExtension 模块解析多部分请求(包括上传的文件)的支持。

环境变量

标准的 CGI 环境变量可用作 CGI 对象的只读属性。以下是这些变量的列表

AUTH_TYPE               HTTP_HOST          REMOTE_IDENT
CONTENT_LENGTH          HTTP_NEGOTIATE     REMOTE_USER
CONTENT_TYPE            HTTP_PRAGMA        REQUEST_METHOD
GATEWAY_INTERFACE       HTTP_REFERER       SCRIPT_NAME
HTTP_ACCEPT             HTTP_USER_AGENT    SERVER_NAME
HTTP_ACCEPT_CHARSET     PATH_INFO          SERVER_PORT
HTTP_ACCEPT_ENCODING    PATH_TRANSLATED    SERVER_PROTOCOL
HTTP_ACCEPT_LANGUAGE    QUERY_STRING       SERVER_SOFTWARE
HTTP_CACHE_CONTROL      REMOTE_ADDR
HTTP_FROM               REMOTE_HOST

对于这些变量中的每一个,都有一个对应的属性,其名称相同,只是全部小写且没有前导 HTTP_。content_lengthserver_port 是整数;其余的是字符串。

参数

方法 params() 返回请求中所有参数的哈希,作为名称/值列表对,其中值列表是一个或多个值的数组。 CGI 对象本身也表现为参数名称到值的哈希,但每个参数名称仅返回一个值(作为字符串)。

例如,假设请求包含多个值为“蓝色”和“绿色”的参数“favourite_colours”。将发生以下行为

cgi.params["favourite_colours"]  # => ["blue", "green"]
cgi["favourite_colours"]         # => "blue"

如果参数不存在,则前一种方法将返回一个空数组,后一种方法返回一个空字符串。测试参数是否存在的 最简单方法是使用 has_key? 方法。

Cookies

HTTP Cookie 会自动从请求中解析。它们可以从 cookies() 访问器获得,该访问器返回从 cookie 名称到 CGI::Cookie 对象的哈希。

多部分请求

如果请求的方法是 POST 并且其内容类型是 multipart/form-data,那么它可能包含上传的文件。这些文件由 QueryExtension 模块存储在请求的参数中。参数名称是文件输入字段的 name 属性,与通常情况相同。但是,该值不是字符串,而是一个 IO 对象,对于小文件是 IOString,对于大文件是 Tempfile。此对象还具有其他单例方法

local_path()

上传的文件在本地文件系统上的路径

original_filename()

客户端计算机上的文件名

content_type()

文件的内容类型

响应

CGI 类提供了将标头和内容输出发送到 HTTP 客户端的方法,并从 CGI::HtmlExtension 和 CGI::TagMaker 模块混合了用于以编程方式生成 HTML 的方法。用于 HTML 生成的确切 HTML 版本在对象创建时指定。

写入输出

将输出发送到 HTTP 客户端的最简单方法是使用 out() 方法。这会将 HTTP 标头作为哈希参数,并通过块获取正文内容。可以使用 http_header() 方法将标头生成为字符串。可以使用 print() 方法直接写入输出流。

生成 HTML

每个 HTML 元素都有一个对应的方法,用于将该元素生成为字符串。此方法的名称与元素的名称相同,全部小写。元素的属性作为哈希传入,正文作为不带参数的块,该块的计算结果为字符串。HTML 生成模块知道哪些元素始终为空,并静默删除任何传入的正文。它还知道哪些元素需要匹配的结束标记,哪些不需要。但是,它不知道哪些属性对于哪些元素是合法的。

还有一些其他 HTML 生成方法是从 CGI::HtmlExtension 模块混合而来的。这些方法包括用于不同类型的表单输入的单独方法,以及用于通常采用特定属性的元素的方法,其中这些属性可以直接指定为参数,而不是通过哈希。

实用 HTML 转义和其他类似函数的方法。

cgi/util.rb 中定义了一些实用工具。包含后,你可以像函数一样使用实用方法。

使用示例

获取表单值

require "cgi"
cgi = CGI.new
value = cgi['field_name']   # <== value string for 'field_name'
  # if not 'field_name' included, then return "".
fields = cgi.keys            # <== array of field names

# returns true if form has 'field_name'
cgi.has_key?('field_name')
cgi.has_key?('field_name')
cgi.include?('field_name')

注意!cgi['field_name'] 返回一个带有旧 cgi.rb 的数组(包含在 Ruby 1.6 中)

将表单值作为哈希获取

require "cgi"
cgi = CGI.new
params = cgi.params

cgi.params 是一个哈希。

cgi.params['new_field_name'] = ["value"]  # add new param
cgi.params['field_name'] = ["new_value"]  # change value
cgi.params.delete('field_name')           # delete param
cgi.params.clear                          # delete all params

将表单值保存到文件

require "pstore"
db = PStore.new("query.db")
db.transaction do
  db["params"] = cgi.params
end

从文件恢复表单值

require "pstore"
db = PStore.new("query.db")
db.transaction do
  cgi.params = db["params"]
end

获取多部分表单值

require "cgi"
cgi = CGI.new
value = cgi['field_name']   # <== value string for 'field_name'
value.read                  # <== body of value
value.local_path            # <== path to local file of value
value.original_filename     # <== original filename of value
value.content_type          # <== content_type of value

并且 value 具有 StringIO 或 Tempfile 类方法。

获取 cookie 值

require "cgi"
cgi = CGI.new
values = cgi.cookies['name']  # <== array of 'name'
  # if not 'name' included, then return [].
names = cgi.cookies.keys      # <== array of cookie names

并且 cgi.cookies 是一个哈希。

获取 cookie 对象

require "cgi"
cgi = CGI.new
for name, cookie in cgi.cookies
  cookie.expires = Time.now + 30
end
cgi.out("cookie" => cgi.cookies) {"string"}

cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }

require "cgi"
cgi = CGI.new
cgi.cookies['name'].expires = Time.now + 30
cgi.out("cookie" => cgi.cookies['name']) {"string"}

将 HTTP 标头和 HTML 字符串打印到 $DEFAULT_OUTPUT ($>)

require "cgi"
cgi = CGI.new("html4")  # add HTML generation methods
cgi.out do
  cgi.html do
    cgi.head do
      cgi.title { "TITLE" }
    end +
    cgi.body do
      cgi.form("ACTION" => "uri") do
        cgi.p do
          cgi.textarea("get_text") +
          cgi.br +
          cgi.submit
        end
      end +
      cgi.pre do
        CGI.escapeHTML(
          "params: #{cgi.params.inspect}\n" +
          "cookies: #{cgi.cookies.inspect}\n" +
          ENV.collect do |key, value|
            "#{key} --> #{value}\n"
          end.join("")
        )
      end
    end
  end
end

# add HTML generation methods
CGI.new("html3")    # html3.2
CGI.new("html4")    # html4.01 (Strict)
CGI.new("html4Tr")  # html4.01 Transitional
CGI.new("html4Fr")  # html4.01 Frameset
CGI.new("html5")    # html5

一些实用方法

require 'cgi/util'
CGI.escapeHTML('Usage: foo "bar" <baz>')

一些类似函数的实用方法

require 'cgi/util'
include CGI::Util
escapeHTML('Usage: foo "bar" <baz>')
h('Usage: foo "bar" <baz>') # alias

常量

CR

回车符的字符串

EOL

标准 Internet 换行符序列

HTTP_STATUS

HTTP 状态代码。

LF

换行符的字符串

MAX_MULTIPART_COUNT

多部分时请求参数的最大数量

NEEDS_BINMODE

是否需要在二进制与文本中处理

PATH_SEPARATOR

不同环境中的路径分隔符。

VERSION

属性

accept_charset[R]

返回此 CGI 实例的接受字符集。

公共类方法

accept_charset() 点击以切换源代码

返回所有新 CGI 实例的接受字符集。

# File cgi/core.rb, line 759
def self.accept_charset
  @@accept_charset
end
accept_charset=(accept_charset) 点击以切换源代码

设置所有新 CGI 实例的接受字符集。

# File cgi/core.rb, line 764
def self.accept_charset=(accept_charset)
  @@accept_charset=accept_charset
end
new(tag_maker) { block } 点击以切换源代码
new(options_hash = {}) { block }

创建新的 CGI 实例。

tag_maker

这与使用 options_hash 形式,值为 { :tag_maker => tag_maker } 相同。请注意,建议使用 options_hash 形式,因为它还允许你指定将接受的字符集。

options_hash

一个 Hash,识别三个选项

:accept_charset

指定接收到的查询字符串的编码。如果省略,则使用 @@accept_charset。如果编码无效,则会引发 CGI::InvalidEncoding

示例。假设 @@accept_charset 是“UTF-8”

当未指定时

cgi=CGI.new      # @accept_charset # => "UTF-8"

当指定为“EUC-JP”时

cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP"
:tag_maker

指定要使用的 HTML 生成方法版本的字符串。如果未指定,则不会加载任何 HTML 生成方法。

支持以下值

“html3”

HTML 3.x

“html4”

HTML 4.0

“html4Tr”

HTML 4.0 过渡

“html4Fr”

具有框架集的 HTML 4.0

“html5”

HTML 5

:max_multipart_length

指定多部分数据的最大长度。可以是整数标量或 lambda,它将在解析请求时进行计算。这允许在确定是否接受多部分数据时设置更复杂的逻辑(例如,查阅注册用户的上传限额)

默认值为 128 * 1024 * 1024 字节

cgi=CGI.new(:max_multipart_length => 268435456) # simple scalar

cgi=CGI.new(:max_multipart_length => -> {check_filesystem}) # lambda
block

如果提供,则在遇到无效编码时调用该块。例如

encoding_errors={}
cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value|
  encoding_errors[name] = value
end

最后,如果 CGI 对象不是在标准的 CGI 调用环境中创建的(也就是说,它无法在其环境中找到 REQUEST_METHOD),则它将在“脱机”模式下运行。在此模式下,它会从命令行或(如果失败)从标准输入中读取其参数。否则,cookie 和其他参数将根据 REQUEST_METHOD 自动从标准 CGI 位置解析,该位置因 REQUEST_METHOD 而异。

# File cgi/core.rb, line 850
def initialize(options = {}, &block) # :yields: name, value
  @accept_charset_error_block = block_given? ? block : nil
  @options={
    :accept_charset=>@@accept_charset,
    :max_multipart_length=>@@max_multipart_length
  }
  case options
  when Hash
    @options.merge!(options)
  when String
    @options[:tag_maker]=options
  end
  @accept_charset=@options[:accept_charset]
  @max_multipart_length=@options[:max_multipart_length]
  if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
    Apache.request.setup_cgi_env
  end

  extend QueryExtension
  @multipart = false

  initialize_query()  # set @params, @cookies
  @output_cookies = nil
  @output_hidden = nil

  case @options[:tag_maker]
  when "html3"
    require_relative 'html'
    extend Html3
    extend HtmlExtension
  when "html4"
    require_relative 'html'
    extend Html4
    extend HtmlExtension
  when "html4Tr"
    require_relative 'html'
    extend Html4Tr
    extend HtmlExtension
  when "html4Fr"
    require_relative 'html'
    extend Html4Tr
    extend Html4Fr
    extend HtmlExtension
  when "html5"
    require_relative 'html'
    extend Html5
    extend HtmlExtension
  end
end
parse(query) 点击以切换源代码

将 HTTP 查询字符串解析为键=>值对的哈希。

params = CGI.parse("query_string")
  # {"name1" => ["value1", "value2", ...],
  #  "name2" => ["value1", "value2", ...], ... }
# File cgi/core.rb, line 393
def self.parse(query)
  params = {}
  query.split(/[&;]/).each do |pairs|
    key, value = pairs.split('=',2).collect{|v| CGI.unescape(v) }

    next unless key

    params[key] ||= []
    params[key].push(value) if value
  end

  params.default=[].freeze
  params
end

公共实例方法

header(options='text/html')

HTML5 标签生成器处于非活动状态时,此方法是 http_header 的别名。

注意:使用 http_header 创建 HTTP 头部块,此别名仅为向后兼容而提供。

headerHTML5 标签生成器一起使用将创建一个 <header> 元素。

别名:http_header
http_header(headers_hash) 点击切换源码

创建一个 HTTP 头部块作为字符串。

包含结束头部块的空行。

content_type_string

如果使用此形式,则此字符串是 Content-Type

headers_hash

头部值的哈希。以下头部键被识别

type

Content-Type 头部。默认为 “text/html”

charset

正文的字符集,附加到 Content-Type 头部。

nph

一个布尔值。如果为 true,则前置协议字符串和状态码以及日期;并为 “server” 和 “connection” 设置默认值(如果未显式设置)。

status

作为 Status 头部返回的 HTTP 状态码(字符串)。这些值是

OK

200 OK

PARTIAL_CONTENT

206 Partial Content

MULTIPLE_CHOICES

300 Multiple Choices

MOVED

301 Moved Permanently

REDIRECT

302 Found

NOT_MODIFIED

304 Not Modified

BAD_REQUEST

400 Bad Request

AUTH_REQUIRED

401 Authorization Required

FORBIDDEN

403 Forbidden

NOT_FOUND

404 Not Found

METHOD_NOT_ALLOWED

405 Method Not Allowed

NOT_ACCEPTABLE

406 Not Acceptable

LENGTH_REQUIRED

411 Length Required

PRECONDITION_FAILED

412 Precondition Failed

SERVER_ERROR

500 Internal Server Error

NOT_IMPLEMENTED

501 Method Not Implemented

BAD_GATEWAY

502 Bad Gateway

VARIANT_ALSO_VARIES

506 Variant Also Negotiates

server

服务器软件,作为 Server 头部返回。

connection

连接类型,作为 Connection 头部返回(例如,“close”)。

length

将要发送的内容长度,作为 Content-Length 头部返回。

language

内容的语言,作为 Content-Language 头部返回。

expires

当前内容过期的时机,作为 Time 对象,作为 Expires 头部返回。

cookie

一个或多个 cookie,作为一或多个 Set-Cookie 头部返回。值可以是 cookie 的字面字符串;一个 CGI::Cookie 对象;字面 cookie 字符串或 Cookie 对象的数组;或者所有值都是字面 cookie 字符串或 Cookie 对象的哈希。

这些 cookie 是 @output_cookies 字段中保存的 cookie 的补充。

还可以设置其他头部;它们以 key: value 的形式附加。

示例

http_header
  # Content-Type: text/html

http_header("text/plain")
  # Content-Type: text/plain

http_header("nph"        => true,
            "status"     => "OK",  # == "200 OK"
              # "status"     => "200 GOOD",
            "server"     => ENV['SERVER_SOFTWARE'],
            "connection" => "close",
            "type"       => "text/html",
            "charset"    => "iso-2022-jp",
              # Content-Type: text/html; charset=iso-2022-jp
            "length"     => 103,
            "language"   => "ja",
            "expires"    => Time.now + 30,
            "cookie"     => [cookie1, cookie2],
            "my_header1" => "my_value",
            "my_header2" => "my_value")

此方法不执行字符集转换。

# File cgi/core.rb, line 160
def http_header(options='text/html')
  if options.is_a?(String)
    content_type = options
    buf = _header_for_string(content_type)
  elsif options.is_a?(Hash)
    if options.size == 1 && options.has_key?('type')
      content_type = options['type']
      buf = _header_for_string(content_type)
    else
      buf = _header_for_hash(options.dup)
    end
  else
    raise ArgumentError.new("expected String or Hash but got #{options.class}")
  end
  if defined?(MOD_RUBY)
    _header_for_modruby(buf)
    return ''
  else
    buf << EOL    # empty line of separator
    return buf
  end
end
也别名为:header
out(content_type_string='text/html') 点击切换源码
out(headers_hash)

将 HTTP 头部和正文打印到 $DEFAULT_OUTPUT ($>)

content_type_string

如果传递的是字符串,则假定为内容类型。

headers_hash

这是一个头部哈希,类似于 http_header 使用的哈希。

block

需要一个代码块,它应该解析为响应的正文。

Content-Length 会根据内容代码块返回的字符串大小自动计算。

如果 ENV['REQUEST_METHOD'] == "HEAD",则仅输出头部(仍然需要内容块,但会被忽略)。

如果字符集为 “iso-2022-jp”、“euc-jp” 或 “shift_jis”,则内容将转换为此字符集,并且语言设置为 “ja”。

示例

cgi = CGI.new
cgi.out{ "string" }
  # Content-Type: text/html
  # Content-Length: 6
  #
  # string

cgi.out("text/plain") { "string" }
  # Content-Type: text/plain
  # Content-Length: 6
  #
  # string

cgi.out("nph"        => true,
        "status"     => "OK",  # == "200 OK"
        "server"     => ENV['SERVER_SOFTWARE'],
        "connection" => "close",
        "type"       => "text/html",
        "charset"    => "iso-2022-jp",
          # Content-Type: text/html; charset=iso-2022-jp
        "language"   => "ja",
        "expires"    => Time.now + (3600 * 24 * 30),
        "cookie"     => [cookie1, cookie2],
        "my_header1" => "my_value",
        "my_header2" => "my_value") { "string" }
   # HTTP/1.1 200 OK
   # Date: Sun, 15 May 2011 17:35:54 GMT
   # Server: Apache 2.2.0
   # Connection: close
   # Content-Type: text/html; charset=iso-2022-jp
   # Content-Length: 6
   # Content-Language: ja
   # Expires: Tue, 14 Jun 2011 17:35:54 GMT
   # Set-Cookie: foo
   # Set-Cookie: bar
   # my_header1: my_value
   # my_header2: my_value
   #
   # string
# File cgi/core.rb, line 367
def out(options = "text/html") # :yield:

  options = { "type" => options } if options.kind_of?(String)
  content = yield
  options["length"] = content.bytesize.to_s
  output = stdoutput
  output.binmode if defined? output.binmode
  output.print http_header(options)
  output.print content unless "HEAD" == env_table['REQUEST_METHOD']
end
print(*options) 点击切换源码

将一个或多个参数打印到默认输出流

cgi = CGI.new
cgi.print    # default:  cgi.print == $DEFAULT_OUTPUT.print

私有实例方法

_no_crlf_check(str) 点击切换源码
# File cgi/core.rb, line 191
def _no_crlf_check(str)
  if str
    str = str.to_s
    raise "A HTTP status or header field must not include CR and LF" if str =~ /[\r\n]/
    str
  else
    nil
  end
end
env_table() 点击切换源码

ENV 的同义词。

# File cgi/core.rb, line 59
def env_table
  ENV
end
stdinput() 点击切换源码

$stdin 的同义词。

# File cgi/core.rb, line 64
def stdinput
  $stdin
end
stdoutput() 点击切换源码

$stdout 的同义词。

# File cgi/core.rb, line 69
def stdoutput
  $stdout
end