[ruby-list:46797] ERBを利用した問合せメールシステム開発

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

[ruby-list:46797] ERBを利用した問合せメールシステム開発

ShingoKintaka
Shingo Kintakaです。

現在、会社等で利用される事の多い「問合せフォームCGI」を作成しております。
実行環境は@niftyが提供するLacoocanサービス。

先輩方の指摘と知恵を頂きたく、ソースコードをメール末尾に記述しております。
※ソースコードは ruby -c ./request_oem_chk.cgi として Syntax OK を確認しております。
※プログラムはrequest_oem_chk.cgiだけで、その他は画面表示用のhtmlファイル(テンプレート)とcssファイルです。
※問合せフォームのあるページは「request_oem.html」です。

長文です。
ERBを利用した問合せメールシステムを設計してみたのですが、テンプレートの読み込み表示処理が思うように動作しておりません。
理解できたフリや知ったかぶりをしてそれらしい質問を投げるよりも、現状の私の技術と問題をさらけだし、識者からの厳しい指摘を受けた方が良いと判断しソースコードを添付しております。

設計してみた問合せメールシステムを動作させる事もこの質問を投げる目的ではありますが、第一に私はどうしてもRubyを習得したい。
設計製造していて、こんなに楽しい言語は他にありません。
ERBを利用した技術を取得したい。もっともっと技術力を上げたい。
Rubyでもっともっと沢山の処理をこなせるようになりたいです。
是非、お力をお借りしたく、お願い申し上げます。

<仕様概略>

HTML(問合せ入力ページ)表示
    ↓
HTML(formのactionからcgiコール)
    ↓
HTML(問合せ入力ページ)のformタグ内にある入力パラメータを取得
    ↓
取得した入力パラメータの内容が正しいかどうかチェック
    ↓                   ↓
HTML(確認画面)表示              HTML(エラー画面表示)
    ↓                   ↓
HTML(確認画面formのactionからcgiコール)   ※再び入力画面へ戻る
    ↓
sendmailを利用したメール送信
    ↓
ありがとうございました画面を表示




<抱えている問題>
HTMLと処理を行うcgiを分けたかった為、ERBを利用したテンプレートを読み込む仕様を設計してみました。
簡単なERBのサンプルを作ったのちに、本番の問合せフォームを作成したのでススっと出来ると思ったのですが。。。。

1.HTMLの中に<%= XXXX %>を埋め込んだテンプレートを正しく読み込めない
  テンプレートである「errormsg.html」にある<%= err_msg %>は思う通りに表示できております。
  ※err_msgには入力チェックにひっかかった内容のエラー指摘文章が入っています。
   動作させると、赤字でエラー文章が列挙されます。
  
  しかしながら、新しく<%= hogehoge  %>等をHTMLの中に追加すると、ブラウザに「500Internal_Error」と表示されます。
  確認用画面のテンプレートである「affirmation_oem.html」も正しく値(@で始まるクラス変数)が表示されません。
  ※エラーを表示させるためのerr_cgiメソッドを作成し、begin〜endを利用してエラー表示させているはずなのですが。。???
  
  テンプレートを読み込む処理は、RequestOEMChkクラスのメソッドに下記のように実装しております。

 # 確認画面の表示
  def show_inquiry
    begin
      templates = ERB.new(File.read(AFFIR_HTM),nil,"-")
      templates.run
    rescue
      error_cgi
    end
  end

  私のERBオブジェクトの使い方が間違っているのでしょうか???

2.エラー表示メソッドを実装しているはずなのに、「500Internal_Error」が表示される
  私は、google検索と書籍「Ruby de CGI」を参考にして下記のメソッドを実装しました。

  def error_cgi
    print "*** CGI Error List ***<br>"
    print "#{CGI.escapeHTML($!.inspect)}<br>"
    $@.each { |x|
      print CGI.escapeHTML(x), "<br>"
    }
  end

  このメソッドをソースコードのメイン処理部分に下記の要領で実装しました。

