HTML フォームを Remote.cgi に POST します - Ruby で書かれていますか?
質問
私は Microsoft の Office Live サービスでホストされている Web サイトで作業しています。訪問者が所有者に連絡できるお問い合わせフォームがあります。別のサーバー上に配置され、フォームが POST する Ruby スクリプトを作成したいと考えています。フォームデータを解析し、詳細を事前に設定されたアドレスに電子メールで送信します。その後、スクリプトはブラウザを確認ページにリダイレクトする必要があります。
私はnginxとpostfixを実行しているubuntuの頑丈なマシンを持っています。Ruby がインストールされているので、Thin とその Rack 機能を使用してスクリプトを処理する方法を見ていきます。さて、脚本を書くことになり、空白を描きました。
長い時間が経ちましたが、私の記憶が正しければ、そのプロセスは次のようなものです。
- HTTPヘッダーを読み取る
- パラメータを解析する
- メールを送る
- リダイレクトヘッダーを送信する
大まかに言えば、質問は答えられました。答えをどう使うかを考えるのは予想よりも複雑だったので、共有する価値があると思いました。
最初のステップ:
nginx が CGI スクリプトを直接サポートしていないことを、私はかなり突然知りました。スクリプトを実行し、nginx にリクエストをプロキシさせるには、他のプロセスを使用する必要があります。これを php で実行していた場合 (後から考えると、これがより自然な選択だったと思います)、php-fcgi のようなものを使用でき、作業は非常に簡単になると予想されます。
Ruby と fcgi はかなり難しく感じました。しかし、実行時にこれらのものをロードするという理想を放棄するのであれば、Rack がおそらく最も単純なソリューションであり、Thin には必要なものがすべて含まれています。これらを使って基本的な小さなアプリを作成する方法を学ぶことは、私のような Rails 初心者にとって非常に有益です。Rails アプリの基礎は長い間隠されているように見えましたが、Rack はその幕を少しだけ引き上げるのに役立ちました。
それにもかかわらず、イェフダのアドバイスに従ってシナトラを調べてみると、またしても驚かされました。これで、基本的な Sinatra アプリが Thin インスタンスで実行されました。私が収集した標準的な方法では、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 はすべてを開始し、コントローラーのリダイレクト先のアドレスを返します。
指定されたターゲットが request.referrer で指定された URL からのメッセージを受け入れることを確認するために何らかの認証を行う別の ContactTarget クラスがあります。これは 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 フォームで指定されます。今後このスクリプトを使用する Web サイトは、ユーザー自身の ~/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 に知らせることだけです。これで準備は完了です。
正しい方向への有益な指針を提供してくれた皆さん、ありがとう!シナトラ万歳!
解決
おそらくこれを行うための最善の方法は、シナトラのような既存のRubyライブラリを使用するようになります:
require "rubygems"
require "sinatra"
get "/myurl" do
# params hash available here
# send email
end
あなたは、おそらく実際の電子メールを送信するためにMailFactoryを使いたいでしょうが、あなたは間違いなく、ヘッダや解析パラメータで約いじくるする必要はありません。
他のヒント
これは、ネットモジュール内のすべてのですが、ここでの例です。
@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
のRubyのCGIクラスは、CGIスクリプトを書くために使用することができます。確認してください: http://www.ruby-doc.org /stdlib/libdoc/cgi/rdoc/index.htmlする
ところで、HTTPヘッダーを読み取る必要はありません。パースparametresはCGIクラスを使用して簡単になります。その後、電子メールを送信し、リダイレクトします。