🕵️‍♀️

Webエンジニア初心者を抜けるための3つのバリデーション

2024/02/15に公開

ラブグラフのエンジニア兼CTO、横江 (@yokoe24) です。
この間インターンのかたに説明する機会があったので、こちらのブログでも。

3つのバリデーション

サーバーサイドアプリケーションの制作にあたっては
『3つのバリデーション』が必要になります。
バリデーション とは、入力された値をチェックする仕組みのことです。

データベースに変な値が保存されてアプリケーションが変な動きをしないために、
バリデーションはとても大事なものです。

そしてバリデーションは大きく3つ、

    1. フロントエンド のバリデーション
    1. サーバーサイド のバリデーション
    1. データベース のバリデーション

に分けられます。順番に見ていきましょう!

1. フロントエンドのバリデーション

フロントエンドのバリデーションは、
スマホアプリだったらスマホアプリ側の実装となりますが、
今回はWebアプリケーションを想定して、「HTMLバリデーション」と「JavaScriptバリデーション」について話します。

HTMLバリデーション

HTMLバリデーションは、例えば <input> タグがわかりやすいです。
inputタグの属性はいろいろありますが、その中でも

  • required: required="required" のとき、なんらかの入力を必須とし、入力欄が空のままだといけなくする
  • maxlength: 入力できる文字数の長さを制限する( <input type="text"> などで有効)
  • max, min: 入力できる値の最大値・最小値を指定する( <input type="number"> などで有効)

などは使う機会が多いと思います。

例えば required を設定すると、なにも記入せず送信ボタンを押すと、
以下のように表示が出て次に進むことができません。

JavaScriptバリデーション

スクショからだとわかりづらいのですが、
例えば以下は Vue.js で、Eメールの入力がない限り、
送信ボタンが disabled 状態になるようにしたものです。


https://jsfiddle.net/nekonenene/x7wjntpz/53

フロントエンドバリデーションの問題点

これらのフロントエンドバリデーションは、
ユーザー補助の観点ではいいのですが、Webアプリケーションを安全に運用するためには不完全です。

Chrome などウェブブラウザ側のデベロッパーコンソールで
HTML を書き換えて、 requireddisabled などの条件を消してしまうことが簡単にでき、
フロントエンドバリデーションをなくした状態で送信できてしまいます。


ここを削除ボタンで消せば、


なかったことに!!!

JavaScript 側で入力内容をチェックして、
JavaScript 側で API リクエストを送るなどは、これよりは複雑でマシですが、
いちおう、JavaScript 側のコードを読めばバリデーションを無視したデータをサーバーに送りつけることが可能です。

2. サーバーサイドのバリデーション

フロントエンドバリデーションが突破されてしまったときに大事なのが、
サーバー側のバリデーションです!
ここがとっても大事です!

例えば Rails には、以下の Rails ガイドのようにモデルに様々なバリデーションを設定できます。

https://railsguides.jp/active_record_validations.html

class Message < ApplicationRecord
  validates :body, presence: true, length: { maximum: 200 }
end

のように書くことで、 Messagebody の値として
空文字 " や、200文字より多いテキストを入れて保存しようとしたときにエラーを吐いてくれます。

コントローラー側では以下のように書いて、
body に問題があることをユーザー側に表示させるなどをおこなうことでしょう。

def create
  @message = Message.new(message_params)

  if @message.save
    redirect_to messages_path, notice: "メッセージを作成しました"
  else
    redirect_to new_message_path(@message), alert: @message.errors.full_messages.join(", ")
  end
end

private

def message_params
  params.require(:message).permit(:body)
end

また、モデル側に条件があるのではなく、
コントローラー側で保存しようとする内容や、保存しようとしているユーザーの権限などをチェックし、
問題があればリダイレクトするなども、サーバー側のバリデーションと言えるでしょう。

このようなサーバーバリデーションによって、
望ましくないデータが保存されることを防ぐことはとても大切です!

エンジニア初心者〜中級者にかけて、 1. のフロントエンドバリデーションの実装で満足してしまって
サーバーサイドのバリデーションをかけていないということはよくあるので、
フロントエンドバリデーションをかけた際には 「これに対するサーバーサイドバリデーションは書けていたっけな?」 と、セットのように考えることをオススメします😉

3. データベースのバリデーション

最後の砦、データベースのバリデーションです。
これは NOT NULL 制約や UNIQUE (ユニーク)制約などがよく使われると思います。

例えば Rails で以下のように create_table した場合を考えます。

create_table :products do |t|
  t.string :uuid, null: false
  t.string :name, null: false, default: ""
  t.integer :price, null: false
  t.text :description

  t.timestamps

  t.index :uuid, unique: true
  t.index :price
end

こうすると、 price カラムを未定義、つまり NULL の状態でデータを入れようとすると
データベースはエラーを吐き、 INSERT や UPDATE がおこなわれることを防いでくれます。

また、 uuid カラムにはユニークインデックスが貼られているので、
すでに登録されているデータと同じ文字列の uuid を設定しようとした際もエラーを吐いてくれます。

このように、想定されていないデータをデータベース側で弾くことが可能です。

ただし、 できるだけサーバーバリデーションで防いでおいて、データベースバリデーションが実行されてDBからサーバーにエラーが返されるというのは防ぎたいものです。
けっこうDBエラーの例外処理って抜けていて 500 エラーになりがちですしね。

データベースバリデーションは、やむをえぬ事情でサーバーを介さずに
DBに直接 INSERT や UPDATE のクエリを流さないといけないときに
変なデータが入ることを防ぐためにも役立ってくれることでしょう。
(そういうこともないといいですが……)

おわりに

以上、Webアプリケーション制作に必要な3つのバリデーションについてまとめました。

特に サーバーサイドのバリデーションが超大事! ってことが伝わりましたら幸いです!

ラブグラフのエンジニアブログ

Discussion