if __FILE__ == $0
  begin
    req_oem_chk = RequestOEMChk.new                     # オブジェクト生成
    req_oem_chk.get_form                                # 入力データを取得する
    err_msg = req_oem_chk.data_check                    # 獲得したデータをチェックする
    case err_msg                                        # データチェック
    when "OK"                                           # OK
      req_oem_chk.show_inquiry                            # 確認画面表示
    else                                                # NG
      req_oem_chk.show_errorhtm(err_msg)                  # エラー画面表示
    end
  rescue
    req_oem_chk.error_cgi                               # エラー処理 <----ココ
  end
end

  私の中では、この実装により「処理内部で発生するエラーは確実に拾える」と思っておりました。
  それなのに何故「500Internal_Error」とブラウザに表示されるのでしょうか???




以上となります。
このメーリングリストがサポートセンターでは無く、メンバーの皆さんも忙しい何かを抱えている事も理解できているのですが、設計方法のまずさやコーディングに対する指摘など頂ければ幸いです。


以下がテンプレートのhtmlコードとcgiコードとなります。




--<問合せフォーム:request_oem.html>-- ここから
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>販売代理店・OEM お問い合わせフォーム</title>
  <meta http-equiv="Content-Type" content="text/html; charset=euc-jp" />
  <link rel="stylesheet" type="text/css" href="css/request_oem.css" media="all">
 </head>
 <body>
   <h1>販売代理店/OEM お客様お問合せ</h1>
<p>

販売代理店/OEMに関する様々なお問合せは、電話または、こちらの問合せフォームより承っております。

<h2>電話でのお問合せ</h2>

<p>
商品の企画・開発およびPB商品の事など、気軽にお問合せください。<br>
<table class="line">
  <tr>
    <td>住所</td>
    <td>
      〒000-1111 サイド6ホワイトベース<br>
      第13独立部隊 (だいじゅうさんどくりつぶたい)
    </td>
  </tr>
  <tr>
    <td>Tel</td>
    <td>0000-11-2222</td>
  </tr>
  <tr>
    <td>受付時間</td>
    <td>平日10:30〜17:30</td>
  </tr>
</table>
</p>

<h2>お問い合わせフォーム</h2>
以下のフォームより弊社宛のお問合せ内容を送信して下さい。<br>
内容を確認後、カスタマーサポート担当者からメールにて回答を返信いたします。<br>
<br>
なお、お問い合わせいただきました内容によっては協力会社等の関係部署に確認する必要があるため、<br>
ご返答までに1週間程度のお時間をいただく場合がございます。あらかじめご了承ください。 <br>
<br>
<span class="hissu">*</span>は入力必須項目です。
</p>

<form action="cgi/request_oem_chk.cgi" method="post">
  <table>
    <tr>
      <td width="200">貴社名<span class="hissu">*</span></td>
      <td width="356"><input type="text" name="CompanyName" size="40"></td>
    </tr>
    <tr>
      <td>お問合せ種別<span class="hissu">*</span></td>
          <td>
          <select name="EnquiryClass">
          <option value="SalesAgent">販売代理店</option>
          <option value="OEM">OEM開発</option>
          <option value="Other">その他</option>
          </select>
      </td>
    </tr>
    <tr>
      <td>部署名</td>
      <td><input type="text" name="PostName" size="40"></td>
    </tr>
    <tr>
      <td>お名前(カナ)<span class="hissu">*</span></td>
      <td><input type="text" name="NameKana" size="40"></td>
    </tr>
    <tr>
      <td>お名前(漢字)<span class="hissu">*</span></td>
      <td><input type="text" name="NameKanji" size="40"></td>
    </tr>
    <tr>
      <td>郵便番号</td>
      <td><input type="text" name="ZipCode" size="40"></td>
    </tr>
    <tr>
      <td>都道府県</td>
      <td><input type="text" name="AdministrativeDivisions" size="40"></td>
    </tr>
    <tr>
      <td>市町村郡</td>
      <td><input type="text" name="LocalAuthorityCounty" size="40"></td>
    </tr>
    <tr>
      <td>番地</td>
      <td><input type="text" name="HouseNumber" size="40"></td>
    </tr>
    <tr>
      <td>電話番号<span class="hissu">*</span></td>
      <td><input type="text" name="TelephoneNumber" size="40"></td>
    </tr>
    <tr>
      <td>メールアドレス<span class="hissu">*</span></td>
      <td><input type="text" name="EmailAddress" size="40"></td>
    </tr>
    <tr>
      <td>メールアドレス(確認)<span class="hissu">*</span></td>
      <td><input type="text" name="EmailAddressAff" size="40"></td>
    </tr>
    <tr>
      <td height="109">お問い合わせ内容<span class="hissu">*</span></td>
      <td><textarea name="ContentOfInquiry" rows="6" cols="70"></textarea></td>
    </tr>
  </table>
  <input type="submit" value="確認"><input type="reset" value="リセット">
 </form>
  <br>
  <a class="btn" href="http://hogehoge.jp/">TOPページ</a>
 </body>
