SlideShare a Scribd company logo
confidential
WebAPIのバリデーションを、型の力でいい感じにする

Web API LT会 - vol.3
takuya kikuchi / Showcase Gig
confidential
©Showcase Gig
自己紹介

● takuya kikuchi
● twitter: @_pochi
● Engineer Group Manager @ Showcase Gig
● モバイルオーダープラットフォームを作っています
● たまに実店舗も作ります
confidential
©Showcase Gig
APIが受け取るパラメータのバリデーション

ちゃんとできてますか?
SDK, OpenAPI, gRPC, etc…
「そんな入力予期してなかった・・・」
「そんな使い方しないで・・・」
confidential
©Showcase Gig
素直なアプローチ紹介

gRPCの例
protoc-gen-validate を使ってバリデータの自動生成ができるよ 🐔
参考:
【Go】gRPCのリクエストバリデータを自動生成する
https://note.com/scg_tech/n/nb12a33bfd391
import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto";
~~~~~~~~~~~
service TestServer {
rpc Test(TestMessage) returns (Result) {}
}
~~~~~~~~~~~
message TestMessage {
// 0~100間の整数
int32 seisuu = 1 [(validate.rules).int32 = {gte:0, lt: 100}];
// floatで0~1の値
double fudou = 2 [(validate.rules).double = {gte: 0, lte: 1}];
// アルファベットと数値で、5〜30文字のrepeated
repeated string mojiretsu = 3 [(validate.rules).repeated.items.string = {pattern:
"^[a-z0-9]{5,30}$", min_len: 5, max_len:30}];
// RFC 1034で解釈可能なメールアドレス
string mail_address = 4 [(validate.rules).string.email = true];
}
confidential
©Showcase Gig
型の話をします

confidential
©Showcase Gig
型はいいぞ

● 静的型付け言語では、コンパイル時に型チェックをしてくれる
● 型の明らかな渡し間違いはすぐ気づくことができる
import "fmt"
func main() {
var intValue int = 12345
printString (intValue)
}
func printString (str string) {
fmt.Printf("%sn", str)
}
import "fmt"
func main() {
var str string = "文字列だよ"
printString (str)
}
func printString (str string) {
fmt.Printf("%sn", str)
}
コンパイルOK コンパイルエラー
confidential
©Showcase Gig
しかし基本データ型には限界がある

● メールアドレスのみを受け入れたいのだけど、コンパイラさん気づいてくれない
import "fmt"
func main() {
var mailAddress string = "メアドではないよ"
printMailAddress
(mailAddress)
}
func printMailAddress
(mailAddress string) {
fmt.Printf("%sn", mailAddress)
}
import "fmt"
func main() {
var mailAddress string = "hoge@example.com"
printMailAddress
(mailAddress)
}
func printMailAddress
(mailAddress string) {
fmt.Printf("%sn", mailAddress)
}
コンパイルOK 🤗 コンパイルOK 😢
confidential
©Showcase Gig
しかし基本データ型には限界がある

● そもそもユーザーから任意の値が入力されるものはどうしようもない
import "fmt"
func main() {
var str string
// ユーザー入力を受け取る
_, err := fmt.Scan(&str)
if err != nil {
// 読み取り失敗
return
}
printMailAddress(str)
}
コンパイルOK。ただしメアド以外も printできてしまう。
confidential
©Showcase Gig
さてどうする

confidential
©Showcase Gig
「メールアドレス」型を作ろう

● 正しいメアド入力以外はオブジェクトを生成できないようにする
type MailAddress string
func NewMailAddress (mailAddress string) (MailAddress , error) {
if !isValidMailAddress (mailAddress) {
return "", errors.New("メアドではないので NG")
}
return MailAddress (mailAddress) , nil
}
func printMailAddress(mailAddress MailAddress) {
fmt.Printf("%sn", mailAddress)
}
型とコンストラクタを定義。コンストラクタで、「正しいメールアド
レスかどうか」をチェックする
printMailAddressメソッドの引数も、その型を受け取るようにする
confidential
©Showcase Gig
「メールアドレス」型を活用する

● コンパイル時チェックはできないが、「メールアドレスじゃない文字列が渡された場合」のエラーハンドリング実装が強制さ
れる
○ なので、「うっかりメアドじゃない文字列が渡されてクラッシュ!」みたいなことが回避できる
func main() {
var str string
// ユーザー入力を受け取る
_, err := fmt.Scan(&str)
if err != nil {
// 読み取り失敗
return
}
// 入力された文字列から、
MailAddressオブジェクト生成
mailAddress, err := NewMailAddress
(str)
if err != nil {
// メールアドレスではない文字列が渡された
return
}
// MailAddress
を出力する
printMailAddress
(mailAddress)
}
confidential
©Showcase Gig
本題。WebAPIの話

