class: title, smokescreen, shelf, no-footer # Web APIの基礎 <div class=footnote> <small> </small> </div> --- class: col-2,compact # Webの基礎 <div class=footnote> <small> (脚注) Linuxの資格がLPIC(全世界共通)です。 別途ガラパゴス規格で日本にしかないLinucがあります。 SIerなどサーバやネットワーク構築系の業種に就職すると、 CiscoとLinuxの資格をとれと確実に言われます </small> </div> - (1番やさしいグレードの)資格試験のひとつ LPIC Web Development Essentials の解説動画があるので、 これを見てください。 スライド10頁で商用インターネット時代のウエブ30年分の大事な用語が解説されいて、 ちょうどよいです - 次頁以降のために、 この動画前半(10分〜30分あたりの約20分)の用語を確認してください (知らなかった!という人は覚えてください:-) <iframe width="480" height="270" src="https://www.youtube.com/embed/0MtB-AJNY5g" title="いよいよ登場!「Web Development Essentials」の概要と学習のポイント 2022-6-25 D-3" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> - 再生できない方は[こちら](https://youtu.be/0MtB-AJNY5g)から --- class: img-right,compact # [復習] HTTP (over TCP) <div class=footnote> <small> (脚注) 自作すれば、もっといろいろ出来ますね〜 ただし超大変 (自作すると日々のセキュリティ対応も発生) </small> </div> ![height240px](http://lectures.fml.org/slides/network/tcpip/tpt_tcp/images/kids_nagashisoumen.png) ![height240px](http://lectures.fml.org/slides/network/tcpip/app_www/images/http-overview.gif) - いわば(双方向)**デジタルながしそうめん** - TCPは**論理的なデジタル土管**を作ります - この土管上には何でも流せるのですが、それでは収拾がつかないので、 HTTPが**簡単な約束事(protocol)**を決めています - WWWクライアント(ブラウザ)とWWWサーバ**双方が理解できるHTTPのお約束だけが利用可**です(あたりまえ) - ブラウザ ... 例: chrome, firefox - サーバ ... 例: apache, nginx, thttpd --- class: center,compact # CGI (Common Gateway Interface) 全体像 <div class=footnote> <small> </small> </div> ![height480px](images/janken-api-wholesight.png) --- class: compact # CGI: データ転送 <div class=footnote> <small> </small> </div> - HTTPを使い、 クライアント(WWWブラウザ)からWWWサーバ側へ情報(リクエスト,引数のパラメータ群)を渡す取り決めがCGI - HTMLでCGI呼び出しをしたいときに使う書式がFORM文 <br> これを書けばブラウザはCGIなんだなと分かってくれます - CGI呼び出しの際に指定するHTTPのメソッドとしてはGETとPOSTが定番 ``` # GETメソッド (ブラウザは送信先のURLにパラメータを追加; でも、これでログインするとパスワードが見えちゃうよね?) ACTION = http://ise.ex2022.fml.org/api/janken/v1?key=value # POSTメソッド (URLにパラメータはつけない、送信するデータ本文に key=value が含まれています) ACTION = http://ise.ex2022.fml.org/api/janken/v1 ``` --- class: img-right,compact # CGI: サーバ側(apacheなどでの定番の仕組み) <div class=footnote> <small> </small> </div> ![height320px](images/janken-api-wholesight.png) - 注意: 今回の見本www.pyなどではサーバ本体とCGIは一体化しています。 以下はWWWサーバの定番の動作説明です - (サーバ外に)WWWサーバが呼び出す下請け(CGI)プログラムを用意します。 Web初期には[言語PerlでCGI!が大流行](#perl-cgi.pm)) - ブラウザからのリクエストを受けるとWWWサーバはCGIを呼び出します。 CGIへは標準出力もしくは環境変数を使い情報を渡します。 たいていのプログラミング言語には、 これらの情報を取り出す関数が用意されているので(CGI側では)それを使い取り出します (付録: 各言語の例: [Go言語](#golang) [Python](#python) [Perl](#perl) [シェル](#shell) , [勉強のためベタ書き](#perl-raw)してもよいね:-) --- class: compact # CGI: クライアント側(リクエスト) FORM文 <div class=footnote> <small> </small> </div> ``` <form action="http://ise.ex2022.fml.org/api/janken/v1" method=POST> <input type=textarea id=jibun value=2 name=jibun> <input type=submit value="click(python version)"> </form> ``` ブラウザはFORMをみるとCGIね!と分かってくれます。 大昔からのHTML標準,詳細は自分で勉強してね(概略は以下)。 なお基本的にスペース区切り(元々Unix上の開発だもん:-) ``` <form> ... </form> CGIを呼び出すお約束 <form ... >文 action=URL 呼び出す先のURLです。よくある例: http://サーバ/cgi-bin/下請け.cgi method=POST HTTPで定義されているメソッドの一つを選択、POSTが定番 <input ...>文 typeで入力する情報のタイプ指定をする type=textarea 「name=キー value=値」で「キー」と「値」の組を指定(値は変更可,「value=値」はデフォルト値) type=submit 送信ボタン(valueは表示されるアイコン内の文字列) ``` --- class: compact # CGI: クライアント側(レスポンス) HTMLの解釈 <div class=footnote> <small> (脚注) どこで誰(e.g. 下請け(CGI)部分とクライアント部分)が何をしているのか?を意識してください </small> </div> - **ブラウザは渡されたデータ=HTMLをレンダリングして表示**するだけです <br> **じゃんけんAPIは標準機能だけを使ったシンプルな実装**です(次頁も参照) - 下請けの(CGI)プログラムは単に**HTML形式の答え**を返し、 ブラウザは渡されたデータがHTMLだと理解してブラウザの画面を作成します - タイプ = HTML (HTTPヘッダのContent-Typeがtext/html) - データ = HTML (HTTPペイロードにある`<html> ...`データ本体) - [発展] もっと凝ったことがしたいなら -> [こちら](#api-hack) - 例: CGIプログラムが「HTML5やJavascriptを駆使したゴリゴリのHTML5」を生成してブラウザに返し、 ブラウザに頑張ってもらう - 例: データのやりとりから独自のものを考える。 やりとりするデータフォーマットやプロトコルの設計、 下請けプログラムの改造、 クライアント側JSの準備など... --- class: img-right,compact # [まとめ] 実例: じゃんけんAPI <div class=footnote> <small> (脚注) 見本のwww.pyはサーバとCGI部分が一つのプログラムに共存しています。 そのため別途WWWサーバの設定はしません。 前述のとおりモダンじゃないけど、演習の難易度をほどほどにするためです </small> </div> ![height320px](images/janken-api-wholesight.png) - 標準的なCGIの仕組みそのままです - クライアント(WWWブラウザ)はFORM文のHTMLをレンダリングして表示します。 FORMとあるのでCGIだと理解。 submit(たいてい「送信」などと表示)をクリックすると 「ブラウザはHTTPでパラメータをサーバへ送ります」 - サーバは下請けのCGIを呼び出します。 CGIがサーバへHTMLを返し、 それをサーバはクライアントへ返します - クライアントは「返されたHTMLをレンダリングして表示」(するだけ) --- name: api-hack class: title, smokescreen, shelf, no-footer # 独自APIの設計と実装 <div class=footnote> <small> </small> </div> --- class: center,compact # 全体像「独自APIの設計と実装」 ![height480px](images/janken-api-hack.png) --- class: compact # 独自APIの設計と実装 <div class=footnote> <small> (脚注) 見本のwww.pyは自分でWWWサーバを実行する形式ですがmodernじゃない。 でも本気をだすと別途nginxの設定が..などとサーバ設定の話が始まってしまい、 終わらない;_; 本気だすなら秋学期にレッツトライ </small> </div> - すみずみまで独自実装は、しないでしょう <br> (1/1000秒で戦うアプリとかなら独自実装がありうるか?そんなの株取引とかくらいか?) - 次のあたりが検討事項です - HTTPでデータを渡すところはそのまま利用する?しない?(したほうがいいぞ:-) - 基本的にやりとりするデータフォーマットの選択くらい? TEXT? XML? JSON? - **やりとりするべきデータは各自が考える(設計する)のです(本命1**) - 「データを渡されたサーバ側の下請けの独自実装」と 「クライアント側の独自実装(データを送信、受信したデータの解釈)」 は、ありうる話やね - 下請けの実装は好きな言語でok (**本命2のサーバ側**,元ネタとしてwww.py) - ブラウザを独自開発などはしない(開発もセキュリティ対応など運用が大変) - (独自解釈をするために)Javascriptをがんばってゴリゴリ書くのが典型です (ここが、**本命2のクライアント側**で、 返ってきたデータをJSで動かしてかっこいいことできるかな?とか考えるパート; ジャンケンなら絵を表示するとか?) --- class: img-right,compact # ヒント: HTTPでやりとりするデータを考える ![height320px](images/janken-api-hack.png) 1. ジャンケンAPIの見本はシンプルで、 じゃんけんの必須情報の数字をやりとりしているだけ 1. 統計情報を駆使してcomputerの手を改善したいなら、 CGIの中のコードをゴリゴリ書く話です(CGIと外部との連携はあるかもしれない)。 ブラウザとサーバとのデータのやりとりは関係ない 1. ブラウザで動いているJavascriptへの指示があれば、その指示を渡す必要がある --- class: img-right,compact # ヒント: クライアントで頑張るとは何か? <div class=footnote> <small> (脚注1) GoogleがAnguler,FacebookがReact。 VueはAngulerの中の人がGoolgeやめて中国に帰って開発してるやつ (脚注2) もうSPAも一昔前だね〜とか言ってるね(流行が早すぎてよくわからない系やで、この分野) </small> </div> ![height320px](images/janken-api-hack.png) - これも、どこで何を動かしているのか分かってる?をよく考えてください - Javascriptはブラウザの中(上?)で動かしているプログラムです。 めちゃくちゃ進化を続けているので、 ブラウザ上で見栄えのよいプログラムを動かして、 その制御に必要な情報だけ(差分だけでok)をサーバから送ればよいのです - むかしはブラウザ画面全体を更新していたのですが、 一昔前の流行SPA (single page application)は、 必要な一部だけを更新しています (例: Angular, React, Vue) --- class: title, smokescreen, shelf, no-footer # 付録 <div class=footnote> <small> </small> </div> --- name: golang class: compact # Go言語でCGIの値を取り出す ``` // keyを取り出すところ(0028-0029行) // 注意: www.goの最初にimport "net/http"している前提 // キー key // 値 value func Handler(w http.ResponseWriter, r *http.Request) { // r = クライアントからのリクエストが入っている構造体へのポインタ value := r.FormValue("key") // ...略... } ``` - [www.goのソースコード注解](https://exercises-aws.fml.org/ja/appendix/golang/www.go/) (AWS構築ガイド付録より) --- name: python class: compact # Python言語でCGIの値を取り出す ``` # 最初の方でモジュール群をimportしている前提 # import http.server # import socketserver form = cgi.FieldStorage() value = form.getvalue("key") # ...省略 ... ``` - [www.pyのソースコード注解](https://exercises-aws.fml.org/ja/appendix/python/www.py/) (AWS構築ガイド付録より) --- name: perl class: compact # Perl言語でCGIの値を取り出す (最近) ``` use Mojolicious::Lite; # Mojolicious::Lite use utf8; # ルーティング get '/search/:id' => sub { # URLの一部分だけにマッチさせる my $self = shift; my $value = $self->param('key'); # ... 省略 ... }; ``` - モダン過ぎる書き方で、いまいち愛が湧かないなぁ(w) - [Mojolicious Perl Webフレームワーク入門](https://mojolicious.perlzemi.com/) (perlゼミ) --- name: perl-raw class: compact # Perl言語でCGIの値を取り出す (1990年代なかば) ``` $ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; # 念のため大文字に変換 if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); # POST データをスカラー変数 $buffer に代入 } else { $buffer = $ENV{'QUERY_STRING'}; # GET データをスカラー変数 $buffer に代入 } # $buffer は key1=value1&key2=value2& ... という形なので foreach (split(/&/, $buffer)) { # & で切って(暗黙の)配列に入れる ($key, $value) = split(/=/, $_); # = で分けて(split)、$keyと$valueへ代入 $value =~ tr/+/ /; # MIME decode (準備) $value =~ s/%(..)/pack("C", hex($1))/eg;# MIME decode } ``` - 1990年代後半あたりの書き方、生で環境変数を操作しています(なつかしい) - [CGI](https://www.tohoho-web.com/perl/cgi.htm)(とほほのWWW入門より) --- name: perl-cgi.pm class: compact # Perl言語でCGIの値を取り出す (1990年代後半?〜) ``` use CGI; my $query = new CGI; my $value = $query->param("key"); # ... 省略 ... ``` - 有名なCGIモジュール - ながらく標準モジュールでPerlといえばCGI.pmという時代があったけれど、 Perl 5.22 release(2015)で削除されました;_; --- name: shell class: compact # シェルでCGIの値を取り出す ``` query=$QUERY_STRING # 環境変数から値を取得 query=$(echo $query | nkf -mQ) # mime decode ... $query をparse ... ``` - 環境変数は、そのままの変数名で取り出せます - MIME decodeは下請けに`nkf`(network kanji filter)を呼ぶと楽だと思います - `&`で切って、さらに`=`で切るのをシェルでやるのが割と面倒 ... <br> sedで頑張るか、いいかawkで書いちゃうかな〜とか - 参考文献: [Windows/Mac/UNIX すべてで20年動くプログラムはどう書くべきか](https://www.c-r.com/book/detail/1083) - 著者:松浦智之/監修:USP研究所 - ISBNコード:978-4-86354-209-9