</html>
--<問合せフォーム:request_oem.html>-- ここまで




--<cgiコード:request_oem_chk.cgi>-- ここから
#!/usr/local/bin/ruby -Ke
#=問合せフォームによるメール配信
#
# Authors::   Shingo Kintaka
# Version::   1.0 2010-01-02 S.Kintaka
# Copyright:: Copyright (C) S.Kintaka , 2006. All rights reserved.
# OEM/販売代理店向け問合せフォームの値を取得し、値をチェックした後に確認画面を表示する
#==開発履歴
# K20100119_00 新規開発

require "cgi"                                                   # CGIクラス
require "erb"                                                   # ERBクラス
require 'kconv'                                                 # 漢字コード変換用

ERRMSG_HTM = "./errormsg.html"
AFFIR_HTM = "./affirmation_oem.html"

#
#= お問い合わせフォームからパラメータを取得し確認テンプレート、またはエラーテンプレートを表示させる
#
class RequestOEMChk

  # アクセサメソッド
  attr_accessor :CompanyName                 # 貴社名
  attr_accessor :EnquiryClass                # お問合せ種別
  attr_accessor :PostName                    # 部署名
  attr_accessor :NameKana                    # お名前カナ
  attr_accessor :NameKanji                   # お名前漢字
  attr_accessor :TelephoneNumber             # 電話番号
  attr_accessor :EmailAddress                # E-Mailアドレス
  attr_accessor :EmailAddressAff             # E-Mailアドレス
  attr_accessor :ZipCode                     # 郵便番号
  attr_accessor :AdministrativeDivisions     # 都道府県
  attr_accessor :LocalAuthorityCounty        # 市町村郡
  attr_accessor :HouseNumber                 # 番地

  #
  #= initialize
  # イニシャライズ
  #== Input
  # Nothing
  #
  #== Result
  # Nothing
  def initialize
    @cgi_form = CGI.new
  end

  #
  # フォーム情報を取得する
  #  formタグに設定されているパラメータを取得する
  #== Input
  # Nothing
  #
  #== Result
  # Nothing
  def get_form
    @CompanyName              = @cgi_form["CompanyName"]               # 貴社名
    @EnquiryClass             = @cgi_form["EnquiryClass"]              # お問合せ種別
    @PostName                 = @cgi_form["PostName"]                  # 部署名
    @NameKana                 = @cgi_form["NameKana"]                  # お名前(カナ)
    @NameKanji                = @cgi_form["NameKanji"]                 # お名前(漢字)
    @TelephoneNumber          = @cgi_form["TelephoneNumber"]           # 電話番号
    @EmailAddress             = @cgi_form["EmailAddress"]              # E-Mailアドレス
    @EmailAddressAff          = @cgi_form["EmailAddressAff"]           # E-Mailアドレス(確認用)
    @ZipCode                  = @cgi_form["ZipCode"]                   # 郵便番号
    @AdministrativeDivisions  = @cgi_form["AdministrativeDivisions"]   # 都道府県
    @LocalAuthorityCounty     = @cgi_form["LocalAuthorityCounty"]      # 市町村郡
    @HouseNumber              = @cgi_form["HouseNumber"]               # 番地
  end

  #
  #= 入力データのチェック
  # 必須項目の入力データをチェック(入力の有無)
  # メールアドレスに関しては、正規表現を利用してメールアドレスの構成と確認用に入力した内容と同一であるかをチェックする
  #== Input
  # Nothing
  #
  #== Result
  # error_message : エラーメッセージ文字列
  def data_check
   
    error_message = "" # エラーメッセージの初期値は""とする
   
    # 貴社名が入力されているかチェック
    if "" == @CompanyName then
      error_message << "貴社名を入力してください<br>"
    end

    # お問い合わせ種別の選択が正しく行われているか
    if "" == @EnquiryClass then
      error_message << "お問合せ種別を選択してください<br>"
    end
   
    # 部署名が入力されているかチェック
    if "" == @PostName then
      error_message << "部署名を入力してください<br>"
    end
   
    # お名前(カナ)にデータが入力されているかチェック
    if "" == @NameKana then
      error_message << "お名前(カナ)を入力してください<br>"
    end

    # お名前(漢字)にデータが入力されているかチェック
    if "" == @NameKanji then
      error_message << "お名前(漢字)を入力してください<br>"
    end
   
    if "" == @TelephoneNumber then
      error_message << "電話番号を入力してください<br>"
    end
   
    # メールアドレスが同じ内容でなければエラーとする
    if @EmailAddress != @EmailAddressAff then
      error_message << "入力されたメールアドレスと確認入力欄のメールアドレスの内容が異なります<br>"
    end
   
    # メールアドレスが正しく入力されているかチェック
    if false ==  mail_check(@EmailAddress) then
      error_message << "有効なメールアドレスを入力してください<br>"
    end
   
    # エラーが無ければ戻り値は"OK"とする
    if error_message == "" then
      error_message = "OK"
    end

    return error_message
  end
 
 
  #
  #= 確認画面を表示する
  # ERBを利用してテンプレート(htmlファイル)を読み込み確認画面を表示する
  #== Input
  # Nothing
  #
  #== Result
  # Nothing
  def show_inquiry
    begin
      templates = ERB.new(File.read(AFFIR_HTM),nil,"-")
      templates.run
    rescue
      error_cgi
    end
  end
 
  #
  #= 入力値エラー画面を表示する
  # ERBを利用してテンプレート(htmlファイル)を読み込みエラー画面を表示する
  #== Input
  # err_msg : エラーメッセージ
  #
  #== Result
  # Nothing
  def show_errorhtm(err_msg)
    templates = ERB.new(File.read(ERRMSG_HTM),nil,"-")
    templates.run
  end

  #
  #= メールアドレスのエラーチェック
  # 正規表現によるメールアドレスの書式チェック
  #== Input
  # Nothing
  #
  #== Result
  # Nothing
  def mail_check(mail_addr)
    if mail_addr.downcase.scan(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/).length > 0
      return true
    else
      return false
    end
  end
 
  #
  #= エラー処理
  #== Input
  # Nothing
  #
  #== Result
  # Nothing
  def error_cgi
    print "*** CGI Error List ***<br>"
    print "#{CGI.escapeHTML($!.inspect)}<br>"
    $@.each { |x|
      print CGI.escapeHTML(x), "<br>"
    }
  end
 
