2020/08,江端さんの技術メモ

  • 概要
    • Webシステムを外部から利用するためのプログラムの呼び出し規約(API)
    • リソースの操作はHTTPメソッドによって指定(取得ならGETメソッド、書き込みならPOSTメソッド)される
    • 結果はXMLやHTML、JSONなどで返される
    • 処理結果はHTTPステータスコードで通知する
  • RESTの基本仕様
    • セッション管理を行わない
    • 情報を操作する命令の体系が予め定義(HTTPのGETやPOSTメソッドなどで)されている
    • 汎用的な構文で識別される(URLやURIなど)
    • 情報の内部に、別の情報を含めることができる
    • リソースに対してURLが対応づけられる
    • 「GET」なら取得、「POST」なら作成、「PUT」なら更新、「DELETE」なら削除のように処理する
  • ここから先は、Golangの開発プロセスになる
    • go get -u github.com/gorilla/mux を行う

golangでシンプルなRESTful APIを作ってみよう!を、そのまま書き下してトレースで動きを追ってみました。

今回から、emacs + gdb を断念して、Visual Studio Code を使ってみました。うん、死ぬほど便利。

//C:\Users\ebata\go_rest\restful\main.go
 
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
)

// Item representation
type Item struct {
	Title       string `json:"title"`
	Description string `json:"description"`
}

// Global, static list of items
var itemList = []Item{
	Item{Title: "Item A", Description: "The first item"},
	Item{Title: "Item B", Description: "The second item"},
	Item{Title: "Item C", Description: "The third item"},
}

// Controller for the / route (home)
func homePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "This is the home page. Welcome!")
}

// Contoller for the /items route
func returnAllItems(w http.ResponseWriter, r *http.Request) {
	respondWithJson(w, http.StatusOK, itemList)
}

// Controller for the /items/{id} route
func returnSingleItem(w http.ResponseWriter, r *http.Request) {
	// Get query parameters using Mux
	vars := mux.Vars(r)

	// Convert {id} parameter from string to int
	key, err := strconv.Atoi(vars["id"])

	// If {id} parameter is not valid in
	if err != nil {
		respondWithError(w, http.StatusBadRequest, "Invalid reqest payload")
		return
	}

	// If item with ID of {id} does not exist
	if key >= len(itemList) {
		respondWithError(w, http.StatusNotFound, "Item does not exist")
		return
	}

	respondWithJson(w, http.StatusOK, itemList[key])
}

func respondWithError(w http.ResponseWriter, code int, msg string) {
	respondWithJson(w, code, map[string]string{"error": msg})
}

func respondWithJson(w http.ResponseWriter, code int, payload interface{}) {
	response, _ := json.Marshal(payload)
	w.Header().Set("Content-type", "application/json")
	w.WriteHeader(code)
	w.Write(response)
}

func handleRequests() {
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	myRouter.HandleFunc("/items", returnAllItems)
	myRouter.HandleFunc("/items/{id}", returnSingleItem)
	log.Fatal(http.ListenAndServe(":8000", myRouter))
}

func main() {
	handleRequests()
}

http://localhost:8000/ → ホームページ、テキスト("This is the home page. Welcome!")を見せる

http://localhost:8000/items → "[{"title":"Item A","description":"The first item"},{"title":"Item B","description":"The second item"},{"title":"Item C","description":"The third item"}]"

http://localhost:8000/items/1 → {"title":"Item B","description":"The second item"}

なるほど、こんな感じで使うのね。

ーーーーー

jsonの使い方についても、Go言語でJSONを扱う を写経させて頂いた。

vro.json

[
    {"id":1, "name":"akane","birthday":"08-16","vivid_info":{"color":"red","weapon":"Rang"}},
    {"id":2, "name":"aoi","birthday":"06-17","vivid_info":{"color":"blue","weapon":"Impact"}},
    {"id":3, "name":"wakaba","birthday":"05-22","vivid_info":{"color":"green","weapon":"Blade"}},
    {"id":4, "name":"himawari","birthday":"07-23","vivid_info":{"color":"yellow","weapon":"Collider"}}, 
    {"id":0, "name":"rei"}    
]

vro.go

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
)

/** JSONデコード用の構造体定義 */

type Person struct {
	Id       int      `json:"id"`
	Name     string   `json:"name"`
	Birthday string   `json:"birthday"`
	Vivid    struct { // 構造体の中にネストさせて構造体を定義
		Color  string `json:color`
		Weapon string `json:weapon`
	} `json:"vivid_info"`
}

