Размещение HTML-формы в remote.cgi - написано на Ruby? - PullRequest
0 голосов
/ 12 июня 2009

Я работаю над сайтом, размещенным в офисе 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 знать, с каким сокетом поговорить, и нас нет.

Спасибо всем за ваши полезные указатели в правильном направлении! Да здравствует Синатра!

Ответы [ 3 ]

2 голосов
/ 12 июня 2009

Это все в сетевом модуле, вот пример:

  @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 голос
/ 12 июня 2009

Вероятно, лучший способ сделать это - использовать существующую библиотеку Ruby, такую ​​как Sinatra:

require "rubygems"
require "sinatra"

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

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

0 голосов
/ 12 июня 2009

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

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

...