知識のリンク集

技術系アウトプット

SQLインジェクションとplaceholder機能を使った対応処理

SQLインジェクション

r.GET("/1/users/:Id"

上記のAPIをWebで叩くとき、WebではURLがブラウザの上部に表示され、下記のようにURLが表示される。
"https://domain.jp/users/id"

パラメーター部分(id)はURLを直接書き換えることで別の値に置き換えることができる。
データベースと連携して動作するWebサイトでは、外部から入力された値を元にSQL文を組み立てるため、
入力された値によって意図しないSQLが生成され、結果として不正にDBのデータが読み取られたり、データが改ざんまたは削除される恐れがある。

この手法の攻撃を"SQLの注入"という意味でSQLインジェクションと呼ぶ。

placeholder機能

SQLインジェクションの対応として、安全なパラメーターを使ってSQL文を生成するplaceholder機能を使うことができる。

Routes

func GetUser(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
 // パラメータをチェック
 Id, err := strconv.ParseInt(ps.ByName("id"), 10, 0) helpers.CheckErr(err, "Invalid Id")
 // 問題なければモデルの処理へ受けわたす
 R.JSON(w, http.StatusOK, models.GetUser(Id))
}

Models

func GetUser(Id int64) UserView { 
 query := UserQuery + ` 
  WHERE s.user_id = ? 
  AND s.user_deleted_date IS NULL
 `
 var user UserView 
 _, err := DbMap.Select(&user, query, id) 
 helpers.CheckErr(err, "Executing query failed")

 return user
}

"SELECT s.user_id = ? "の?には"_, err := DbMap.Select(&user, query, id) "によりidで渡ってきた値が代入されてSQL文が生成される。

A Tour of Go vol.1

Packages

goのプログラムはパッケージで構成され、mainパッケージから開始される。
ソースコードの冒頭で自身を表すパッケージを記載し、
続いてインポートするパッケージを記述する。

package main

import {
    "fmt"
    "net/http"
    "tmhub/helpers"
}

自社のソースコードではpackageの後にくるものは下記5つ

  • main
  • helpers
  • models
  • config
  • routes

これはトップレベルに配置されているフォルダと一部一致する。
インポートのパスで"tmhub/helpers"と呼び出されているパッケージは冒頭がpackage helpersステートメントで始まる。

Goでは構成するファイルを全てpackageという要素とみなす。

Exported names

パッケージをインポートすると、そのパッケージがエクスポートしている名前を参照することができる。
Goでは外部のパッケージから参照できるエクスポートされた名前は大文字から始まる。

func main() {
	fmt.Println(math.Pi)
}

上記のコードで `math.pi` とすると参照できずエラーが出力される。

cannot refer to unexported name math.pi

Functions

引数では変数名の後に型を記述する。
この時引数の型が同じ場合は変数を先に記述して最後に型を記述する記法もある。

func add(x int, y int) int {
	return x + y
}

func add(x, y int) int {
	return x + y
}

関数は複数の戻り値を返すこともできる。
また戻り値は引数に続いて宣言することができる。

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

(sum int)に続く(x, y int)が戻り値
上記のコードの結果は7 10が返る

戻り値を引数の後に並べて宣言する。
return は記述必須。

個人的にはreturnが記述必須なら戻り値はreturnに続けて書くか
もしくは戻り値を先頭に記述するなら当然返却するものとしてreturn記述省略可とかにして欲しかった。。

GoのMVC

最近業務でGoの修正を行う機会があり、クライアント側しか触ってこなかった自分には未知のことがたくさんあった。
まずは大枠としてMVCから学ぶ。

 

 
MVCレイヤーアーキテクチャという設計手法に基づいている。

レイヤーアーキテクチャ

アプリケーションを責務に応じたいくつかの層としてとらえる設計手法。

View ユーザーインターフェース モデルのデータを取り出してユーザが見るのに適した形で表示する要素
Controller アプリケーション層 ユーザからの入力(通常イベントとして通知される)をモデルへのメッセージへと変換してモデルに伝える要素
Model ドメイン データと手続きを表現し、データの変更をViewに通知する要素
  • 各オブジェクトはいずれかの層に属し、複数の層にまたがることはない
  • 層の関係は一方通行であり、相互参照する関係は層をまたがない

ModelはControllerやViewでどう呼び出されるか知るべきではないし、ControllerはViewがどのように描画するか知るべきでない。
互いの層を一方的に利用するようにすることで、オブジェクト間の結合を疎に保ち、ドメインロジックの凝集度を高める。

これは作り手により異なるところかもしれないが、Goの勉強なので自社で扱っているプロダクトのGoの構造をこれにあてはめた。

f:id:yuri_iOS:20180612001814p:plain

Controller

  • routes: アプリからユーザーが入力したデータを受け取り、どの処理をするかをモデルへ通知する。
  • handlers: モデルへ通知する前にセッションのチェックなど毎回実施されるチェック処理を行う。
  • helpers: 便利道具。毎回実施するチェック、とはいかないがさまざまなところで使う処理群。

Model

routes
func New (r * httprouter.Router) {
    r.GET('users/:id', handlersLoginRequired(GetUser))
}

handlersであるhandlersLoginRequired()でユーザーがログインしていることを確認し、modelのGetUser()へ通知する。

models
type User struct {
    Id    Int64        `db:"id"     json:"id"`
    Name  String       `db:"name"   json:"name"`
}

func (u *User) Login() {
  // ログインのビジネスロジック...
}

構造体の定義(型とdb,jsonでのkeyの定義)とその構造体に紐づくビジネスロジックの処理がmodels配下に書かれる。

WebSocketを使ってリアルタイムチャットアプリを作った

 Reactの勉強の一環としてWebSocketを用いたリアルタイムチャットアプリを作った。

 

使用ライブラリ

Express

Socket.IO

リアルタイムな双方向通信を可能にするNode.jsライブラリとブラウザ用ライブラリのセット

 

WebSocket

サーバーとクライアントの双方向通信を可能にする技術。

ウェブ ブラウザとサーバー間に「ソケット」接続を確立する API を定義してあり、クライアントとサーバーの間に持続的接続が存在するためどちらの側からでもいつでもデータの送信を開始できる。

主にHTMLで書かれた文書を転送するためのプロトコルであるHTTPでは双方向の通信ができなかったために生み出された。

この時、一定間隔でサーバに問い合わせをし続けるポーリングやサーバで発生したイベントをクライアントからの要請なしにクライアントに送信することができる技術であるCometなどを使ってどうにかHTTPでクライアントとサーバ間の双方向通信を実現しようという動きもあった。

 

WebSocket通信の確立

HTTP通信によりハンドシェイクを行うことで通信を確立させる。

リクエス

HTTPのUpgradeヘッダを使用し、プロトコルの変更を行う。

レスポンス

ステータスコード101「Switching Protocols」が返る。
これによりハンドシェイクによってコネクションが確立し、これ以降はHTTPではなくWebSocketのプロトコルで通信が行われることになる。

 

github.com

SPA(ReactとExpress)

Reactの勉強の一環としてSPAを作成してみた。
WebサーバーはNode.jsで構築するが、その際にフレームワークExpressを使用した。

 

SPA

それまでWebアプリケーションは画面の表示を変更するたびにWebサーバーと通信を行い、表示すべきHTMLを受け取っていた。

それに対しSPAでは必要な時だけWebサーバーと通信する。
これにより使い勝手や操作性が向上するが反面複数のモジュールを読み込むために初回の読み込みには時間がかかる。

f:id:yuri_iOS:20180610002748p:plain

使用したライブラリ

NeDB

データはJSON形式で保存され、全てJavaScriptで書かれているのでバイナリ依存の心配は不要。

NeDBについて詳しく書かれた記事はこちら

 

JavaScriptでDBもAPIもクライアント側も全部作れてしまうのでいい時代に生まれた。

 

作ったものは簡単な掲示

github.com

 

APIを自作してDBとやりとりするところまで全部書いたのは初めてでなんとなくAPIとかサーバー全般に対して恐怖心があったけどぬぐえた気がする。

 

Reactの描画処理が速い理由

Reactの描画処理では仮想DOMの採用にはじまり、高速化するために工夫がこらされている。

工夫の方向としては"処理が重いリアルなDOMの操作を極力抑える"というものでる。

まずはリアルDOMの処理内容を知り、そこからどういった点で仮想DOMの方が速いのか比較する。

 

続きを読む