func main() {
	// JSONファイル読み込み
	bytes, err := ioutil.ReadFile("vro.json")
	if err != nil {
		log.Fatal(err)
	}
	// JSONデコード
	var persons []Person
	if err := json.Unmarshal(bytes, &persons); err != nil {
		log.Fatal(err)
	}
	// デコードしたデータを表示
	for _, p := range persons {
		fmt.Printf("%d : %s : (%s) [%s]\n", p.Id, p.Name, p.Vivid.Color, p.Birthday)
	}

	// JSONデコード
	var decode_data interface{}
	if err := json.Unmarshal(bytes, &decode_data); err != nil {
		log.Fatal(err)
	}
	// 表示
	for _, data := range decode_data.([]interface{}) {
		var d = data.(map[string]interface{})
		fmt.Printf("%d : %s\n", int(d["id"].(float64)), d["name"])
	}
}

2020/08,江端さんの忘備録

『フィクションにしても、ちょっと(私には)合わないなぁ』と思って、視聴してはいませんが、「恋人をレンタルする」というテーマのアニメがあるようです。

I thought "Even if it's fictional, it's not quite (for me)" about an anime about "renting a lover". so I haven't watched it.

しかし、そのコストや業務形態には興味がありました。

However, I was interested in the costs and business model.

でもって、ちょっと調べてみたら、出るわ出るわ、山程の「レンタル彼女」「レンタル彼氏」の紹介サイト。

But when I did some research, I found a lot of sites that introduce "rental girlfriends" and "rental boyfriends".

正直、ちょっと引いてしまう程でした。

That kind of site was a real turnoff.

-----

しかし、考えてみれば、「レンタル彼女/彼氏」は、普通にビジネスとして成立するはずで、別段、驚くことでもないはずです。

But thinking it again, "rental girlfriend/boyfriend" should be a normal business, and it shouldn't be a surprise to me.

接待を伴う飲食業の「店舗外バージョン」です。

This is the "out-of-store" version of the restaurant business with entertainment.

「デートできればそれで足る」というニーズに対して、Win-Winのリソース活用(金と時間の交換)とも言えます。

It can be seen as a win-win resource utilization (exchange of money and time) for the needs of "If I can date, it's good enough".

これは「デートのクラウド化」であり、ITの世界では当たり前の「リソースシェアリング」です。

This is the "cloud of dating" and "resource sharing" that is commonplace in the IT world.

その他のメリットを上げれば、(1)デートのフィールドトライアル、(2)本番デートの前のチェックリストやデバッグ、(3)孤食の回避、(4)服飾系のコンサルタント、等々。

Other benefits include (1) field trials for dates, (2) checklists and debugging before the real date, (3) avoidance of isolation, (4) clothing consultants, etc.

それ以外では、(A)結婚圧力の強い環境(肉親、地域、組織(会社))、(B)同性愛等の無理解な環境、あるいは(C)「一人ぼっちは体裁が悪い」と考えている人にとっては、一種の「擬態」としても有効なのかもしれません

Otherwise, it may be useful as a kind of "mimicry" for people who (a) are in an environment of strong marriage pressure (immediate family, community, organization (company)), (b) are in an environment where there is no understanding of homosexuality, etc., or (c) think that "being alone is bad for their appearance"

―― 知らんけど。

I don't know them well.

(続く)

(To be continued)

2020/08,江端さんの技術メモ

  1. まずは、MSYS2をメンテナンス(2年近く放置していたから)
$ pacman -Syu

を、ターミナルを何度も立ち直し続けてパッケージの更新を繰返す。

$ pacman -S base-devel
$ pacman -S msys2-devel
$ pacman -S mingw-w64-x86_64-toolchain
$ pacman -S mingw-w64-x86_64-gnutls
$ pacman -S mingw-w64-x86_64-ruby
$ pacman -S nano      #(簡易エディター)
$ pacman -S make      #(make をインストール ※重要※)
$ pacman -S openssh   #(openssh をインストール)
$ pacman -S git       #(Git をインストール)
$ pacman -S ruby      #(Ruby をインストール)
$ pacman -S ruby-docs #(Rubyドキュメント をインストール)
$ pacman -S p7zip     #(7z をインストール)
$ pacman -S mingw-w64-x86_64-ag  #(ag 高速検索コマンドをインストール)