confidential
©Showcase Gig
メールアドレスを登録するWebAPI

● メールアドレスを受け取ってDBに記録するエンドポイントのコントローラをイメージしてください
● MailAddressRepositoryは単にSQLを呼ぶだけの実装だと思ってください
// メールアドレスを登録するエンドポイントの実装
func updateMailAddressController
(userID int, mailAddress string) error {
// DBアクセス用のリポジトリ取得
repository := newMailAddressRepository
()
// DBに書き込み
err := repository.
Update(userID, mailAddress)
if err != nil {
// DB書き込みエラー
return errors.New("internal error"
)
}
// 正常終了
return nil
}
type MailAddressRepository interface {
Update(userID int, mailAddress string) error
}
confidential
©Showcase Gig
問題点

● mailAddress のバリデーションをしていない。
○ 渡されてくるmailAddress次第でいろんなエラーが起きてしまう
■ メアドじゃなくても登録できる(それはダメ!)
■ 文字列が長すぎたらDBへのクエリでエラーが起きて internal error (カッコ悪い!)
● メアドじゃない文字列が渡されたらクライアントエラーにしてあげたい
// メールアドレスを登録するエンドポイントの実装
func updateMailAddressController
(userID int, mailAddress string) error {
// DBアクセス用のリポジトリ取得
repository := newMailAddressRepository
()
// DBに書き込み
err := repository.
Update(userID, mailAddress)
if err != nil {
// DB書き込みエラー
return errors.New("internal error"
)
}
// 正常終了
return nil
}
confidential
©Showcase Gig
先程作ったメールアドレス型を使います

● MailAddressRepositoryの引数を、string から MailAddress型に変更
type MailAddressRepository interface {
Update(userID int, mailAddress MailAddress) error
}
type MailAddressRepository interface {
Update(userID int, mailAddress string) error
}
confidential
©Showcase Gig
するとどうなるか

● 元のコードに戻ります。
● mailAddressが string なのでコンパイルエラーになります
// メールアドレスを登録するエンドポイントの実装
func updateMailAddressController
(userID int, mailAddress string) error {
// DBアクセス用のリポジトリ
repository := newMailAddressRepository
()
// DBに書き込み
err := repository.
Update(userID, mailAddress)
if err != nil {
// DB書き込みエラー
return errors.New("internal error"
)
}
// 正常終了
return nil
}
confidential
©Showcase Gig
あとはコンパイルエラーを解消する

● コンパイルエラーを解消するために、引数で渡された文字列をMailAddress型に変換します
● その際にエラーハンドリングがごく自然に実装されます
// メールアドレスを登録するエンドポイントの実装
func updateMailAddressController
(userID int, mailAddressStr string) error {
// DBアクセス用のリポジトリ
repository := newMailAddressRepository
()
mailAddress, err := NewMailAddress(mailAddressStr)
if err != nil {
// 渡された文字列がメールアドレスではない
return errors.New("client error")
}
// DBに書き込み
err = repository.
Update(userID, mailAddress)
if err != nil {
// DB書き込みエラー
return errors.New("internal error"
)
}
// 正常終了
return nil
}
confidential
©Showcase Gig
まとめ

リクエストパラメータをバリデーションしたいあなたへ
→ バリデーションしたいということは、その値には何らかのルールがある
→ そのルールを表現する、専用の型を用意してみよう
→ 専用の型の利用を強制しよう
→ すると、パラメータのバリデーションは自然に実装される
confidential
©Showcase Gig
おしまい

confidential
©Showcase Gig
宣伝

● APIが好きな人
● 型が好きな人
● Goが好きな人
● gRPCが好きな人
● エンジニアリングが好きな人
● POSが好きな人
● 次世代店舗を作りたい人
https://www.showcase-gig.com/recruit/engineer/

More Related Content

PDF
Elixir Meetup #1 Loggerの構造と拡張
PDF
Ruby風Swift NSOperation編
PDF
20150908 ”時間の流れ” という無限リストを扱うAWS Lambda
PDF
AWSをコードで定義する
PDF
API Gatewayで re:Inventのセッション探し
PDF
ADO.NETとORMとMicro-ORM -dapper dot netを使ってみた
PDF
Visual studio 14 CTP2 概要
PDF
React Native GUIDE
Elixir Meetup #1 Loggerの構造と拡張
Ruby風Swift NSOperation編
20150908 ”時間の流れ” という無限リストを扱うAWS Lambda
AWSをコードで定義する
API Gatewayで re:Inventのセッション探し
ADO.NETとORMとMicro-ORM -dapper dot netを使ってみた
Visual studio 14 CTP2 概要
React Native GUIDE

