0

我正在开发一个托管在 Microsoft 的 Office Live 服务上的网站。它有一个联系表格,使访客能够与所有者取得联系。我想编写一个 Ruby 脚本,它位于一个单独的服务器上,表单将发布到该脚本。它将解析表单数据并将详细信息通过电子邮件发送到预设地址。然后该脚本应将浏览器重定向到确认页面。

我有一台运行 nginx 和 postfix 的 ubuntu hardy 机器。Ruby 已安装,我们将了解如何使用 Thin 及其 Rack 功能来处理脚本。现在到了写剧本的时候了,我画了一个空白。

已经很长时间了,如果我没记错的话,这个过程是这样的;

  • 读取 HTTP 标头
  • 解析参数
  • 发送电子邮件
  • 发送重定向标头

从广义上讲,这个问题已经得到了回答。弄清楚如何使用答案比预期的要复杂,我认为值得分享。

第一步:

我突然了解到 nginx 不直接支持 cgi 脚本。您必须使用其他一些进程来运行脚本并让 nginx 代理请求。如果我在 php 中执行此操作(事后看来,我认为这将是一个更自然的选择),我可以使用 php-fcgi 之类的东西,并期望生活会非常简单。

Ruby 和 fcgi 感觉相当令人生畏。但是,如果我们放弃在运行时加载这些东西的理想,那么 Rack 可能是最直接的解决方案,而 Thin 包含我们需要的一切。学习如何用它们制作基本的小应用程序对像我这样的 Rails 新手来说非常有益。Rails 应用程序的基础似乎隐藏了很长时间,而 Rack 帮助我进一步揭开了帷幕。

尽管如此,听从耶胡达的建议并查阅辛纳屈,却是另一个惊喜。我现在有一个在 Thin 实例中运行的基本 sinatra 应用程序。它以我收集的标准方式通过 unix 套接字与 nginx 通信。Sinatra 提供了一种非常优雅的方式来处理进入应用程序的不同请求和路由。您所需要的只是get '/' {}开始处理对虚拟主机的请求。要添加更多(以干净的方式),我们只需将 routes/script.rb 包含到主文件中。

# cgi-bin.rb
# main file loaded as a sinatra app

require 'sinatra'

# load cgi routes
require 'routes/default'
require 'routes/contact'

# 404 behaviour
not_found do
  "Sorry, this CGI host does not recognize that request."
end

这些路由文件将调用存储在单独的类库中的功能:

# routes/contact.rb
# contact controller

require 'lib/contact/contactTarget'
require 'lib/contact/contactPost'

post '/contact/:target/?' do |target|
  # the target for the message is taken from the URL
  msg = ContactPost.new(request, target)
  redirect msg.action, 302
end

弄清楚这么简单的事情的纯粹恐惧会伴随我一段时间。我期待冷静地让 nginx 知道 .rb 文件将被执行并继续执行它。现在这个小 sinatra 应用程序已经启动并运行了,如果我将来想添加额外的功能,我可以直接投入使用。

执行:

ContactPost 类处理消息传递方面。它只需要知道请求中的参数和电子邮件的目标。ContactPost::action 启动一切并返回控制器重定向到的地址。

有一个单独的 ContactTarget 类,它执行一些身份验证以确保指定的目标接受来自 request.referrer 中给出的 URL 的消息。这是在 ContactTarget::accept? 正如我们可以从 ContactPost::action 方法中猜到的那样;


# lib/contact/contactPost.rb

class ContactPost

# ...

  def action
    return failed unless @target.accept? @request.referer
    if send?
      successful
    else
      failed
    end
  end

# ...

end

ContactPost::successful 和 ContactPost::failed 通过将 HTML 表单提供的路径与 request.referer URI 结合起来,各自返回一个重定向地址。因此,所有行为都在 HTML 表单中指定。未来使用此脚本的网站只需在用户自己的 ~/cgi/contact.conf 中列出,它们就会消失。这是因为 ContactTarget 在 /home/:target/cgi/contact.conf 中查找详细信息。也许有一天这将是不合适的,但现在它对我的目的来说还不错。

send 方法很简单,它创建一个简单的 Email 类的实例并将其发送出去。Email 类几乎基于 Ruby net/smtp 文档中给出的标准用法示例;

# lib/email/email.rb
require 'net/smtp'

class Email
  def initialize(from_alias, to, reply, subject, body)
    @from_alias = from_alias
    @from = "cgi_user@host.domain.com"
    @to = to
    @reply = reply
    @subject = subject
    @body = body
  end

  def send
    Net::SMTP.start('localhost', 25) do |smtp|
      smtp.send_message to_s, @from, @to
    end
  end

  def to_s
<<END_OF_MESSAGE
From: #{@from_alias} 
To: #{@to} 
Reply-To: #{@from_alias} 
Subject: #{@subject}
Date: #{DateTime::now().to_s}

#{@body}
END_OF_MESSAGE
  end
end

我需要做的就是构建应用程序,让 nginx 知道要与哪个套接字通信,然后我们就走了。

感谢大家对正确方向的有用指示!辛纳特拉万岁!

4

3 回答 3

2

这一切都在 Net 模块中,这里有一个例子:

  @net = Net::HTTP.new 'http://www.foo.com', 80
  @params = {:name => 'doris', :email => 'doris@foo.com'}

  # Create HTTP request
  req = Net::HTTP::Post.new( 'script.cgi', {} )
  req.set_form_data @params

  # Send request
  response = @net.start do |http|
    http.read_timeout = 5600
    http.request req
  end
于 2009-06-12T16:52:40.707 回答
1

可能最好的方法是使用现有的 Ruby 库,如 Sinatra:

require "rubygems"
require "sinatra"

get "/myurl" do
  # params hash available here
  # send email
end

您可能希望使用 MailFactory 来发送实际的电子邮件,但您绝对不需要处理标题或解析参数。

于 2009-06-12T17:13:54.993 回答
0

Ruby 的 CGI 类可用于编写 CGI 脚本。请检查:http ://www.ruby-doc.org/stdlib/libdoc/cgi/rdoc/index.html

顺便说一句,不需要读取 HTTP 标头。使用 CGI 类解析参数将很容易。然后,发送电子邮件并重定向。

于 2009-06-12T16:53:49.097 回答