シナジーマーケティング株式会社

Menu

Salesforce用のメールアドレス入力チェックを行うJavaScriptを作りました
(Salesforce Email Address Validation)

SFBD室の秋月です。Webフォームでよく使われるメールアドレス項目ですが、この入力チェックの実装にかなり苦労されておられる方は多いのではないでしょうか?

今回はSalesforceにデータを登録する際の、簡単、シンプルなメールアドレス入力チェックを行うJavaScriptを作りましたのでご紹介します。その名も…

Salesforce Email Address Validation

安易なネーミング…という突っ込みはおいておいて。本題にはいりましょう。
(このJavaScriptは弊社のBtoBマーケター向けのメルマガ「BtoB Marketing Journal」の登録フォームでも利用しています。)

前提

Salesforceのメールアドレスの形式RFC 2822を基本としています。IETF(インターネット関連技術の標準化団体)が定める最新のメールアドレスの形式はRFC 5321またはRFC 5322となりますので、Salesforceはやや古いものを利用しています。で、「仕様があるなら簡単にプログラム作れるでしょ?」とお考えの方もおられるかもしれませんが、メールアドレスの入力チェックは非常に奥深いものがあります。それは、単純にRFC 2822に完全準拠する処理を作ること自体大変手間がかかるのですが、それに加え

  • RFC 2822に準拠していても実際には配信できないものがある
  • RFC 2822に準拠していなくても実際には配信できるものがある
  • RFC 2822に規定はされているが一般的には利用しないものがあり、そういった一般的に利用されないメールアドレスは単純に入力ミスである可能性がある
  • Salesforce自身RFC 2822に準拠している、と名言していますが、実は完全に準拠しているわけでなく独自のチェックが入っている

といったことが挙げられますので、RFC 2822に完全準拠するといいのか?というとそういうわけでもありません。これらを踏まえると、「精度の高い、程よいチェック」を行う処理が望ましいように思います。

仕様

ここでご紹介する「精度の高い、程よいチェック」を行うメールアドレスチェックの仕様は以下のとおりです。

RFC 2822カスタマイズ

まず、RFC 2822で一般的に利用するものに限定します

  • addr-specを対象とする
    → GroupA:hoge <hoge@example.com>,fuga <fuga@example.com>; といった形式はエラーにします。
  • domain-literalは対象外とする
    → hoge@[192.0.2.1] といった、ドメインが[]で囲まれた表現はエラーにします。
  • 廃止された文法(Obsolete Syntax)は対象外とする
  • 折り返された空白文字(FWS)は対象外とする
    → スペース、水平タブ、改行コードが含まれている場合はエラーにします。
  • ()で囲まれたコメント(comment)は対象外とする
    → hoge(huga)@example.com といった表現はエラーにします。
    「BtoCだとメールアドレスに顔文字いれる方もおられるのですがはいりますか?」というお声をいただきまして、「(」と「)」で囲まれたコメント(comment)はSalesforceの独自ルールにあわせつつ対応することにしました。
    → (^^)/@example.com といったケースです。

Salesforce独自ルール

次に標準のRFC-2822には規定のない、Salesforce独自ルールに対応します。現段階でわかっているものは以下の通りです。(Salesforceサポートに問い合わせをしましたが、不具合との回答でいつ修正されるかは未定です。)

  • 全文字数最大80桁
    → 80桁を越えた場合はエラーにします。
  • ドメインパートに「.」(ドット)が必須
    → 無い場合はエラーにします。
  • ローカルパートに連続した「.」(ドット)と@直前に「.」(ドット)が入力できる
    → hoge.@examplehoge..fuga@example.com 等はエラーにならないようにします。過去の経緯により、実はドコモやauでこういったメールアドレスが存在します。Salesforceでエラーにならないので非常にありがたいですね。
  • ドメインパートの「.」の前半、後半文字列それぞれの先頭、最後尾文字に「-」を指定することはできない
    → hoge@-example.comhoge@example-.comhoge@example.-comhoge@example.com- 等はエラーにします。実際こういったドメインをご利用されている方もおられますが、Salesforceの仕様なのでどうしようもありません…
  • quoted-stringで、qtextはatextに「.」(ドット)を加えたもののみ対応しており、quoted-pairは未対応
    → “hoge@fuga”@example.com“hoge\fuga”@example.com 等はエラーにします。

