Ruby Web 应用
Ruby Web 应用 - CGI 编程基础
Section titled “Ruby Web 应用 - CGI 编程基础”通用网关接口(Common Gateway Interface,CGI)是一个标准协议,定义了 Web 服务器软件如何将网页生成委托给可执行文件(脚本)。虽然 Ruby 是一种功能强大的语言,可以做更多事情,但理解 CGI 能提供关于 Web 服务器和应用程序代码如何交互的基础认识。现代 Ruby Web 开发主要使用诸如 Ruby on Rails、Sinatra 或 Hanami 等框架,这些框架通过 Rack 等接口抽象了 CGI 的底层细节。然而,Ruby 的 cgi 标准库仍然可用于简单的 CGI 脚本编写或解析 CGI 环境变量和查询参数。
本教程重点介绍使用 Ruby 的 cgi 库进行 CGI 脚本编写的基础知识。需要注意的是,由于与现代框架相比,直接的 CGI 脚本编写在性能、可伸缩性和开发复杂性方面存在劣势,因此很少用于新建复杂的 Web 应用程序。
一个基本的 CGI 脚本:
Section titled “一个基本的 CGI 脚本:”CGI 脚本必须输出一个有效的 HTTP 头部,后跟内容(例如 HTML)。最小的头部包括 Content-Type。
#!/usr/bin/env ruby
# 输出 HTTP 头部puts "Content-Type: text/html\n\n" # 双换行非常重要
# 输出 HTML 内容puts "<html>"puts " <head><title>My First CGI Script</title></head>"puts " <body>"puts " <h1>Hello from Ruby CGI!</h1>"puts " <p>Current time: #{Time.now}</p>"puts " </body>"puts "</html>"要运行它,你需要将脚本(例如 test.cgi)放在 Web 服务器支持 CGI 的目录中(比如 cgi-bin),使其可执行(chmod +x test.cgi),并通过 Web 浏览器访问它(例如 http://yourserver.com/cgi-bin/test.cgi)。Web 服务器会执行该脚本,其输出将被发送到浏览器。
使用 cgi 库:
Section titled “使用 cgi 库:”Ruby 的 cgi 库通过提供处理 HTTP 头部、查询参数、Cookie 和生成 HTML 的方法,简化了 CGI 脚本编写。
#!/usr/bin/env rubyrequire 'cgi'
cgi = CGI.new
# 使用 cgi.header 输出头部# 这会自动将 Content-Type 默认设置为 text/html# 并处理其他可能的头部需求。puts cgi.header
puts "<html>"puts " <head><title>CGI Library Test</title></head>"puts " <body>"puts " <h1>Using Ruby's cgi Library</h1>"puts " </body>"puts "</html>"处理表单数据:
Section titled “处理表单数据:”cgi 库可以轻松访问从 HTML 表单发送的参数(通过 GET 或 POST 方法)。参数可以通过 CGI 对象访问,该对象表现得像一个 hash。
假设你有一个像这样的 HTML 表单:
<html><body> <form method="POST" action="/cgi-bin/process_form.cgi"> Your Name: <input type="text" name="username" value=""><br> Your Email: <input type="text" name="email" value=""><br> <input type="submit" value="Submit"> </form></body></html>process_form.cgi 脚本可以是:
#!/usr/bin/env ruby# process_form.cgirequire 'cgi'
cgi = CGI.new
username = cgi['username'] # 返回单个值(如果存在多个则返回第一个)email = cgi.params['email'] # 返回值的数组
puts cgi.headerputs "<html><head><title>Form Data</title></head><body>"puts "<h1>Submitted Data:</h1>"puts "<p>Username: #{CGI.escapeHTML(username.to_s)}</p>"# cgi.params['email'] 返回一个数组,因此访问其第一个元素puts "<p>Email: #{CGI.escapeHTML(email.first.to_s)}</p>"
puts "<h2>All Parameters:</h2>"cgi.params.each do |key, values| puts "<p>**#{CGI.escapeHTML(key)}:** #{CGI.escapeHTML(values.join(', '))}</p>"end
puts "</body></html>"cgi['param_name'] 获取参数的第一个值。cgi.params 返回一个 hash,其中键是参数名,值是提交值的数组(因为参数名可以出现多次)。在显示用户提交的数据时,务必始终使用 CGI.escapeHTML 以防止跨站脚本攻击(Cross-Site Scripting,XSS)漏洞。
使用 cgi 生成 HTML:
Section titled “使用 cgi 生成 HTML:”cgi 库包含用于生成 HTML 标签的方法。你可以向 CGI.new 传递 "html5" 来指示 HTML5 doctype,尽管现代实践通常涉及模板引擎来生成复杂的 HTML。
#!/usr/bin/env rubyrequire 'cgi'
# 使用 'html5' 表示 HTML5 doctype,或使用 'html4' 表示较旧的 HTML# 单独使用 CGI.new 会默认生成一个基本的 HTML 结构。# 对于现代 HTML5 更精确的控制,你可以手动构建或使用 CGI.new("html5")cgi = CGI.new("html5") # 这有助于 doctype 和结构
# cgi.out 接受一个块,并输出必要的头部和 HTML 结构cgi.out do cgi.html do cgi.head do cgi.title { "HTML Generation Test" } + cgi.style { "body { font-family: sans-serif; }" } # 内联样式示例 end + cgi.body do cgi.h1 { "Generated HTML Page" } + cgi.p { "This page was generated using Ruby's cgi library." } + cgi.form(method: "post", action: "self_script.cgi") do cgi.label("Your message: ", for: "message_id") + cgi.textarea(name: "message", id: "message_id") { "Default text" } + cgi.br + cgi.submit(value: "Send") end end endend这种 HTML 生成方法对于大型页面来说可能会变得笨拙。现代框架中更倾向于使用模板系统,如 ERB(Ruby 标准库的一部分)或专门的模板 gem(例如 Slim, Haml)。
转义特殊字符:
Section titled “转义特殊字符:”处理 URL 或 HTML 时,特殊字符必须被转义。
CGI.escape(string): 对字符串进行 URL 编码(例如,空格变为+或%20,/变为%2F)。CGI.unescape(string): 解码 URL 编码的字符串。CGI.escapeHTML(string): 转义 HTML 中的特殊字符(例如,<变为<,&变为&)。在显示用户输入时,这对于安全性至关重要。CGI.unescapeHTML(string): 将 HTML 实体转换回字符(谨慎使用)。
#!/usr/bin/env rubyrequire 'cgi'
raw_url_param = "A value with / and &"encoded_param = CGI.escape(raw_url_param)puts "Encoded for URL: #{encoded_param}" #=> A+value+with+%2F+and+%26
html_input = "<script>alert('XSS')</script>"escaped_html = CGI.escapeHTML(html_input)puts "Escaped for HTML: #{escaped_html}" #=> <script>alert('XSS')</script>使用 cgi 处理 Cookie 和 Session:
Section titled “使用 cgi 处理 Cookie 和 Session:”cgi 库提供了处理 HTTP Cookie 的基本支持。
#!/usr/bin/env rubyrequire 'cgi'
cgi = CGI.new
# 读取现有 cookieuser_visits = cgi.cookies['visits'].first.to_i || 0user_visits += 1
# 设置一个新 cookie(或更新现有 cookie)new_cookie = CGI::Cookie.new( 'name' => 'visits', 'value' => user_visits.to_s, 'path' => '/', # 可选:cookie 路径 'expires' => Time.now + (3600*24*30) # 可选:过期时间(从现在起 30 天))
# 输出头部,包括 Set-Cookie 指示puts cgi.header('cookie' => [new_cookie])
puts "<html><body>"puts "<h1>Cookie Test</h1>"puts "<p>You have visited this page #{user_visits} times.</p>"puts "</body></html>"单独使用 cgi 管理会话(跟踪跨多个请求的用户状态)是手动的。cgi/session 库(如果可用或需要显式 require)提供了一些会话管理功能,但通常很复杂。现代框架具有健壮的内置会话处理能力。
局限性与现代替代方案:
Section titled “局限性与现代替代方案:”直接的 CGI 脚本编写对于现代 Web 应用程序有显著缺点:
- 性能: 通常每个 CGI 请求都会启动一个新的进程,这会占用大量资源。
- 状态管理: 跨请求维护状态(如用户会话)很麻烦。
- 开发复杂性: 缺乏现代框架的结构、路由、ORM 和模板特性。
- 可伸缩性: 扩展 CGI 应用程序可能很困难。
对于任何非简单的 Ruby Web 应用程序,考虑使用基于 Rack 的框架:
- Ruby on Rails: 一个功能齐全、基于约定优于配置的框架,用于构建大型应用程序。
- Sinatra: 一个轻量级的 DSL,用于快速创建 Web 应用程序,只需极少的努力。
- Hanami: 一个现代的全栈 Ruby 框架,专注于清晰的架构和可维护性。
这些框架为 Web 开发提供了更高效、可伸缩且更易于维护的环境。