end

#
#= MainProgram
#  メインプログラム
#== Input
# Nothing
#
#== Result
# Nothing
if __FILE__ == $0
  begin
    req_oem_chk = RequestOEMChk.new                     # オブジェクト生成
    req_oem_chk.get_form                                # 入力データを取得する
    err_msg = req_oem_chk.data_check                    # 獲得したデータをチェックする
    case err_msg                                        # データチェック
    when "OK"                                           # OK
      req_oem_chk.show_inquiry                            # 確認画面表示
    else                                                # NG
      req_oem_chk.show_errorhtm(err_msg)                  # エラー画面表示
    end
  rescue
    req_oem_chk.error_cgi                               # エラー処理
  end
end

--<cgiコード>-- ここまで




--<テンプレート 確認画面:affirmation_oem.html>-- ここから
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp" />
<link rel="stylesheet" type="text/css" href="../css/request_oem.css" media="all">
<title>販売代理店・OEM お問い合わせフォーム</title>
</head>
<body>
<h1>販売代理店/OEM お客様お問合せ内容の確認</h1>
<p>
<h2>入力内容の確認</h2>
<form action="http://foobar/cgi/request_oem_mail.cgi" method="post">
<table border="1">
  <tr>
    <td width="200">貴社名<span class="hissu">*</span></td>
    <td width="356"><input type="text" name="CompanyName" size="40" style="display:none" value="<%= @CompanyName %>"><%= @CompanyName %></td>
  </tr>
  <tr>
    <td>お問合せ種別<span class="hissu">*</span></td>
    <td><select name="EnquiryClass" style="display:none">
        <option value="SalesAgent">販売代理店</option>
        <option value="OEM">OEM開発</option>
        <option value="Other">その他</option>
        <option selected="selected"><%= @EnquiryClass  %></option>
      </select>
      <%= @EnquiryClass  %></td>
  </tr>
  <tr>
    <td>部署名<span class="hissu">*</span></td>
    <td><td><input type="text" name="PostName" size="40" style="display:none" value="<%= @PostName %>"><%= @PostName %></td>
  </tr>
  <tr>
    <td>お名前(カナ)<span class="hissu">*</span></td>
    <td><input type="text" name="NameKana" size="40" style="display:none" value="<%= @NameKana %>"><%= @NameKana %></td>
  </tr>
  <tr>
    <td>お名前(漢字)<span class="hissu">*</span></td>
    <td><input type="text" name="NameKanji" size="40" style="display:none" value="<%= @NameKanji %>"><%= @NameKanji %></td>
  </tr>
  <tr>
    <td>郵便番号</td>
    <td><input type="text" name="ZipCode" size="40" style="display:none" value="<%= @ZipCode %>"><%= @ZipCode %></td>
  </tr>
  <tr>
    <td>都道府県</td>
    <td><input type="text" name="AdministrativeDivisions" size="40" style="display:none" value="<%= @AdministrativeDivisions %>"><%= @AdministrativeDivisions %></td>
  </tr>
  <tr>
    <td>市町村郡</td>
    <td><input type="text" name="LocalAuthorityCounty" size="40" style="display:none" value="<%= @LocalAuthorityCounty %>"><%= @LocalAuthorityCounty %></td>
  </tr>
  <tr>
    <td>番地</td>
    <td><input type="text" name="HouseNumber" size="40" style="display:none" value="<%= @HouseNumber %>"><%= @HouseNumber %></td>
  </tr>
  <tr>
    <td>電話番号<span class="hissu">*</span></td>
    <td><input type="text" name="TelephoneNumber" size="40" style="display:none" value="<%= @TelephoneNumber %>"><%= @TelephoneNumber %></td>
  </tr>
  <tr>
    <td>メールアドレス<span class="hissu">*</span></td>
    <td><input type="text" name="EmailAddress" size="40" style="display:none" value="<%= @EmailAddress %>"><%= @EmailAddress %></td>
  </tr>
  <tr>
    <td height="109">お問い合わせ内容<span class="hissu">*</span></td>
    <td><input type="text" name="EmailAddressAff" size="40" style="display:none" value="<%= @ContentOfInquiry %>"><%= @ContentOfInquiry %></td>
  </tr>