以下コメント対応分追加

  • 折り返し空白文字の内、改行コードは入力できない
    → 改行コードはエラーにします。
  • 入れ子となったコメントはエラーとなる
    → (hoge(fuga)piyo)foo@example.com 等はエラーにします。
  • コメント内の「)」はエラーとなり、またASCII以外の文字を許容する
    → (π≠™)hoge@example.com といったメールアドレスをSalesforceに登録できますが、ここは対象を狭めます。「)」と、水平タブ以外の制御文字を除いたASCII文字を登録可能な文字にします。
  • ローカルパート、ドメインパートの前後以外にもコメントが現れてもエラーとならない
    → ローカルパート中に現れるコメントはエラーとせず救済することにします。(ドメインパート中のコメントはエラーにします)
    エラーにしない : abc(hoge)def@example.com
    エラーにする : abcdef@exam(hoge)ple.com
  • @前後に現れるコメントの前後のホワイトスペースはエラーとなる
    → abc△(hoge)△@△(fuga)△example.com 等はエラーにします(以降△はホワイトスペースの意)。
  • Salesforce独自仕様のローカルパート、ドメインパートの前後以外に現れるコメントの前後のホワイトスペースもエラーとなる
    → a△(hoge)△bc@example.com 等はエラーにします。
    ただし、 ab(△hoge△)c@example.com といったコメント内部のホワイトスペースはエラーとなりません。

これはあくまで個人的な推測ですが、恐らくコメント部を取り除いた形でメールアドレスチェックを行っているのではないかと思われます。(実際にコメント付きでデータを登録してみると、コメントを含んだ状態でデータ登録されますが、画面表示はコメント部分を除外して表示されます)
例えば abc△(hoge)△@△(fuga)△example.com は abc△△@△△example.comでチェックしエラーとしているのではないか?ということです。

ABNF

以上より、ABNFで表してみます。(「全文字数最大80桁」についてはちょっと除外します。)

sfdc-addr-spec              =  sfdc-local-part "@" sfdc-domain

sfdc-local-part             =  sfdc-dot-atom / sfdc-quoted-string

sfdc-domain                 =  *sfdc-comment sfdc-domain-dot-atom-text [sfdc-CWSP]
                               ; sfdc-domain-dot-atom-text前のコメント前後にはホワイトスペースは現れない

sfdc-domain-dot-atom-text   =  1*sfdc-domain-atext *(1*"-" 1*sfdc-domain-atext) 1*("." 1*sfdc-domain-atext *(1*"-" 1*sfdc-domain-atext))
                               ; ドメインパートに1つ以上の.(ドット)が必要
                               ; ドメインパートの先頭・末尾とドット「.」の直前直後はハイフン「-」を入れてはいけない

sfdc-dot-atom               =  [sfdc-CWSP] sfdc-dot-atom-text *(sfdc-dot-atom-text / sfdc-comment)
                               ; sfdc-dot-atom-text後のコメント前後にはホワイトスペースが含まれない    

sfdc-dot-atom-text          =  1*atext *sfdc-datext
                               ; 連続したドット「.」とアットマーク「@」直前のドット「.」も可とする

sfdc-quoted-string          =  [sfdc-CWSP] DQUOTE *(sfdc-cdatext) DQUOTE *sfdc-comment
                               ; atextやドット「.」をダブルクォート「"」で括る
                               ; ダブルクオート「"」で括られた後(2つ目のダブルクォート後)に現れるコメント前後にはホワイトスペースが含まれない

sfdc-cdatext                =  sfdc-comment / sfdc-datext

sfdc-datext                 =  atext / "."

sfdc-CWSP                   =  *([sfdc-WSP] sfdc-comment) (([sfdc-WSP] sfdc-comment) / sfdc-WSP)

sfdc-comment                =  "(" *([sfdc-WSP] sfdc-ccontent) [sfdc-WSP] ")"
                               ; 改行は含めない
                               ; コメント内部に")"は現れない

sfdc-ccontent               =  %x09 / %x20-28 / %x2a-7e
                               ; ”)”と水平タブ以外の制御文字除いたASCII文字

sfdc-WSP                    =  1*WSP
                               ; SP / HTAB
                               ; white space

