Slide 1

Slide 1 text

YouTube Live (2020.11.19 Thur. 21:00~)

Slide 2

Slide 2 text

話す人 現役のエンジニア二人 赤貝が好きな CTO と デザイン勉強中のエンジニア @mu_book 最近の仕事は figma で画 面設計をつくることで す。英語の勉強してる。 ムー zaru @zaru CTO, Love 赤貝, JavaScript, Firebase, Web Components. Twitter フォロー お願いします!

Slide 3

Slide 3 text

- Ajax について - Ajax を実現する実装例 - CORS・クロスドメインの Ajax 話すこと

Slide 4

Slide 4 text

Ajaxってなんだろう?

Slide 5

Slide 5 text

Ajaxとは Asynchronous JavaScript + XML 非同期 に JavaScript 使っていい感じに データ 取得 and の a じゃない のか… 非同期については async/await の動画を 見てください〜

Slide 6

Slide 6 text

JavaScriptでデータ取得 click 新しい HTML サーバ 通常のページ遷移 click データ サーバ Ajax で UI 書き換え JSがDOM を部分的 に変更 通常は、サーバへリクエストすると HTML 自体が返ってきてブラウザはそれを表示す るためにページ自体を書き換える。 JavaScript の XMLHttpRequest という 機能を使ってサーバにデータをリクエスト し、受け取ったら HTML の中の一部の DOM だけを書き換える。 JSが サーバに リクエス ト

Slide 7

Slide 7 text

Ajaxの歴史 Jesse James Garrett さんが JavaScript を使って非同期にコン テンツを更新している手法を Ajax を名付けた IEが XMLHttpRequest 独自実装 Firefox が互換性ある XMLHttpRequest 実装 Google Map など非同期 コンテンツ更新のサービ スが盛り上がる クロスドメイン対応のLevel2 Ajaxを手軽に扱うFW登場 ネイティブアプリのように リッチで軽快な Web アプ リが盛り上がる XMLからJSONメインへ

Slide 8

Slide 8 text

Ajaxを実装してみる

Slide 9

Slide 9 text

XMLHttpRequest 1 2
const buttons = document.querySelectorAll('.js-ajax'); buttons.forEach((button) => { button.addEventListener('click', (event) => { const id = event.target.dataset.id; const xhr = new XMLHttpRequest(); xhr.addEventListener('load', (res) => { const json = JSON.parse(res.target.responseText); const resultElm = document.getElementById('result'); resultElm.innerText = json.title; }); xhr.open('GET', `api.php?id=${id}`); xhr.send(); }); }); HTML + JS ボタンを押すと XMLHttpRequest を使っ てサーバへリクエストをする。返ってきた JSON を指定の DOM に挿入する。 click データ サーバ

Slide 10

Slide 10 text

fetch ( async / await ) 1 2
const buttons = document.querySelectorAll('.js-ajax'); buttons.forEach((button) => { button.addEventListener('click', async (event) => { const id = event.target.dataset.id; const url = `http://0.0.0.0:9000/api.php?id=${id}`; const res = await fetch(url); const json = await res.json(); const resultElm = document.getElementById('result'); resultElm.innerText = json.title; }); }); HTML + JS fetch と await を使えばもっと直感的に気 軽にかける。 ただし IE11 で使えない(polyfill)、アップ ロード時の進行状況が取得できないなど、 完全に置き換え可能ではない点に注意。

Slide 11

Slide 11 text

Ajaxを実現する手段 const url = `/api.php?id=${id}`; $.ajax({ url, success (json) { const resultElm = document.getElementById('result'); resultElm.innerText = json.title; } }); jQuery const url = `/api.php?id=${id}`; const res = await axios.get(url); const resultElm = document.getElementById('result'); resultElm.innerText = res.data.title; axios 今、Ajax 目的で積極的に使うことはない が、XMLHttpRequest をラップしていい 感じに使えるようにされている。 Ajax ライブラリの定番。Promise で返っ てくるので await などが使えて便利。 jQuery と同様に XMLHttpRequest を ラップしているので IE11 も使える。

Slide 12

Slide 12 text

Ajax 以外のデータ通信 JSONP クロスドメインで JS からデータを取 得するための方法。 XMLHttpRequest Level2 の登場で 必要なくなった。現代では忘れて良 い技術。むしろ使ったらダメ。 comet ブラウザからのリクエストなしに、 サーバからデータを送信することが できる。チャットなどの半リアルタ イムな挙動が作れる。ただ WebSocket に代替している WebSocket ブラウザと常に接続をし双方向通信 をするプロトコル。HTTP と違い、 都度コネクションを張らずにすみ軽 量にデータのやりとりができる。専 用のサーバが必要だが強力 EventSource ブラウザと常に接続をしサーバから ブラウザにデータを送信できる(双 方向ではない)。標準化され通常の HTTP と同じように扱えるので使い やすい

Slide 13

Slide 13 text

XML or JSON ? ? click データ サーバ

Slide 14

Slide 14 text

昔はXML、今はJSON XML ユーザ定義のタグで構造化できるマーク アップ言語。汎用性が高いが、シンプルな Web アプリケーションでは冗長で取り扱い が難しい側面がある。 JSON JavaScript のオブジェクト表記でかける データ既述言語。JavaScript で扱いやす く汎用性も高い。数値・真偽や Null など の型がある。 Ajax で返すデータには HTML を返すパターンもある(Rails など)。 サーバサイドで HTML を組み立てて、ブラウザは受け取ったものを単 純に innerHTML に入れるだけなので楽といえば楽。 HTML

Slide 15

Slide 15 text

ここまでが普通のAjaxの話 普通じゃ ないって 何?

