将HTML表单发布到remote.cgi - 用Ruby编写?

时间:2009-06-12 16:43:59

标签: ruby email cgi sinatra thin

我正在微软办公室现场服务上托管的网站上工作。它有一个联系表格,使访客可以与业主联系。我想编写一个位于单独服务器上的Ruby脚本,表单将POST到该脚本。它将解析表单数据并通过电子邮件将详细信息发送到预设地址。然后,该脚本应将浏览器重定向到确认页面。

我有一台运行nginx和postfix的ubuntu hardy机器。安装了Ruby,我们将看到使用Thin和它的Rack功能来处理脚本。现在是编写脚本,我画了一个空白。

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

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

从广义上讲,问题已得到解答。弄清楚如何使用答案比预期更复杂,我认为值得分享。

第一步:

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

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

尽管如此,遵循耶胡达的建议并查找sinatra是另一个惊喜。我现在有一个在Thin实例中运行的基本sinatra应用程序。它通过unix socket与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中查找详细信息。也许oneday这是不合适的,但是现在它对我的目的来说还不错。

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知道要与哪个套接字进行通信,我们就离开了。

感谢大家在正确的方向上提供有用的指示!辛那太万岁!

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

执行此操作的最佳方法可能是使用像Sinatra这样的现有Ruby库:

require "rubygems"
require "sinatra"

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

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

答案 2 :(得分:0)

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

顺便说一句,没有必要读取HTTP头。使用CGI类可以轻松解析参数。然后,发送电子邮件并重定向。