</table>
  <input type="submit" value="送信">
 </form>
 <br>
<table border="0" cellspacing="0" cellpadding="0" width="200">
  <tr>
    <td><a class="btn" href="javascript:history.back();">再編集</a></td>
    <td><a class="btn" href="http://hogehoge.jp/">TOPページ</a></td>
  </tr>
</table>
</body>
</html>
--<テンプレート 確認画面>-- ここまで




--<テンプレート エラー画面:errormsg.html>-- ここから
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>販売代理店・OEM お問い合わせフォーム</title>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp" />
<link rel="stylesheet" type="text/css" href="../css/request_oem.css" media="all">
</head>
<body>
<h1>販売代理店/OEM 入力エラー</h1>
<p>
<h2>不正なデータが入力されたか、必須項目にデータが入力されておりません</h2>
<br>
<p> <span class="hissu"><%= err_msg %></span><br>
  該当する入力エラーを修正してください。<br>
  <br>
  必須項目は全て入力しなければなりません。<br>
  メールアドレスはドメインネームまで正しく入力されていなければなりません。<br>
  <br>
</p>
<table border="0" cellspacing="0" cellpadding="0" width="200">
  <tr>
    <td><a class="btn" href="javascript:history.back();">再編集</a></td>
    <td><a class="btn" href="http://hogehoge.jp/">TOPページ</a></td>
  </tr>