2. パッケージのインストールとアンインストール

sudo pacman -S [パッケージ名]
sudo pacman -R [パッケージ名]

3. Windows10の環境をMSYS2に引きつぐ方法

2020/08,江端さんの技術メモ



go func() { defer close(done) for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } log.Printf("recv: %s", message) } }() ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-done: return case t := <-ticker.C: err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) if err != nil {

go func()で、defer close(done)が効いてくるまで、 case <-done:はロックされて、デッドロックになるじゃないか? と、ずっと考えて訳が分からなくなってきたところで、Go言語でチャネルとselect というページに、

チャネルに値が入っていない場合、受信はブロックする。ブロックせずに処理を行いたい場合は select を使う。

そんなSwitchの使いかた、あるかーーーー! と、叫びそうになりました(私の2時間を返せ)

2020/08,江端さんの忘備録

「やはり俺の青春ラブコメは間違っている完」の第8話の視聴後感想になります。

This is my impression after watching episode 8 of "My Youth Romantic Comedy is Wrong,As I Expected -- Final"

実は、今回のエピソードが、これまでで、一番私にショックを与えています。

Actually, this episode has shocked me the most, so far.

-----

私、ティーンエイジャの頃、生徒会役員とか、文化祭実行委員などもやっていました。

When I was a teenager, I was a student council member and a member of the school festival committee.

各種の企画を通す為であれば、生徒会会長や、実行委員長の職権を利用して、裏側で色々なことを画策していました。

In order to get the various projects passed, I was planning many things behind the scenes, using the authority of the student council president and the chairman of the executive committee.

企画を通す為に、正規のルートを通さない「根回し」や「裏工作」もしてきたと思います。

I think I've done some "diggint route root" and "behind-the-scenes maneuvering" that didn't go through regular channels if it was to get the project through.

で、まあ、この私の性格(狭量、浅学、卑怯)は、今に至るまで直っていませんが。

And, well, this character of mine (narrow, shallow, and cowardly) has not been fixed to this day.

ちなみに、私の価値観の根幹は「本物」でもなく「正道」でもなく「完了」です ―― が、まあ、その話はいずれまた。

By the way, my core values are not "real" or "righteous" but "complete" -- but, well, I'll talk about that soon.

-----

今回の第8話を視聴して、私が、今、ゾッとしていることは、私の、ティーンエイジャの頃、いい気になってやっていた「根回し」や「裏工作」を、

After watching this episode 8, about "diggint route root" and "behind-the-scenes maneuvering" I had done when I was a teenager,

当時、私を監修している大人たちからは、

At the time, the adults supervising me had

―― 丸見え

"seen them all"

だったのではないか、ということです。

I doubt they had thought

■『あいつ(江端)が何をやっているのか分かっているが、ほっとけば、企画をそこそこのレベルに持ち上げて、完了させるだろう』

"I know what he (Ebata) is doing, but if we leave him alone, he'll lift the project to that level and get it done."

■『本当に、ヤバくなれば、あいつ(江端)1人を潰せば、企画も潰せる』

"If things get really bad, we can destroy the project if we destroy him alone"

と思われて、

私の「根回し」や「裏工作」は、看過され続けていたのではないか?

Didn't my "diggint route root" and "behind-the-scenes maneuvering" continue to be overlooked?

私はいい気になって、「完了」できたことに、一人悦に入っていたのではないか?

Didn't I feel good about it, and I was alone in my euphoria at being able to "complete" it?

今、思い返せば、そういう例もいくつかあったような気がします。

Now that I think about it, I think there were a few examples of that.

-----

私のティーンエイジャとは、

My teenagers may bave been just a

『大人の掌の上で踊らされ続けて、それすら気が付つかなかった"頭の悪い子ども"』

"stupid child who has been dancing on the hands of adults and doesn't even know it"

に過ぎなかったのかもしれない。

-----

嫌なことに気が付いてしまったなぁ ―― と、番組の視聴後に思いました。

After watching the episode 8, "I just found something I didn't like".

2020/08,江端さんの技術メモ

https://github.com/gorilla/websocket/tree/master/examples/echo

この"server.go"は、client.goからの通信だけではなく、ブラウザの画面も提供します(スゴい!)。

というか、Goプログラムの中に、html書けるなんて知らんかった。

 
// server.go

// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore
// +build ignore

package main

import (
	"flag"
	"html/template"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var addr = flag.String("addr", "localhost:8080", "http service address")

var upgrader = websocket.Upgrader{} // use default options

func echo(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()
	for {
		mt, message, err := c.ReadMessage()
		if err != nil {
			log.Println("read:", err)
			break
		}
		log.Printf("recv: %s", message)
		err = c.WriteMessage(mt, message)
		if err != nil {
			log.Println("write:", err)
			break
		}
	}
}

func home(w http.ResponseWriter, r *http.Request) {
	homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}

func main() {
	flag.Parse()
	log.SetFlags(0)
	http.HandleFunc("/echo", echo)
	http.HandleFunc("/", home)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>  
window.addEventListener("load", function(evt) {

    var output = document.getElementById("output");
    var input = document.getElementById("input");
    var ws;

    var print = function(message) {
        var d = document.createElement("div");
        d.textContent = message;
        output.appendChild(d);
        output.scroll(0, output.scrollHeight);
    };

    document.getElementById("open").onclick = function(evt) {
        if (ws) {
            return false;
        }
        ws = new WebSocket("{{.}}");
        ws.onopen = function(evt) {
            print("OPEN");
        }
        ws.onclose = function(evt) {
            print("CLOSE");
            ws = null;
        }
        ws.onmessage = function(evt) {
            print("RESPONSE: " + evt.data);
        }
        ws.onerror = function(evt) {
            print("ERROR: " + evt.data);
        }
        return false;
    };

    document.getElementById("send").onclick = function(evt) {
        if (!ws) {
            return false;
        }
        print("SEND: " + input.value);
        ws.send(input.value);
        return false;
    };

    document.getElementById("close").onclick = function(evt) {
        if (!ws) {
            return false;
        }
        ws.close();
        return false;
    };

});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server, 
"Send" to send a message to the server and "Close" to close the connection. 
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="Hello world!">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output" style="max-height: 70vh;overflow-y: scroll;"></div>
</td></tr></table>
</body>
</html>
`))
// client.go

// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore
// +build ignore

package main

import (
	"flag"
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/gorilla/websocket"
)

var addr = flag.String("addr", "localhost:8080", "http service address")

func main() {
	flag.Parse()
	log.SetFlags(0)

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
	log.Printf("connecting to %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer close(done)
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				log.Println("read:", err)
				return
			}
			log.Printf("recv: %s", message)
		}
	}()

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-done:
			return
		case t := <-ticker.C:
			err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
			if err != nil {
				log.Println("write:", err)
				return
			}
		case <-interrupt:
			log.Println("interrupt")

			// Cleanly close the connection by sending a close message and then
			// waiting (with timeout) for the server to close the connection.
			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			return
		}
	}
}

2020/08,江端さんの忘備録

私は、一人で仕事をするのが好きです。

I like to work alone.

ですから、コラムの執筆は、私の性癖にあっているようです。

So, writing the column seems to be in my nature.

しかし、現在のコラムの執筆に関しては ――

But as for writing the current column,

編集担当者のMさん、量子コンピュータの監修者のTさん、そして、いわゆる「無礼な後輩」の御三方の支援なくしては、成立しません。

It wouldn't be possible without the support of Ms. M. as the editor, Mr. T as the supervisor of quantum computer, and the three so-called "rude junior".

ところが、Mさんを除いては、無償でご協力頂いている(クレジットの表示もご辞退されています)ので、大変心苦しいと思っています。

However, with the exception of Mr. M., I am very distressed by the fact that they has cooperated with me free of charge (and have declined to show credit).

だから、監修や査読のお願いに対して、締切等を申し上げられない立場です。

So it is hard for me ask them for deadline about supervision and peer review

-----

今回、「無礼な後輩」に対して、3回ほど、立て続けにフォローメールをしたところ、

This time, I sent three follow-up emails in a row to the "rude junior, and the call came back and

開口一番、

His first words are

『江端さん、嫌がらせですか』

"Ebata-san, are you harassing me?"

と、電話がかかってきました。

-----

そんな、ボランティアでご協力頂いている恩人に、事もあろうに『嫌がらせ』なんて、そんな不遜な気持ち ――

I don't like the idea of "harassment" for a benefactor who has volunteered to help me. Such irreverence is

「半分くらい」

"only the half".

しか、ありません。