Build Web Application With Golang を試してやってみる 13

はじめに

前回の記事の続きです。
前回の記事はこちら

フォームに入力された内容の検証

前回は、ついにフォームからのリクエストをサーバ側で受け取って出力するというところまでやりました。
受け取ったリクエストをサーバ側で検証して行う実装をします。

Webアプリケーションを書く時は主に2つの場所でデータ検証を行います。
ひとつはページ上でのJavaScriptによる検証で(現在この方面では多くのプラグインがあります。
例えばValidationJSプラグインなどがそうです)、もうひとつはサーバ側での検証です。
この節ではどのようにサーバでの検証を行うか解説します。

ということですね、セキュリティ上、クライアント側のみの検証ですと問題がありますのでサーバ側での検証を行います。

必須フィールド

あるフィールド(項目)に必須チェックをつけたい場合の実装ですね。
len 関数を使って文字列の長さが 0 の場合にエラーにするようです。
早速実装してみましょう。
前回のプログラムに関数を追加します。

func validate(form url.Values) (errors []string) {
	const requiredErr = "%sは必須です。"
	if len(form["username"][0]) == 0 {
		errors = append(errors, fmt.Sprintf(requiredErr, "ユーザ名"))
	}
	return
}

また、usernamePrintln している else の一番最後に 下記を入れてみます。

// バリデーションしてみる
errors := validate(r.Form)
fmt.Println(errors)

さて、これで実行した後にユーザ名を空にして送信すると必須エラーが出るはずです。
見てましょう。

web-server-01

r.Formは異なる型のフォーム要素の空白に対して異なる処理を行います。
空のテキストフィールド、テキストエリアおよびファイルアップロードに対して、その要素の値を空にします。
また選択されていないコンボボックスやセレクトボックスr.Formの中にはそもそもその項目を作りません。
上の例の中の方法でデータを取得した時プログラムはエラーを発生させます。
そのため、r.Form.Get()を使って値を取る必要があります。
なぜなら、もしフィールドが存在しなかった場合、この方法で取得すると空の値を得るからです。
ですが、r.Form.Get()は単体の値しか得ることができません。
もしmapの値であれば必ず上の方法で得る必要があります。

JavaScript とかで動的にフォームを変えた場合とかに、フォームの値があるかないかを検証する場合は、r.Form.Get() を使う必要がありそうですね。

いわゆる 数値チェック ですね。
strconv.Atoi で判定するようです。 これも実装してみましょう。

login.gtpl に下記を追加します。

年齢:<input type="text" name="age">

そして魔改造したvalidate 関数です。

func validate(form url.Values) (errors []string) {
	const requiredErr = "%sは必須です。"
	const numErr = "%sは数字で入力してください。"
	const rangeErr = "%sは0~99の値で入力してください。"
	if len(form["username"][0]) == 0 {
		errors = append(errors, fmt.Sprintf(requiredErr, "ユーザ名"))
	}
	if form.Get("age") != "" {
		getint, err := strconv.Atoi(form.Get("age"))
		if err != nil {
			errors = append(errors, fmt.Sprintf(numErr, "年齢"))
		} else {
			if getint < 0 || getint > 99 {
				errors = append(errors, fmt.Sprintf(rangeErr, "年齢"))
			}
		}
	}
	return
}

これで実行してみましょう。
0 → 100 → aaa で入力してみました。
下記のようになりました。うまく効いているようですね。

$ go run mathapp/main.go 
method: GET
method: POST
username: [user]       
password: [pass]       
age:  [0]
GET query string /login
[]
method: POST
username: [user]
password: []
age:  [101]
GET query string /login
[年齢は0~99の値で入力してください。]
method: POST
username: [user]
password: []    
age:  [aaaa]
GET query string /login
[年齢は数字で入力してください。]

また、 正規表現を使った数字のチェックもあります。 たぶんこちらのほうがスマートですね。
ただ、範囲チェックする場合は後で数字を使いますので、strconv のほうが良さそうですね。

中国語

英語

こちらは正規表現を使って実装していますね。
実装自体は先程と同じ手順ですね、 validate メソッドに追加してフィールドをチェックします。
さらっといきましょう。

メールアドレス

下記のような正規表現でチェックできるらしいです。
PHP フレームワークの Laravel とかだと DNS チェックまでするやつがあったような気がします。
フレームワークレベルになると結構ガチガチのチェックで、要望でもっとゆるくしてほしいとか言われることもありますね。
そういうときは、下記のように正規表現をゴニョゴニョしています。

if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m {
    fmt.Println("no")
}else{
    fmt.Println("yes")
}

携帯電話番号

こちらも正規表現ですね。
これはハイフン無しの場合ですね(0000000000)みたいに続けて打つタイプです。
これも実務だとハイフンありなしで変わってくるので、正規表現をゴニョるしかないですね。
海外の番号とか日本の番号とかで形式も変わってくるので、作るシステムが海外でも使用するようであればちゃんと調べないといけませんね。

if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
    return false
}

プルダウンメニュー

フォームの中の