</table>
</body>
</html>
--<テンプレート エラー画面:errormsg.html>-- ここまで

Reply | Threaded
Open this post in threaded view
|

[ruby-list:46798] Re: ERBを利用した問合せメールシステム開発

okkez
okkez です。

2010年1月21日0:31 ShingoKintaka <[hidden email]>:
> Shingo Kintakaです。
>
> 現在、会社等で利用される事の多い「問合せフォームCGI」を作成しております。
> 実行環境は@niftyが提供するLacoocanサービス。

http://lacoocan.nifty.com/service/outline.htm
によると Ruby1.8.6 ですね。

長すぎて引用も面倒なのでざっと読んで気付いた点をいくつか。

* コメントが多すぎやしませんか?
変数名やメソッド名が十分にわかりやすい場合はコメントを書かない方がコードが読みやすく
なると思います。RDocとかでドキュメントを書いているならそれはそれで構わないんですが、
コメントの内容はもう少し吟味した方がよろしいかと。

* rescue で全ての例外を捕捉できるわけではありません
resucue 節は捕捉する例外を省略すると StandardError (とそのサブクラス)を捕捉します。
例外クラスについて調べてみてください。

* attr_accessor の引数が PascalCase で気持ち悪い
Ruby ではメソッド名は単語の区切りをアンダーバーにするスタイルが好まれています。
クラス名やモジュール名は PascalCase です。

* ERB#run で binding を渡していないためインスタンス変数が参照できていない
Kernel.#binding というメソッドでそのコンテキストのバインディングを取得できます。

class Foo
  def output
    @bar = 'barbar'
    ERB.new("path_to_erb_template", nil, "-").run(binding)
  end
end

みたいな感じです。
この辺はリファレンスマニュアルやるびまをちゃんと読むと書いてあるかもしれません。
http://doc.okkez.net/186/view/class/ERB

CGI にしなくてもローカルで動作確認は出来るはずなので確認してみてください。

* 発生したエラーを標準出力に出力している
logger などを使用してログファイルに出力した方がいいと思います。
また、CGIでよくわからないエラーが出たときは Apache などの HTTP サーバのエラーログを読むと
何かヒントが書いてあるかもしれません。

* 例のスクリプトで hogehoge.jp というドメインが使用されている
example.com や example.jp を使用してください。
例示用の URL では hogehoge.jp とか誰かが所有しているようなドメインは使わないのが無難です。
# 確かもっと強い調子で書かれてる文書がどっかにあったんだけど失念しました

* テンプレート内の <%= ... %> でユーザの入力をそのまま出力している
ユーザの入力は信用してはいけません。ちゃんとエスケープしてください。
ちなみにこの実装には致命的な脆弱性があります。XSS について調べてみてください。

* たまに変数名やメソッド名が微妙に省略されている
変な省略をするよりは完全な名前を書いた方が良いです。

* そもそも素の CGI はやめた方がいいんじゃないでしょうか
現在では Ruby on Rails 以外でも軽量の web アプリケーションフレームワークがいくつかあります。
なので、それらを学んだ方が安全なアプリケーションを手軽に作成出来るのではないでしょうか。
Rack, Sinatra, Ramaze などなど。Lacoocanで使用するのは難しいかもしれませんが。


とりあえず Ruby に関係ありそうな部分はこんなもんだと思います。
# 一部関係ないのもありますが。
HTML や Javascript にもツッコミどころがありますが、それについては自分で調べてください。
主に規格やブラウザごとの挙動の違いを調べるといいと思います。


--
okkez
[hidden email]