私はマイクロソフトのオフィスライブサービスでホストされているWebサイトで作業しています。訪問者が所有者と連絡を取ることができる連絡フォームがあります。別のサーバーに配置され、フォームがPOSTされるRubyスクリプトを作成したいと思います。フォームデータを解析し、詳細を事前設定されたアドレスに電子メールで送信します。次に、スクリプトはブラウザを確認ページにリダイレクトする必要があります。
私はnginxとpostfixを実行しているubuntuの丈夫なマシンを持っています。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
そのような単純なことを理解することの全くの恐怖は、しばらくの間私にとどまります。.rbファイルが実行されることをnginxに冷静に知らせ、それを実行することを期待していました。この小さな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にどのソケットと通信するかを知らせることだけです。
正しい方向への有益な指針をありがとうございました!長生きするシナトラ!