Slide 16

Slide 16 text

CORS・クロスドメイン オリジン間リソース共有 Cross-Origin Resource Sharing

Slide 17

Slide 17 text

クロスドメインのAjaxは難しい a.example.com b.example.com click データ サーバ 別のサーバ からの通信 だから拒否 ❌ セキュリティの観点から XMLHttpRequest などで、別のド メインに対してのリクエストは成功しない。事前に許可して もらう必要がある。 エラー例 見たこと ある…!

Slide 18

Slide 18 text

事前許可の方法 リクエストされるがレスポンスヘッダで、許可するオリジン (リクエスト元ドメイン)を指定することで、クロスドメイン でも Ajax が可能になる。 Access-Control-Allow-Origin: https://a.example.com Response Header a.example.com b.example.com click データ サーバ OK! * ワイルドカード指定もできるが、基本は明示的に指定する方が良い。

Slide 19

Slide 19 text

CORS の Cookie リクエスト先のサーバと Cookie の送受信をするには、サーバ 側から許可してもらう必要がある。 Access-Control-Allow-Credentials: true Response Header a.example.com b.example.com click データ サーバ Cookie 送るよ Cookie 送るよ const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.withCredentials = true; xhr.send(null); fetch(url, { credentials: 'include' }) Cookie 送信例 JS 側もクレデンシャル情報を送るよと言う指示が必要

Slide 20

Slide 20 text

これで全部解決…? 指定する だけじゃ ないの?

Slide 21

Slide 21 text

プリフライトリクエスト

Slide 22

Slide 22 text

単純ではないリクエストは拒否される ??? 単純では ない…とは?

Slide 23

Slide 23 text

単純と、単純ではない a.example.com b.example.com click データ サーバ GET など は単純なの でOK a.example.com b.example.com click データ サーバ これはNG ❌ GET POST PUT DELETE カスタムヘッダ Access-Control-Allow-Origin で許可されたオリジンからの リクエストでも「単純ではない」と判定されたリクエストは 許可されない。 単純 単純ではない

Slide 24

Slide 24 text

単純と単純じゃない 単純 単純じゃない HTTP Method GET, POST, HEAD PUT, DELETE Request Header (主要だけ抜粋) Accept Accept-Language Content-Language Content-Type ( ↓条件あり ) ←以外のリクエストヘッダが含 まれている Content-Type application/x-www-form-urlencoded multipart/form-data text/plain ←以外のContent-type application/json など ReadableStream OK NG JSON の POST は 単純じゃな くなる

Slide 25

Slide 25 text

プリフライトリクエスト Preflight Request 単純じゃないのは事前に検証する

Slide 26

Slide 26 text

リクエスト前に検証をする a.example.com b.example.com click サーバ ❶JSON を POST したい ❷本当に送信して いいか確認するね プリフライト リクエスト OK ❸OK だから送信する JSON POST 単純でないと判断された JS からのリクエストは、ブラウザが 自動的にプリフライトと言われる事前検証をして通ってから 送信をするようにしている。 ブラウザ

Slide 27

Slide 27 text

プリフライトは OPTIONS で送信 OPTIONS プリフライト リクエスト プリフライトリクエストは OPTIONS と言う HTTP Method で送信される。サーバはそれに対して許可するメソッドや ヘッダ、オリジンなどをリクエストヘッダで応答する。 ● Access-Control-Request-Method ● Access-Control-Request-Headers ● Origin PATCHは 受け付ける HTTP Method つまり OPTIONS のリクエストに応答できないと Ajax できないし、許可リクエストヘッダ返さないのもダメ ポイント

Slide 28

Slide 28 text

??? なんでこんな 面倒くさいこ とやるの〜

Slide 29

Slide 29 text

なぜプリフライトが必要か サーバ側が想定をしていないリクエストを 受け付けないようにするために事前にチェックをする 理由 ???

Slide 30

Slide 30 text

想定していないリクエスト DELETE 例えば Access-Control-Allow-Origin を設定していない状態で DELETE メソッドのリクエストを送信した場合、ブラウザ側では結果 を受け取れないため CORS block エラーになる。つまりユーザ視点で 言うとリクエストは失敗しているように見える。 ただし…サーバ側にはリクエストは来ているので処理がされてしまう 可能性がある。 つまり実質リクエストは成立してしまっている。

Slide 31

Slide 31 text

???

Slide 32

Slide 32 text

もし、プリフライトがなかったら DELETE リクエストを受け付けて処理する Access-Control-Allow-Origin は 許可していないレスポンスを返す ブラウザが許可されていないのを受 け取って、JS ではレスポンス処理 できないように制限する ❶ Ajax で DELETE メソッドを送信 ❷ ❸ ❹ この時点で 削除される このような副作用が起こる可能性のリクエストを事前に きちんと許可されているかをチェックしておかないと困 るケースがある 理由 ❌

Slide 33

Slide 33 text

今回は言及しませんが、クロスドメインの Ajax は CSRF ( クロスサ イトリクエストフォージェリ ) にも関連があります。挙動を理解せ ずに、盲目的に Access-Control-Allow-Origin などを付けるのは 避けてください。動いたから良い…は危険です。

Slide 34

Slide 34 text

結論: 同じドメインでやろう 念のためですが、同じドメインにすれば CSRF の危険がなくなるわけじゃないです

Slide 35

Slide 35 text

ありがとうございました! 次回は... 未定! 質問感想など呟いていただけると嬉しいです! - ハッシュタグ #mu_zaru - ツイッター情報 @mu_book , @zaru チャンネル登録 Good ボタン お願いします! ムーザルちゃんねる