javascriptのfetchを使ってAPIより結果を取得
概要
fetch使ってみたい
APIはgo言語で作成した適当なやつ
サーバ側
package main import ( "encoding/json" "log" "math/rand" "net/http" "time" ) func main() { http.HandleFunc("/sample_api1", sampleAPI1) http.HandleFunc("/sample_api2", sampleAPI2) http.HandleFunc("/sample_api3", sampleAPI3) http.ListenAndServe("127.0.0.1:8080", nil) } // Response ... type Response struct { Status int `json:"status"` Rssult string `json:"result"` } func sampleAPI1(w http.ResponseWriter, r *http.Request) { //if r.Method == http.MethodOptions { // return //} log.Println("api1", r.Method) rand.Seed(time.Now().UnixNano()) time.Sleep(time.Duration(int(rand.Intn(5))) * time.Second) response := Response{http.StatusOK, "ok1"} res, _ := json.Marshal(response) r.Close = true defer r.Body.Close() w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "*") w.Header().Set("Accept", "application/json") w.Header().Set("Content-Type", "application/json") w.Write(res) } func sampleAPI2(w http.ResponseWriter, r *http.Request) { //if r.Method == http.MethodOptions { // return //} log.Println("api2", r.Method) rand.Seed(time.Now().UnixNano()) time.Sleep(time.Duration(int(rand.Intn(5))) * time.Second) response := Response{http.StatusOK, "ok2"} res, _ := json.Marshal(response) w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "*") w.Header().Set("Content-Type", "application/json") w.Write(res) } func sampleAPI3(w http.ResponseWriter, r *http.Request) { //if r.Method == http.MethodOptions { // return //} log.Println("api3", r.Method) rand.Seed(time.Now().UnixNano()) time.Sleep(time.Duration(int(rand.Intn(5))) * time.Second) response := Response{http.StatusOK, "ok3"} res, _ := json.Marshal(response) w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "*") w.Header().Set("Content-Type", "application/json") w.Write(res) }
やって置かないとポートが違うとかなんとかでCORSエラーで弾かれる
javascript(Promise.all)
unction taskA() { return fetch("http://127.0.0.1:8080/sample_api1", { method: "GET", // *GET, POST, PUT, DELETE, etc. mode: "cors", // no-cors, cors, *same-origin cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached credentials: "same-origin", // include, same-origin, *omit headers: { "Content-Type": "application/json; charset=utf-8", }, redirect: "manual", // manual, *follow, error referrer: "no-referrer", // no-referrer, *client //body: JSON.stringify("{}"), // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります }) .then(function(response) { console.log("taskA"); return response.json(); }); } function taskB() { return fetch("http://127.0.0.1:8080/sample_api2", { method: "GET", // *GET, POST, PUT, DELETE, etc. mode: "cors", // no-cors, cors, *same-origin cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached credentials: "same-origin", // include, same-origin, *omit headers: { "Content-Type": "application/json; charset=utf-8", }, redirect: "manual", // manual, *follow, error referrer: "no-referrer", // no-referrer, *client //body: JSON.stringify("{}"), // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります }) .then(function(response) { console.log("taskB"); return response.json(); }); } function taskC() { return fetch("http://127.0.0.1:8080/sample_api3", { method: "GET", // *GET, POST, PUT, DELETE, etc. mode: "cors", // no-cors, cors, *same-origin cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached credentials: "same-origin", // include, same-origin, *omit headers: { "Content-Type": "application/json; charset=utf-8", }, redirect: "manual", // manual, *follow, error referrer: "no-referrer", // no-referrer, *client //body: JSON.stringify("{}"), // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります }) .then(function(response) { console.log("taskC"); return response.json(); }); } // 並列ですべて完了後にレスポンスを返却 console.log("start"); Promise.all([ taskA(), taskB(), taskC() ]).then(function(data) { console.log("result start"); console.log(data); console.log("result end"); }); console.log("end");
↓
start end taskB taskC taskA result start (3) [{…}, {…}, {…}]0: {status: 200, result: "ok1"}1: {status: 200, result: "ok2"}2: {status: 200, result: "ok3"}length: 3__proto__: Array(0) result end
※逐次処理も同じ感じで対応できそう
所感
今までajaxのcallbackでやってた部分が多いため、Promiseに対応していかないといけないが
ちょっと仕組みが難しく感じる
少しずつ確認して理解していくしかない…
javascriptのPromiseとかいうのを今更少しだけ学習する
Promise
非同期処理するやつ
Promiseで実行した結果はresolveが正常時、rejectが異常時に設定しないと取得できないらしい
サンプル
1つの処理についての非同期処理
unction sampleTask1() { return new Promise(function(resolve, reject) { setTimeout(function () { console.log('sample1'); resolve("sample1"); }, 3000); }); } console.log("start"); sampleTask1() .then(function(result) { console.log("result", result); }) .catch(function(error) { console.log("error"); }); console.log("end");
↓
start end sample1 result sample1
※単純に「new Promise」のオブジェクトより結果を判定する
複数の処理を非同期内で逐次処理で行う
console.log("start"); Promise.resolve() .then(function() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("sample2-1"); resolve("sample2-1"); }, 16); }); }) .then(function(result) { console.log("result", result); }) .then(function() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("sample2-2"); resolve("sample2-2"); }, 10); }) }) .then(function(result) { console.log("result", result); }) .then(function() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log("sample2-3"); resolve("sample2-3"); }, 5); }); }) .then(function(result) { console.log("result", result); }); console.log("end");
↓
start end sample2-1 result sample2-1 sample2-2 result sample2-2 sample2-3 result sample2-3
順番に処理を行うため、1つ前の実行結果を次の処理で取得するようになっていて、最後に一括で取得などを行いたい場合は少し工夫する必要がある。
複数の処理を実行し、全ての処理が完了後に結果を取得
console.log("start"); Promise.all([ new Promise(function(resolve, reject) { setTimeout(function() { console.log("sample2-1"); resolve("sample2-1"); }, 16); }) , new Promise(function(resolve, reject) { setTimeout(function() { console.log("sample2-2"); resolve("sample2-2"); }, 10); }) , new Promise(function(resolve, reject) { setTimeout(function() { console.log("sample2-3"); resolve("sample2-3"); }, 25); }) ]).then(function(data) { console.log("result start"); console.log(data); console.log("result end"); }); console.log("end"); |< ↓ >|| start end sample2-2 sample2-1 sample2-3 result start (3) ["sample2-1", "sample2-2", "sample2-3"]0: "sample2-1"1: "sample2-2"2: "sample2-3"length: 3__proto__: Array(0) result end
すべての処理が並列で動作し、最後に処理を実行するため、最後に取得するまでは値を取得できない
まあ、なんか方法はあるとは思うが…
所感
allによる同時並列と逐次処理で次の処理へ流せるのは少しだけ理解した
次はfetchを利用した手法で見てみる
golangのpanic発生しても処理を続行したい
golangでのエラーとかって
基本的にはエラーオブジェクトのチェックしかしていない
panic発生したら
落ちます
package main import ( "log" ) func main() { log.Println("start") // 意図的にpanicを起こす panic("aaaa") //var a *interface{} //*a = 0 //x := 10 //y := 0 //b := x / y //log.Println(b) log.Println("end") }
↓
2019/08/14 10:04:05 start panic: aaaa goroutine 1 [running]: main.main() /sample2-b.go:12 +0x79 exit status 2
そうならないようにerrオブジェクトや変数のチェックで抑制していますが
まれになんらかの理由でオブジェクトにnullが挿入される自体が発生してpanicがおきたらマズい
javaのtry-catchとかあればよいがそういうのはないため、
deferとrecoverを併用することで対応できるとのこと
ドキュメントより以下、引用(http://golang.jp/effective_go#recover)
panicが呼び出されたとき(これには、配列のインデックスが範囲外であるときや、型アサーションに失敗したような暗黙的なものも含む)は、すぐさま、カレントの関数を停止し、ゴルーチンのスタックの巻き戻しを開始します。その途中、遅延指定されている関数をすべて実行します。この巻き戻しがゴルーチンのスタックの先頭にたどり着くと、プログラムは終了します。ただし、組み込み関数recoverを使うことで、ゴルーチンの制御を取り戻し、通常の実行を再開させることが可能です。
書き直し
package main import ( "log" "fmt" "runtime" ) func main() { log.Println("start") // 今回は即時関数にしているがerrorの戻り値を取得できるならなんでもよい err := func() (errRes error) { // panic発生時に補足するやつ defer func() { if err := recover(); err != nil { // エラー情報を戻り値のerrResへ設定 errRes = fmt.Errorf("error: %s", err) // stack trace for i:=0;;i++ { _, file, line, ok := runtime.Caller(i) if ok == false { break } log.Printf("depth: %d, line: %4d, file:%v\n", i, line, file) } } }() // 意図的にpanicを起こす //panic("aaaa") //var a *interface{} //*a = 0 x := 10 y := 0 b := x / y log.Println(b) // ここまできたら正常 return nil }() // errorチェック if err != nil { log.Println(err) } log.Println("end") }
↓
2019/08/14 10:13:38 start 2019/08/14 10:13:38 depth: 0, line: 22, file: /work/golang/sample2.go 2019/08/14 10:13:38 depth: 1, line: 522, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/panic.go 2019/08/14 10:13:38 depth: 2, line: 61, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/panic.go 2019/08/14 10:13:38 depth: 3, line: 37, file:/work/golang/sample2.go 2019/08/14 10:13:38 depth: 4, line: 42, file:/work/golang/sample2.go 2019/08/14 10:13:38 depth: 5, line: 200, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/proc.go 2019/08/14 10:13:38 depth: 6, line: 1337, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/asm_amd64.s 2019/08/14 10:13:38 error: runtime error: integer divide by zero 2019/08/14 10:13:38 end
Webフレームワークとかは
API配下のpanicは拾ってくれるが、内部でgoroutineなどを任意で定義した場合にpanicを起こすと
Webフレームワークのアプリ自体が停止する可能性がある・・・
参考
Go言語のエラーハンドリングについて ~panic編~ - Qiita
golangでrecoverしたときの戻り値 - Qiita
http://golang.jp/effective_go#recover
golangでpanicをrecoverしたときにスタックトレースを表示する - code.alone
所感
なんかスマートじゃない感じ…
現状、これしか見つからないからこの方法で対応を試みます。
golangでUUIDの生成をライブラリでお試し
仕事でコード書くより
設計の仕事ばっかり、スケジュール管理とかめんどくさいので
他の人やってくんないかな…
UUIDって?
UUID(Universally Unique Identifier)とは、ソフトウェア上でオブジェクトを一意に識別するための識別子である。
ざっくりいうとユニークIDみたいやつ
基本的にはsha1とかmd5とか乱数で生成していたけど
そういったものにフォーマットがついた感じ
例:3cc807ab-8e31-3071-aee4-f8f03781cb91
バージョン
現在1から5までの方法がある
確認用ライブラリ
https://github.com/google/uuid
今度改めて自身でアルゴリズムは検討するが今回はちょっとお試しでやってみる
ソース
package main import ( "github.com/google/uuid" "fmt" ) func main() { fmt.Println("version1 NewUUID --") for i:=0;i<10;i++ { uuidObj, _ := uuid.NewUUID() fmt.Println(" ", uuidObj.String()) } fmt.Println("version2 NewDCESecurity --") for i:=0;i<10;i++ { uuidObj, _ := uuid.NewUUID() domain := uuidObj.Domain() id := uuidObj.ID() uuidObj2, _ := uuid.NewDCESecurity(domain, id) fmt.Println(" ", uuidObj2.String()) } fmt.Println("version3 NewMD5 --") for i:=0;i<10;i++ { uuidObj, _ := uuid.NewUUID() data := []byte("wnw8olzvmjp0x6j7ur8vafs4jltjabi0") uuidObj2 := uuid.NewMD5(uuidObj, data) fmt.Println(" ", uuidObj2.String()) } fmt.Println("version5 NewSHA1 --") for i:=0;i<10;i++ { uuidObj, _ := uuid.NewUUID() data := []byte("wnw8olzvmjp0x6j7ur8vafs4jltjabi0") uuidObj2 := uuid.NewSHA1(uuidObj, data) fmt.Println(" ", uuidObj2.String()) } fmt.Println("version4 NewRandom --") for i:=0;i<10;i++ { uuidObj, _ := uuid.NewRandom() fmt.Println(" ", uuidObj.String()) } }
結果
mshige1979MBA:golang matsumotoshigeharu$ go run sample1.go version1 NewUUID -- ef45b54c-9f70-11e9-a730-6476baad914e ef45c294-9f70-11e9-a730-6476baad914e ef45c2b2-9f70-11e9-a730-6476baad914e ef45c2c6-9f70-11e9-a730-6476baad914e ef45c2e4-9f70-11e9-a730-6476baad914e ef45c2f8-9f70-11e9-a730-6476baad914e ef45c316-9f70-11e9-a730-6476baad914e ef45c334-9f70-11e9-a730-6476baad914e ef45c352-9f70-11e9-a730-6476baad914e ef45c366-9f70-11e9-a730-6476baad914e version2 NewDCESecurity -- ef45c38e-9f70-21e9-a730-6476baad914e ef45c3ac-9f70-21e9-a730-6476baad914e ef45c3ca-9f70-21e9-a731-6476baad914e ef45c3e8-9f70-21e9-a732-6476baad914e ef45c410-9f70-21e9-a732-6476baad914e ef45c442-9f70-21e9-a732-6476baad914e ef45c46a-9f70-21e9-a733-6476baad914e ef45c49c-9f70-21e9-a733-6476baad914e ef45c4c4-9f70-21e9-a734-6476baad914e ef45c4ec-9f70-21e9-a735-6476baad914e version3 NewMD5 -- 3cc807ab-8e31-3071-aee4-f8f03781cb91 7e7e001e-6957-38d5-b035-41926dd035dd 91d52425-9470-3e2b-8e5f-649b7b4ec795 157ffb9e-d6b2-3b99-9fca-1d347f60b3e3 33b0480d-6d5f-358a-a37a-893e5850fa62 4b41d96b-dd72-3314-9cc2-1ab241eacb30 9337ce23-6909-3137-98d2-a9372bf57692 1e15f3d0-a040-3eca-a8bc-c677bc8bc619 b90f3b5d-059c-3c0e-b3d8-85c577763bf6 f741b68d-a9d2-3396-b499-745920a718ff version5 NewSHA1 -- 13f575fa-02d7-5c38-8e66-48524dd233a4 a56079b2-5373-5656-911a-483e3a92f19c 18c2d54b-1a94-5928-8a4a-2f3dd5fc3e57 8ac4f8a2-89b8-5ea3-81eb-d1654deaab51 bb6bff8b-615f-5791-a9fb-e3543fd9d02e 8209f28f-ccc9-5a90-beaf-d7e8e9927183 387ab566-8b80-5c6c-a156-8e49a5c9c3fe 75a4de2d-8049-5bb1-80c9-7250959af3d2 5991cc2e-2bac-5f83-9ae0-b4a45882a11a b8e33f96-449d-5972-8c9c-98b0487549dd version4 NewRandom -- 01a2c8b4-e3ec-4df5-a027-b7ffe5c6ed5d eb2aa249-3614-4833-b979-99c87c4189a8 4c8bda02-4b27-460a-b46f-d65b4e4f5e48 23df6b4c-e10f-4bcd-b935-0224f3216680 2ab1a839-08b2-4069-9bd7-a772c5934e66 dab1ade8-a446-4609-844b-257a7ab1ba0b eb770f37-a59a-4888-9789-d585f514ae93 b9999fdb-cd20-4bdb-b671-6228667a6746 8160d492-1ead-4d33-b4e8-d242cf2df332 63b96ec6-e9d7-4fa4-abb7-31b9e12fca0d
PlantUMLをVisualStudioCodeで書く
curlを最新版へソースインストール
ruby関連の勉強を始めてみようとしたけど
なんか出る
/tmp/ruby-build.20181006035131.5059 ~/.rbenv/plugins/ruby-build curl: (35) Peer reports incompatible or unsupported protocol version. ~
環境
vangrant上のcentos7(64bit)
なんかcurlが古いらしい
$ curl -V curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.15.4 zlib/1.2.7 libidn/1.28 libssh2/1.4.3 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz $
証明書関連で動かなくなった感じ?
最新をソースコードよりインストールする
yumでツールをインストール
sudo yum install -y openssl-devel libmetalink-devel libssh2-devel c-ares-devel lbzip2
ダウンロード&インストール
wget https://curl.haxx.se/download/curl-7.61.1.tar.gz tar zxf curl-7.61.1.tar.gz cd curl-7.61.1 ./configure --enable-libcurl-option make sudo make install
インストールパスを間違えたので古いやつを削除してリンクにする
sudo rm -f /usr/bin/curl sudo ln -s /usr/local/bin/curl /usr/bin/curl
確認
$ curl -V curl 7.61.1 (x86_64-pc-linux-gnu) libcurl/7.61.1 OpenSSL/1.0.2k zlib/1.2.7 Release-Date: 2018-09-05 Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz UnixSockets HTTPS-proxy $
rbenvでインストールができるようになった\(^o^)/
[vagrant@localhost ~]$ rbenv install 2.3.7 Downloading ruby-2.3.7.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.bz2 Installing ruby-2.3.7... Installed ruby-2.3.7 to /home/vagrant/.rbenv/versions/2.3.7 [vagrant@localhost ~]$
参考
cURLの最新版をインストールする | スターフィールド株式会社
ありがとうございます
go言語の学習:websocketで画像を送信し、webで表示
できたの
なにこれ?
websocketサーバより1秒単位で画像をバイナリ形式で送信したもの
ソース(サーバ側)
package main import ( "log" "net/http" "golang.org/x/net/websocket" "time" "io/ioutil" "image" "gocv.io/x/gocv" "image/color" "bytes" "image/jpeg" "os/user" ) var ( black = color.RGBA{0, 0, 0, 0} ) func main() { name := "server" log.Println(name + " start") http.Handle("/ws", websocket.Handler(WsHandler)) err := http.ListenAndServe(":8001", nil) if err != nil { panic("ListenAndServe: " + err.Error()) } log.Println(name + " end") } func WsHandler(ws *websocket.Conn) { log.Println("connect") // 自動送信 go autoSend(ws) // 受信処理監視 for { var b []byte err := websocket.Message.Receive(ws, &b) if err != nil { log.Println(err) break } } } // 任意の画像を送信する func autoSend(ws *websocket.Conn) { t := time.NewTicker(1 * time.Second) for { select { case <-t.C: // ユーザディレクトリ取得 user, err := user.Current() if err != nil { log.Println(err) return } // 画像ファイル読み込み b, err := ioutil.ReadFile(user.HomeDir + "/Desktop/" + "pug3_pic1.jpg") if err != nil { break } // 画像変換 atom, err := gocv.IMDecode(b, gocv.IMReadUnchanged) if err != nil { log.Println(err) return } defer atom.Close() // 日時取得 now := time.Now() // 偶数 if int(now.Second()) % 2 == 0 { gocv.Rectangle(&atom, image.Rect(400, 400, 900, 700), black, 2) } else { gocv.Rectangle(&atom, image.Rect(400, 400, 900, 700), black, -1) } // 文字列を出力 timeStr := now.Format("2006/01/02 15:04:05") gocv.PutText(&atom, timeStr, image.Pt(20, atom.Rows() - 40), gocv.FontHersheyComplex, 1, black, 1) // 画像に変換 image, err := atom.ToImage() if err != nil { log.Println(err) return } var jpegBytes []byte buf := bytes.NewBuffer(jpegBytes) if err := jpeg.Encode(buf, image, nil); err != nil { log.Println(err) } b = buf.Bytes() websocket.Message.Send(ws, b) log.Println("send") } } }
ソース(クライアント側)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <img id="img1" src=""> <script> var uri = "ws://localhost:8001" + "/ws"; webSocket = new WebSocket(uri); webSocket.binaryType = 'blob'; webSocket.onopen = function(e) { console.log("open"); }; webSocket.onmessage = function(e) { console.log("message"); var data = e.data; if (data.constructor === Blob) { document.getElementById('img1').src = URL.createObjectURL(data); } }; webSocket.onclose = function(e) { console.log("close"); }; </script> </body> </html>
ソース(クライアント版2)
package main import ( "log" "golang.org/x/net/websocket" "strconv" ) var ( doneCh chan bool origin = "http://localhost:8001/" url = "ws://localhost:8001/ws" ) func main() { name := "client" log.Println(name + " start") doneCh = make(chan bool) ws, err := websocket.Dial(url, "", origin) if err != nil { log.Fatal(err) return } go receiveMsg(ws) <-doneCh log.Println(name + " end") } // 受信 func receiveMsg(ws *websocket.Conn) { var b []byte for { err := websocket.Message.Receive(ws, &b) if err != nil { log.Println(err) doneCh <- true break } log.Printf("Receive datasize=" + strconv.Itoa(len(b))) } }
所感
いろいろ悩んだけど基本は画像をバイナリで送信するだけだった感じ
動画のストリーミングという感じではないのでそこまで難しくないかも…
最初はwebsocketの送受信がうまく行かなかったけどw