What's hot (20)

PDF
20140930 anything as_code
PPT
CGI::Application::Dispatch
PDF
認証機能で学ぶ Laravel 5 アプリケーション
PDF
PHPという概念が存在しない退屈な世界 - AWS LambdaでWebAPP編
PDF
Phpでrest apiを作った話
PDF
私のチームのリーダブルコード
PDF
PHPコードではなく PHPコードの「書き方」を知る
PDF
勉強会20140207
PDF
serverless framework + AWS Lambda with Python
PDF
もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境
KEY
CMS for Cloud by Ruby
PDF
Ansible 2.0 のサマライズとこれから
PDF
Heroku+MongoLabでダミーサーバー
PDF
Scala + Finagleの魅力
PDF
React.jsでサービスを作ってみた話
PDF
Vagrant で PHP 開発環境を作る ハンズオン
PDF
Ansible ではじめるサーバ作業の自動化
PPT
Scala on Hadoop
PDF
Twilio API を PHP で触ってみよう
PDF
OSSから学ぶSwift実践テクニック
20140930 anything as_code
CGI::Application::Dispatch
認証機能で学ぶ Laravel 5 アプリケーション
PHPという概念が存在しない退屈な世界 - AWS LambdaでWebAPP編
Phpでrest apiを作った話
私のチームのリーダブルコード
PHPコードではなく PHPコードの「書き方」を知る
勉強会20140207
serverless framework + AWS Lambda with Python
もう XAMPP / MAMP はいらない!
Vagrant で作る PHP 開発環境
CMS for Cloud by Ruby
Ansible 2.0 のサマライズとこれから
Heroku+MongoLabでダミーサーバー
Scala + Finagleの魅力
React.jsでサービスを作ってみた話
Vagrant で PHP 開発環境を作る ハンズオン
Ansible ではじめるサーバ作業の自動化
Scala on Hadoop
Twilio API を PHP で触ってみよう
OSSから学ぶSwift実践テクニック
Ad

Similar to WebAPIのバリデーションを、型の力でいい感じにする (20)

PPT
Inside mobage platform
PPTX
TypeScriptで書くLambdaをCDKでいい感じに管理する.pptx
PDF
SendGrid SDKを捨てた話
PDF
社内勉強会資料(Varnish Module)
PDF
VSCodeで始めるAzure Static Web Apps開発
PDF
OpenStack API
PDF
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
PPTX
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
PDF
Node.jsとAWS入門(Elastic Beanstalk & AWS SDK for Node.js)
PDF
How to Make Own Framework built on OWIN
PDF
ソーシャルアプリ勉強会(第一回資料)配布用
PDF
WTM53 phpフレームワーク いまさらcodeigniter
PDF
Chef+serverspec+werckerでインフラCIする話
PDF
Container Storage Interface のすべて
PDF
Hakodate - simple framework
PDF
初めての Data api cms どうでしょう - 大阪夏の陣
PDF
Server side Swift & Photo Booth
PPT
Ruby on Rails Tutorial Chapter5-7
PPTX
ネットワーク機器のAPIあれこれ入門 (NetOpsCoding#2)
PDF
最近のRails開発のはなし
Inside mobage platform
TypeScriptで書くLambdaをCDKでいい感じに管理する.pptx
SendGrid SDKを捨てた話
社内勉強会資料(Varnish Module)
VSCodeで始めるAzure Static Web Apps開発
OpenStack API
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
Node.jsとAWS入門(Elastic Beanstalk & AWS SDK for Node.js)
How to Make Own Framework built on OWIN
ソーシャルアプリ勉強会(第一回資料)配布用
WTM53 phpフレームワーク いまさらcodeigniter
Chef+serverspec+werckerでインフラCIする話
Container Storage Interface のすべて
Hakodate - simple framework
初めての Data api cms どうでしょう - 大阪夏の陣
Server side Swift & Photo Booth
Ruby on Rails Tutorial Chapter5-7
ネットワーク機器のAPIあれこれ入門 (NetOpsCoding#2)
最近のRails開発のはなし
Ad

WebAPIのバリデーションを、型の力でいい感じにする