Я работаю над сайтом, размещенным в офисе Microsoft. Он имеет контактную форму, позволяющую посетителям связаться с владельцем. Я хочу написать скрипт на Ruby, который находится на отдельном сервере и к которому будет отправлена форма. Он проанализирует данные формы и отправит детали по электронной почте на заданный адрес. Затем скрипт должен перенаправить браузер на страницу подтверждения.
У меня есть выносливый компьютер с Ubuntu, на котором запущены nginx и postfix. Ruby установлен, и мы рассмотрим использование Thin и его функциональных возможностей Rack для обработки скрипта. Теперь пришло время написать сценарий, и я нарисовал бланк.
Прошло много времени, и если я правильно помню, процесс выглядит примерно так:
- читать заголовок HTTP
- Параметры разбора
- отправить письмо
- отправить заголовок перенаправления
В широком смысле на вопрос был дан ответ. Выяснить, как использовать ответ, оказалось сложнее, чем ожидалось, и я подумал, что стоит поделиться.
Первые шаги:
Я довольно неожиданно узнал, что nginx напрямую не поддерживает скрипты cgi. Вы должны использовать какой-то другой процесс для запуска скрипта и пересылки запросов nginx к прокси. Если бы я делал это на php (что, на мой взгляд, было бы более естественным выбором), я мог бы использовать что-то вроде php-fcgi и ожидать, что жизнь будет довольно простой.
Ruby и fcgi чувствовали себя довольно устрашающе. Но если мы отказываемся от идеала загрузки этих вещей во время выполнения, то Rack, вероятно, является наиболее прямым решением, а Thin включает в себя все, что нам нужно. Изучение того, как создавать базовые небольшие приложения с их помощью, было очень полезно для такого новичка в Rails, как я. Основы Rails-приложения могут казаться скрытыми долгое время, и Rack помог мне немного поднять занавес.
Тем не менее, следование совету Иегуды и поиск синатры стало еще одним сюрпризом. Теперь у меня есть базовое приложение sinatra, работающее в Thin. Он связывается с nginx через сокет unix, что, как я понимаю, является стандартным способом. Sinatra позволяет действительно элегантно обрабатывать различные запросы и маршруты в приложение. Все, что вам нужно, это get '/' {}
, чтобы начать обработку запросов к виртуальному хосту. Чтобы добавить больше (в чистом виде), мы просто включаем маршруты / 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, который выполняет некоторую аутентификацию, чтобы убедиться, что указанная цель принимает сообщения с URL-адреса, указанного в request.referrer. Это обрабатывается в 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 :: success и ContactPost :: failed каждый возвращает адрес перенаправления, комбинируя пути, предоставленные в форме HTML, с URI request.referer. Таким образом, все поведение указывается в форме 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 знать, с каким сокетом поговорить, и нас нет.
Спасибо всем за ваши полезные указатели в правильном направлении! Да здравствует Синатра!