WSP                         =  SP / HTAB
                               ; white space

SP                          =  %x20

HTAB                        =  %x09
                               ; horizontal tab

atext                       =  ALPHA / DIGIT /
                               "!" / "#" /
                               "$" / "%" /
                               "&amp;" / "'" /
                               "*" / "+" /
                               "-" / "/" /
                               "=" / "?" /
                               "^" / "_" /
                               "`" / "{" /
                               "|" / "}" /
                               "~"

sfdc-domain-atext           =  ALPHA / DIGIT /
                               "!" / "#" /
                               "$" / "%" /
                               "&amp;" / "'" /
                               "*" / "+" /
                               "/" /
                               "=" / "?" /
                               "^" / "_" /
                               "`" / "{" /
                               "|" / "}" /
                               "~"
                               ; ドメインパートの先頭と.(ドット)直後は-(ハイフン)を入れてはいけない

DQUOTE                      =  %x22
                               ; " (Double Quote)

DIGIT                       =  %x30-39
                               ; "0" / "1" / "2" / "3" / "4" / "5" / "6" /
                               ; "7" / "8" / "9"

ALPHA                       =  %x41-5A / %x61-7A
                               ; A-Z / a-z

コメント対応版(前回から修正しています)

サンプルJavaScript

正規表現

ABNFから正規表現に変換すると以下のようになります。

^((?:(?:(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?(?:(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~])+(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.])*)(?:(?:(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~])+(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.])*)|(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*)|(?:(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?"(?:(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))|(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.]))*"(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))*))\@(?:(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))*(?:(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+(?:\-+(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+)*(?:\.(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+(?:\-+(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+)*)+)(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?))$

コメント対応版(前回から修正しています)

JavaScript

上記の正規表現による形式チェックに「全文字数最大80桁」チェックを付け加えたJavaScriptは以下の通りとなります。

if(email.length > 80) {
    alert("メールアドレスの桁数が80桁を超えています");
    return false;
}
if (!email.match(/^((?:(?:(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?(?:(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~])+(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.])*)(?:(?:(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~])+(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.])*)|(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*)|(?:(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?"(?:(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))|(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.]))*"(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))*))\@(?:(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))*(?:(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+(?:\-+(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+)*(?:\.(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+(?:\-+(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+)*)+)(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?))$/)) {
    alert("メールアドレスの入力が不正です");
    return false
}

コメント対応版(前回から修正しています)

サンプルHTML

上記のJavaScriptを利用した簡単なサンプルHTMLです。
submitをクリックした時に形式や桁数にエラーがある場合はダイアログが表示されます。(これを元にfocusが離れた時にチェックする…等、ご自由にどうぞ。)

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Salesforce Email Address Validation Sample</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript">
<!--
$(function(){
    $("form").submit(function(){
        var email = $("#emailAddressValidation").val();
        if(email.length > 80) {
            alert("メールアドレスの桁数が80桁を超えています");
            return false;
        }
        if (!email.match(/^((?:(?:(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?(?:(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~])+(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.])*)(?:(?:(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~])+(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.])*)|(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*)|(?:(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?"(?:(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))|(?:[A-Za-z0-9!#\$\%&amp;'*+\-/=?\^_`{|}~\.]))*"(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))*))\@(?:(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\))*(?:(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+(?:\-+(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+)*(?:\.(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+(?:\-+(?:[A-Za-z0-9!#\$\%&amp;'*+/=?\^_`{|}~])+)*)+)(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))*(?:(?:(?:(?:[\x20\x09])+)?(?:\((?:(?:(?:[\x20\x09])+)?(?:[\x09\x20-\x28\x2a-\x7e]))*(?:(?:[\x20\x09])+)?\)))|(?:(?:[\x20\x09])+)))?))$/)) {
            alert("メールアドレスの入力が不正です");
            return false
        }
        return true;
    });
});
-->
</script>
</head>
<body>
<form method="post" action="" >
<dl>
<dt>メール</dt>
<dd><input type="text" id="emailAddressValidation" /></dd>
</dl>
<input type="submit">
</form>
</body>
</html>

コメント対応版(前回から修正しています)

※記載されている内容は掲載当時のものであり、一部現状とは内容が異なる場合があります。ご了承ください。

PageTop