Вопрос

Я работаю над веб-сайтом, размещенным в службе Microsoft Office live.Здесь есть контактная форма, позволяющая посетителям связаться с владельцем.Я хочу написать скрипт Ruby, который находится на отдельном сервере и в который будет отправляться форма.Он проанализирует данные формы и отправит подробную информацию по электронной почте на заранее установленный адрес.Затем скрипт должен перенаправить браузер на страницу подтверждения.

У меня есть выносливая машина Ubuntu, работающая под управлением nginx и postfix.Ruby установлен, и мы посмотрим, как использовать функциональность Thin и it's Rack для обработки скрипта.Теперь дело дошло до написания сценария, и я нарисовал пробел.

Это было давно, и, если я правильно помню, процесс выглядит примерно так;

  • прочитать HTTP-заголовок
  • анализировать параметры
  • отправить электронное письмо
  • отправить заголовок перенаправления

Вообще говоря, на этот вопрос был дан ответ.Выяснить, как использовать ответ, оказалось сложнее, чем ожидалось, и я подумал, что стоит поделиться.

Первые шаги:

Я довольно внезапно узнал, что nginx напрямую не поддерживает cgi-скрипты.Вы должны использовать какой-то другой процесс, чтобы запустить скрипт и перенаправить запросы nginx на прокси.Если бы я делал это на php (что, на мой взгляд, было бы более естественным выбором), я мог бы использовать что-то вроде php-fcgi и ожидать, что жизнь была бы довольно простой.

Ruby и fcgi чувствовали себя довольно устрашающе.Но если мы отказываемся от идеала загрузки этих вещей во время выполнения, то Rack, вероятно, является самым простым решением, и Thin включает в себя все, что нам нужно.Изучение того, как создавать с их помощью простые небольшие приложения, было чрезвычайно полезным для такого относительного новичка в Rails, как я.Основы приложения Rails могут долгое время казаться скрытыми, и Rack помогла мне немного приоткрыть завесу.

Тем не менее, последовать совету Иегуды и поискать синатру стало еще одним сюрпризом.Теперь у меня есть базовое приложение sinatra, работающее в тонком экземпляре.Он взаимодействует с nginx через сокет unix, как я понимаю, стандартным способом.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::действие запускает все и возвращает адрес, на который контроллер должен перенаправить.

Существует отдельный класс 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::successful и ContactPost::failed возвращают адрес перенаправления, комбинируя пути, предоставленные в HTML-форме, с URI запроса.refererer.Таким образом, все поведение задается в HTML-форме.Будущие веб-сайты, использующие этот скрипт, просто должны быть перечислены в личном файле пользователя ~/cgi/contact.conf, и они исчезнут.Это связано с тем, что ContactTarget ищет подробности в /home/:target/cgi/contact.conf.Может быть, когда-нибудь это будет неуместно, но сейчас это просто отлично подходит для моих целей.

Метод отправки достаточно прост, он создает экземпляр простого класса электронной почты и отправляет его.Класс 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, такую как Sinatra:

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

Класс CGI Ruby может быть использован для написания CGI-скриптов.Пожалуйста, проверьте: http://www.ruby-doc.org/stdlib/libdoc/cgi/rdoc/index.html

Кстати, нет необходимости читать HTTP-заголовок.Синтаксический анализ параметров будет простым с помощью CGI-класса.Затем отправьте электронное письмо и сделайте перенаправление.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top