ビルトインパッケージ@Go¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
パッケージのコード¶
bytes¶
Buffer
関数¶
渡された文字列を結合し、標準出力に出力する。
*実装例*
package main
import (
"bytes"
"fmt"
)
func main() {
var buffer bytes.Buffer
buffer.WriteString("Hello ")
buffer.WriteString("world!")
log.Printf("%v", buffer.String()) // "Hello world!"
}
context¶
contextとは¶
タイムアウト時間を設定し、またタイムアウトをすぎた場合に処理をキャンセルする。
また、複数の関数に渡って処理の情報 (タイムアウト時間、タイムアウトに関連するキャンセルシグナル、リクエストスコープなど) を伝達する。
type Context interface {
// タイムアウト時間
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
// コンテキストの情報
Value(key interface{}) interface{}
}
Context¶
▼ Value¶
コンテキストから値を取得する。
どんな値を設定しても良いが、プロセスやAPIを渡り歩くリクエストスコープの値を設定することが多い。
WithValue
関数でキー名はユーザー定義型を使用しているはずなので、取得する時もこれをキー名と指定する。
もちろん、ユーザー定義型以外でキー名を指定しても、キー名は不一致になる。
package server
import (
"context"
"log"
)
// コンテキストキー名はプリミティブ型ではなくユーザー定義型を使用する
type contextKey string
const (
Foo contextKey = "Foo"
)
func fooHandler(ctx context.Context) {
// キー名を指定して値を取得する
val, ok := ctx.Value(Foo).(string)
log.Print("val: %v, ok: %v", val, ok)
}
- https://zenn.dev/hsaki/books/golang-context/viewer/value#%E3%81%BE%E3%81%A8%E3%82%81-%26-%E6%AC%A1%E7%AB%A0%E4%BA%88%E5%91%8A
- https://zenn.dev/hsaki/books/golang-context/viewer/appliedvalue#value%E3%81%A8%E3%81%97%E3%81%A6%E4%B8%8E%E3%81%88%E3%81%A6%E3%82%82%E3%81%84%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF%E3%83%BB%E4%B8%8E%E3%81%88%E3%82%8B%E3%81%B9%E3%81%8D%E3%81%A7%E3%81%AA%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF
▼ WithValue¶
コンテキストに値を設定する。
どんな値を設定しても良いが、プロセスやAPIを渡り歩くリクエストスコープの値を設定することが多い。
package server
import (
"context"
)
// コンテキストキー名はプリミティブ型ではなくユーザー定義型を使用する
type contextKey string
const (
Foo contextKey = "Foo"
)
func fooHandler(ctx context.Context) {
...
ctx = context.WithValue(ctx, Foo, "<値>")
...
}
- https://zenn.dev/hsaki/books/golang-context/viewer/value#%E3%81%BE%E3%81%A8%E3%82%81-%26-%E6%AC%A1%E7%AB%A0%E4%BA%88%E5%91%8A
- https://zenn.dev/hsaki/books/golang-context/viewer/appliedvalue#value%E3%81%A8%E3%81%97%E3%81%A6%E4%B8%8E%E3%81%88%E3%81%A6%E3%82%82%E3%81%84%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF%E3%83%BB%E4%B8%8E%E3%81%88%E3%82%8B%E3%81%B9%E3%81%8D%E3%81%A7%E3%81%AA%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF
キー名は、プリミティブ型以外を設定しないと、エラーになる。
# string型のキー名を設定した場合
should not use built-in type string as key for value; define your own type to avoid collisions
WithDeadline¶
▼ WithDeadlineとは¶
コンテキストにタイムアウトの時刻を設定する。
WithTimeout¶
▼ WithTimeoutとは¶
コンテキストにタイムアウト時間を設定する。
リクエスト/レスポンスを宛先に送信できず、タイムアウトになった場合、context deadline exceeded
のエラーを返却する。
タイムアウト時間を設定しない場合、タイムアウトはせず、無限に待機する。
*実装例*
package main
import (
"context"
"fmt"
"time"
)
func main() {
// タイムアウト時間を設定し、コンテキストを作成する
ctx, cancel := context.WithTimeout(
context.Background(),
// タイムアウト時間を設定する
5 * time.Second,
)
// タイムアウト時間経過後に処理を中断する
defer cancel()
select {
// 先に終了したcaseに条件分岐する
case <-neverReady:
fmt.Println("ready")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
}
*実装例*
タイムアウト時間がすでに設定された既存コンテキストを使用した場合、タイムアウト時間のみを上書きする。
package main
import (
"context"
"fmt"
"time"
)
func main() {
// タイムアウト時間を設定し、コンテキストを作成する
ctx1, _ := context.WithTimeout(
context.Background(),
// タイムアウト時間を設定する
5 * time.Second,
)
ctx2, cancel := context.WithTimeout(
ctx1,
// タイムアウト時間を上書きする
10 * time.Second,
)
// タイムアウト時間経過後に処理を中断する
defer cancel()
select {
// 先に終了したcaseに条件分岐する
case <-neverReady:
fmt.Println("ready")
case <-ctx.Done():
fmt.Println(ctx2.Err()) // prints "context deadline exceeded"
}
}
*実装例*
package main
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
// タイムアウト時間を設定し、コンテキストを作成する
ctx, cancel := context.WithTimeout(
context.Background(),
// タイムアウト時間を設定する
5 * time.Second,
)
// タイムアウト時間経過後に処理を中断する
defer cancel()
req, err := http.NewRequest("GET", "http://localhost:8080/example", nil)
if err != nil {
panic(err)
}
req = req.WithContext(ctx)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
// context deadline exceeded エラーになる
fmt.Println("Request error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println("Response:", string(body))
}
伝達のユースケース¶
▼ Goroutine間での伝達¶
伝達できる情報¶
▼ タイムアウト時間¶
タイムアウト時間を伝播できる。
package main
import (
"context"
"fmt"
"time"
)
func fn1(ctx context.Context) {
log("start fn1")
defer log("done fn1")
for i := 1; i <= 4; i++ {
select {
// cancel関数が実行された場合
case <-ctx.Done():
return
default:
log("loop fn1")
time.Sleep(1 * time.Second)
}
}
}
func fn2(ctx context.Context) {
log("start fn2")
defer log("done fn2")
for i := 1; i <= 4; i++ {
select {
// cancel関数が実行された場合
case <-ctx.Done():
return
default:
log("loop fn2")
}
}
}
func log(timing string) {
log.Printf("%v second:%v", timing, time.Now().Second())
}
func main() {
log("start main")
defer log("done main")
// タイムアウト時間を設定し、コンテキストを作成する
ctx, cancel := context.WithTimeout(
context.Background(),
// タイムアウト時間を設定する
5 * time.Second,
)
// タイムアウト時間経過後に処理を中断する
defer cancel()
// タイムアウト時間をfn1に伝播する
go fn1(ctx)
// タイムアウト時間をfn1に伝播する
go fn2(ctx)
time.Sleep(5 * time.Second)
}
▼ キャンセル¶
Contextでは親子関係を設定できる。
先に作成したContextが親、後に作成したContextが子になる。
親コンテキストの処理をキャンセルすると、子コンテキストの処理も連鎖的にキャンセルできる。
package main
import (
"context"
)
func main() {
rootCtx := context.Background()
// 親コンテキストを作成する
parentCtx, parentCancel := context.WithCancel(rootCtx)
// 親コンテキストをパラメータとして、子コンテキストを作成する
childCtx, _ := context.WithCancel(parentCtx)
go func() {
parentCancel()
}()
...
}
▼ リクエストスコープ¶
セッションID、認証トークン、トレースコンテキストなどを伝達できる。
実際はどんな値を設定しても良いが、プロセスやAPIを渡り歩くリクエストスコープの値を設定することが多い。
そのため、多くのフレームワークではコンテキストをリクエストスコープの伝達に使用している。
// Ginのコンテキスト
type Context struct {
Request *http.Request
Writer ResponseWriter
Params Params
Keys map[string]any
Errors errorMsgs
Accepted []string
}
- https://zenn.dev/hsaki/books/golang-context/viewer/appliedvalue#value%E3%81%A8%E3%81%97%E3%81%A6%E4%B8%8E%E3%81%88%E3%81%A6%E3%82%82%E3%81%84%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF%E3%83%BB%E4%B8%8E%E3%81%88%E3%82%8B%E3%81%B9%E3%81%8D%E3%81%A7%E3%81%AA%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF
- https://pkg.go.dev/github.com/gin-gonic/gin#Context
encoding/json¶
Marshal
関数¶
構造体をJSONに変換する。
変換前に、マッピングを実行するようにする。
引数のデータ型は、ポインタ型または非ポインタ型のいずれでも問題ない。
ただし、他の多くの関数がポインタ型を引数型としていることから、それに合わせてポインタ型で渡すことが多い。
*実装例*
package main
import (
"encoding/json"
"fmt"
"log"
)
type Person struct {
// Marshalに渡す構造体のフィールドはパブリックが必須
Name string `json:"name"`
}
func main() {
person := &Person{Name: "Hiroki"}
// ポインタ型と非ポインタ型の両方の引数に対応
byteJson, err := json.Marshal(person)
if err != nil {
log.Printf("ERROR: %v", err)
}
// エンコード結果を出力
log.Printf("%v", string(byteJson)) // "{\"Name\":\"Hiroki\"}"
}
この時、構造体のフィールドはパブリックにする必要がある。
しかし、MarshalJSON
関数を構造体に定義すると、Marshal
関数の代わりにこれがコールするようになる。
構造体にゲッターを用意して、MarshalJSON
関数でパブリックな構造体を作成すると、プライベートな構造体に対してもMarshal
関数を使用できるようになる。
package main
import (
"encoding/json"
"fmt"
"log"
)
type Person struct {
name string
}
func NewPerson(name string) *Person {
return &Person{
name: name,
}
}
func (p *Person) Name() string {
return p.name
}
func (p *Person) MarshalJSON() ([]byte, error) {
byteJson, err := json.Marshal(&struct {
// ここでjsonタグを定義する。
Name string `json:"name"`
}{
Name: p.Name(),
})
return byteJson, err
}
func main() {
person := NewPerson("Hiroki")
byteJson, err := json.Marshal(person)
if err != nil {
log.Printf("Failed to marshal: %v", err)
}
// エンコード結果を出力
log.Printf("%v", string(byteJson)) // "{\"Name\":\"Hiroki\"}"
}
Unmarshal
関数¶
JSONを構造体に変換する。
リクエストの受信によく使われる。
リクエストのメッセージボディにはバイト型データが割り当てられているため、Unmarshal
関数の第一引数はバイト型になる。
また、第二引数として、変換後の構造体のメモリアドレスを渡すことにより、第一引数がその構造体に変換される。
内部的には、そのメモリアドレスに割り当てられている変数を書き換えている。
Unmarshal
関数に渡す構造体のフィールドはパブリックが必要であるが、Marshal
関数と同様にして、UnMarshalJSON
関数を構造体に定義すれば、代わりにこれをコールできる。
*実装例*
package main
import (
"encoding/json"
"log"
)
type Person struct {
// Unmarshalに渡す構造体のフィールドはパブリックが必須
Name string
}
func main() {
// リクエストを受信した場合を想定する。
byte := []byte(`{"name":"Hiroki"}`)
var person Person
log.Printf("%v", person) // main.Person{Name:""} (変数はまだ書き換えられていない)
// person変数を変換後の値に書き換えている。
err := json.Unmarshal(byteJson, &person)
if err != nil {
log.Printf("ERROR: %v", err)
}
log.Printf("%v", person) // main.Person{Name:"Hiroki"} (変数が書き換えられた)
}
RawMessage
関数¶
JSONから構造体にパースするためにUnmarshal
関数を実行した時に、部分的にパースせずにJSONのまま取得できる。
*実装例*
CloudWatchは様々なイベントを処理するため、一部のJSON構造が動的に変化する。
そのため、RawMessage
関数が使用されている。
package events
import (
"encoding/json"
"time"
)
type CloudWatchEvent struct {
Version string `json:"version"`
...
Resources []string `json:"resources"`
// 動的に変化するJSON構造
Detail json.RawMessage `json:"detail"`
}
イベントのJSONを文字列のまま取得できる。
package handler
import "fmt"
/**
* Lambdaハンドラー関数
*/
func HandleRequest(event events.CloudWatchEvent) (string) {
return log.Printf("%v", event.Detail)
}
Indent
関数¶
渡されたJSONにインデントを挿入する。
タブを挿入する場合は『\t
』、空白2つを挿入する場合は『』を設定する。
標準出力に出力すると、整形されたJSONを確認できる。
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
)
type Objects struct {
Id int
Name string
}
func main() {
objects := []Objects{
{1, "Hiroki"},
{2, "Hiroko"},
{3, "Hiroshi"},
}
byteJson, err := json.Marshal(objects)
if err != nil {
log.Print(err)
}
var buf bytes.Buffer
// インデント (タブ、空白) を挿入する。
json.Indent(&buf, byteJson, "", "\t")
// json.Indent(&buf, byteJson, "", " ")
fmt.Println(buf.String())
}
/* 結果
[
{
"Id": 1,
"Name": "Hiroki"
},
{
"Id": 2,
"Name": "Hiroko"
},
{
"Id": 3,
"Name": "Hiroshi"
}
]
*/
flag¶
flagとは¶
記入中...
Parse¶
コマンドのオプションの値を解析する。
package main
import (
"flag"
"fmt"
)
func main() {
// オプションの値を解析する
flag.Parse()
fmt.Println(flag.Args())
}
$ go run main.go foo bar baz
[foo bar baz]
String¶
コマンドでユーザー定義のオプションを設定する。
package main
import (
"flag"
"fmt"
)
func main() {
kubeconfig = flag.String(
"kubeconfig",
filepath.Join(home, ".kube", "config"),
"(optional) absolute path to the kubeconfig file",
)
flag.Parse()
}
help
オプションで確認できる。
$ go run main.go --help
Usage of main.go:
-kubeconfig string
(optional) absolute path to the kubeconfig file (default "/Users/foo/.kube/config")
fmt¶
fmtとは¶
標準エラー出力に出力するlog
パッケージとは異なり、標準出力に設定したメッセージを出力する。
接頭接尾辞無し関数¶
接頭接尾辞の無い関数 (例:Print
関数、Sprint
関数、Fprint
関数など) が所属する。
複数の引数をスペースを挟んで繋ぐ。
*実装例*
package main
import "fmt"
func main() {
fmt.Print("Hello world!") // Hello world!
}
*実装例*
package main
import "fmt"
func main() {
// 複数の引数をスペースで挟んで繋ぐ
fmt.Print(1, 2, 3) // 1 2 3
}
ただし、引数のいずれかがstring値の場合、スペースが挿入されない。
package main
import "fmt"
func main() {
// いずれかが文字列
fmt.Print("Hello", "world!", 12345) // Helloworld!12345
}
また、連続で使用しても、改行が挿入されない。
package main
import "fmt"
func main() {
fmt.Print("Hello", "world!")
fmt.Print("Hello", "world!")
// Hello world!Hello world!
}
接頭辞S
関数¶
接頭辞にS
のある関数 (例:Sprint
関数、Sprintf
関数、Sprintln
関数など) が所属する。
接頭辞がF
やP
の関数とは異なり、処理結果を標準出力に出力せずに返却する。
標準出力に出力できる他の関数の引数として渡す必要がある。
*実装例*
package main
import "fmt"
func main() {
// Sprintは返却するだけ
fmt.Print(fmt.Sprint(1, 2, 3)) // 1 2 3
}
接尾辞ln
関数¶
接尾辞にln
のある関数 (例:Println
関数、Fprintln
関数、Sprintln
関数など) が所属する。
複数の引数をスペースを挟んで繋ぎ、最後に改行を挿入して結合する。
*実装例*
文字を連続で標準出力に出力する。
package main
import "fmt"
func main() {
fmt.Println("Hello", "world!")
fmt.Println("Hello", "world!")
// Hello world!
// Hello world!
}
接尾辞f
関数¶
渡された引数を、事前に定義したフォーマットにも基づいて結合する。
よく使用する識別子 | 標準出力に出力されるもの | 備考 |
---|---|---|
%s |
文字列またはsliceとして | |
%p |
ポインタとして | |
%v |
様々な型として | |
%+v |
フィールドを含む構造体として | データの構造を確認できるため、デバッグに有効である。 |
%#v |
Go構文として | データの構造を確認できるため、デバッグに有効である。 |
*実装例*
渡された引数を文字列として結合する
package main
import "fmt"
func main() {
fmt.Printf("String is %s", "Hello world!")
}
また、連続して使用しても、改行は挿入されない。
package main
import "fmt"
func main() {
fmt.Printf("String is %s", "Hello world!")
fmt.Printf("String is %s", "Hello world!")
// String is Hello world!String is Hello world!
}
*実装例*
渡された引数をポインタとして結合する。
package main
import "fmt"
type Person struct {
Name string
}
func main() {
person:= new(Person)
person.Name = "Hiroki"
fmt.Printf("Pointer is %p", person) // 0xc0000821e0
}
*実装例*
渡された複数の引数を文字列として結合する。
package main
import "fmt"
func main() {
var first string = "Hiroki"
var last string = "Hasegawa"
fmt.Printf("Im %s %s", first, last)
// Im Hiroki Hasegawa
}
hex¶
hexとは¶
値を16
進数にエンコード/デコードする。
EncodeToString¶
値を16
進数にエンコードする。
package main
import (
"encoding/hex"
"fmt"
)
func main() {
// Helloを16進数にエンコードする
encodedStr := hex.EncodeToString([]byte("Hello"))
// 48656c6c6f
fmt.Printf("%s\n", encodedStr)
}
log¶
logとは¶
標準出力に出力するfmt
パッケージとは異なり、標準エラー出力に設定したメッセージを出力する。
Goにはデフォルトで、ロギング用パッケージが用意されている。
接尾辞Print
関数¶
渡された値を標準出力に出力する。
*実装例*
成功をログに残す。
log.Print("〇〇 succeeded")
渡されたerror
インターフェースを標準出力に出力する。
if err != nil {
log.Printf("ERROR: %v", err)
}
接尾辞Fatal
関数¶
渡された値を標準出力に出力し、os.Exit(1)
を実行して、ステータス 1
で処理を完了する。
ただ、この仕様がわかりにくいため、os.Exit(1)
とlog.Printf
関数を別々に実行した方が良い。
*実装例*
渡されたerror
インターフェースを標準出力に出力する。
if err != nil {
// 内部でos.Exit(1)を実行する。
log.Fatalf("ERROR: %v", err)
}
接尾辞Panic
関数¶
渡された値を標準出力に出力し、予期せぬエラーが起きたと見なしてpanic
関数を実行する。
補足として、panic
関数によって、エラーメッセージ出力、スタックトレース出力、処理停止が行われる。
ただし、panic
ではビルドやアーティファクト実行のエラー時に完了ステータスのみを返却することがあり、その場合に何が原因でエラーが発生したのかわからないことがあるため、非推奨である (ビルド失敗の原因がわからずに時間を溶かした経験あり) 。
*実装例*
渡されたerror
インターフェースを標準出力に出力する。
if err != nil {
// panic関数を実行する。
log.Panicf("ERROR: %v", err)
}
net/http¶
httpパッケージとは¶
HTTPクライアントまたはWebサーバを提供する。
そのため、GoではNginxやApacheが不要である。
ただし、Goによるwebサーバーは機能が不十分である、そのため、NginxやApacheをWebサーバとして、GoをAppサーバとして使用した方が良い。
Request¶
▼ Context¶
受信したリクエストを持つコンテキストを取得する。
package server
import (
"http"
)
func fooHandler(w http.ResponseWriter, r *http.Request) {
...
ctx := r.Context()
...
}
▼ Header¶
HTTPヘッダーを操作する。
package server
import (
"http"
)
func main(w http.ResponseWriter, r *http.Request) {
...
ctx := r.Header.Get("<ヘッダーのキー名>")
...
}
Handler系¶
▼ Handler¶
ServeHTTP
関数の実装を強制するインターフェースである。
ServeHTTP
関数は、ResponseWriter
とRequest
を引数に持つ必要がある。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
▼ HandlerFunc¶
Handlerの実装である。
type HandlerFunc func(ResponseWriter, *Request)
Handler
の実装は、HandlerFunc
に型変換できる。
func FooMiddleware() func(http.Handler) http.Handler {
// Handlerインターフェースを実装する関数を定義する
fn := func(w http.ResponseWriter, r *http.Request) {
// 事前処理
// そのまま実装すると事前処理になる
// deferを使用すると事後処理になる
// 本来の処理
next.ServeHTTP(w, r)
}
// Handlerインターフェースの実装をHandlerFunc型に変換する
return http.HandlerFunc(fn)
}
Middleware¶
▼ ミドルウェア処理とは¶
コントローラーの処理前に実行するBeforeMiddlewareと、コントローラーとビューの処理後に実行するAfterMiddlewareがある。
▼ 認証系¶
*実装例*
HTMLをレスポンスとして返信するサーバ (http://127.0.0.1:8080
) を起動する。
package main
import (
"log"
"net/http"
)
// ミドルウェア処理として、Cookieヘッダーに "admin" を持つかをHandler処理前に検証する
func requireAdminCookie(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// そのまま実装しているため、事前処理になる
_, err := r.Cookie("admin")
if err != nil {
http.Error(w, "No admin cookie", http.StatusForbidden)
return
}
// 本来の処理
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func fooHandler(w http.ResponseWriter, r *http.Request) {
// HTMLをレスポンスとして返信する。
fmt.Fprintf(w, "<h1>Hello world!</h1>")
}
func main() {
mux := http.NewServeMux()
// Handler処理前にミドルウェア処理を実行する
mux.Handle("/admin", requireAdminCookie(http.HandlerFunc(fooHandler)))
if err := http.ListenAndServe(":8080", mux); err != nil {
log.Print(err)
}
}
▼ ロギング系¶
記入中...
▼ メトリクス系¶
記入中...
▼ リカバー系¶
HTTPの処理で起こったパニックを、Internal Server Error
として処理する。
package main
import (
"log"
"net/http"
)
// RecoverHttpMiddleware HttpHandlerのパニックをリカバーする
func RecoverHttpMiddleware() func(http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// deferを使用しているため事後処理
defer func() {
if err := recover(); err != nil && err != http.ErrAbortHandler {
log.Printf("Failed to handle http: %v", err)
// Internal Server Errorとして処理する
w.WriteHeader(http.StatusInternalServerError)
}
}()
// 本来の処理
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func fooHandler(w http.ResponseWriter, r *http.Request) {
// HTMLをレスポンスとして返信する。
fmt.Fprintf(w, "<h1>Hello world!</h1>")
}
func main() {
mux := http.NewServeMux()
// Handler処理前にミドルウェア処理を実行する
mux.Handle("/foo", RecoverHttpMiddleware(http.HandlerFunc(fooHandler)))
if err := http.ListenAndServe(":8080", mux); err != nil {
log.Print(err)
}
}
Get
関数¶
*実装例*
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
response, err := http.Get("https://example/api.com")
defer response.Body.Close()
if err != nil {
log.Print(err)
}
fmt.Println(response.Body)
}
Post
関数¶
*実装例*
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
)
type User struct {
id int `json:"id"`
name string `json:"name"`
}
// コンストラクタ
func NewUser(id int, name string) *User {
return &User{
id: id,
name: name,
}
}
func main() {
user := NewUser(1, "Hiroki")
byteJson, err := json.Marshal(user)
response, err := http.Post(
"http://foo-api.com", // URL
"application/json", // Content-Type
bytes.NewBuffer(byteJson), // メッセージボディ
)
defer response.Body.Close()
if err != nil {
log.Print(err)
}
fmt.Println(response.Body)
}
NewRequest
関数¶
*実装例*
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
)
type User struct {
id int `json:"id"`
name string `json:"name"`
}
// コンストラクタ
func NewUser(id int, name string) *User {
return &User{
id: id,
name: name,
}
}
func main() {
user := NewUser(1, "Hiroki")
byteJson, err := json.Marshal(user)
// リクエストを作成する。
request, err := http.NewRequest(
"POST", // HTTP関数
"https://example.api.com", // URL
bytes.NewBuffer(byteJson), // メッセージボディ
)
// ヘッダーを作成する。
request.Header.Set("Content-Type", "application/json") // Content-Type
// クライアントを作成する。
client := &http.Client{}
// リクエストを送信する。
response, err := client.Do(request)
defer response.Body.Close()
if err != nil || response.StatusCode != 200 {
log.Print(err)
}
// レスポンスのボディを取得する。
// 代わりに、httputil.DumpResponseを使用しても良い。
body, _ := ioutil.ReadAll(response.Body)
log.Println(string(body))
}
ListenAndServe
関数¶
サーバを起動する。
第一引数にサーバーのURL、第二引数にServeMux関数 (マルチプレクサ関数) を渡す。
第二引数にnil
を渡した場合、デフォルト引数としてhttp.DefaultServeMux
が渡される。
*実装例*
package main
import (
"net/http"
"log"
)
func main() {
err := http.ListenAndServe(":8080", nil)
// 以下でも同じ。
// http.ListenAndServe(":8080", http.DefaultServeMux)
if err != nil {
log.Print("Error ListenAndServe : ", err)
}
}
NewServeMux
関数¶
サーバーを起動するListenAndServe
関数に対して、自身で定義したServeMux関数を渡す場合、NewServeMux
関数を使用する必要がある。
これのHandleFunc
関数に対してルーティングと関数を定義する。
*実装例*
HTMLをレスポンスとして返信するサーバ (http://127.0.0.1:8080
) を起動する。
package main
import (
"log"
"net/http"
)
func fooHandler(w http.ResponseWriter, r *http.Request) {
// HTMLをレスポンスとして返信する。
fmt.Fprintf(w, "<h1>Hello world!</h1>")
}
func main() {
mux := http.NewServeMux()
// ルーティングと関数を設定する。
mux.HandleFunc("/", fooHandler)
// サーバを起動する。
err := http.ListenAndServe(":8080", mux)
if err != nil {
log.Print("Error ListenAndServe : ", err)
}
}
JSONをレスポンスとして返信するサーバ (http://127.0.0.1:8080
) を起動する。
package main
import (
"encoding/json"
"log"
"net/http"
)
type User struct {
Id int `json:"id"`
Name string `json:"name"`
}
// コンストラクタ
func NewUser(id int, name string) *User {
return &User{
Id: id,
Name: name,
}
}
func fooHandler(w http.ResponseWriter, r *http.Request) {
user := NewUser(1, "Hiroki")
byteJson, err := json.Marshal(user)
if err != nil {
log.Print(err)
}
// JSONをレスポンスとして返信する。
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(byteJson)
}
func main() {
mux := http.NewServeMux()
// ルーティングと関数を設定する。
mux.HandleFunc("/", fooHandler)
// サーバを起動する。
err := http.ListenAndServe(":8080", mux)
if err != nil {
log.Print(err)
}
}
os¶
Open
関数¶
ファイルをReadOnly状態にする。
package main
import (
"log"
"os"
)
func main() {
file, err := os.Open("filename.txt")
if err != nil {
log.Printf("ERROR: %v", err)
}
log.Printf("%v", file)
}
reflect¶
TypeOf
関数、ValueOf
関数¶
構造体からフィールド情報を取得する。
フィールドが複数ある場合は、要素番号の指定が必要になるため、事前に要素数を取得するようにしておく。
package main
import (
"fmt"
"reflect"
)
type Foo struct {
bar string
baz int
}
func main() {
foo := &Foo{bar: "BAR", baz: 1}
fields := reflect.TypeOf(*foo)
fmt.Println(fields)
values := reflect.ValueOf(*foo)
// 再帰的にフィールドと値を取得する
for i := 0; i < fields.NumField(); i++ {
fmt.Println("===", i, "===")
// フィールドを取得
field := fields.Field(i)
fmt.Println(field.Name)
// 値を取得
value := values.Field(i)
fmt.Println(field.Type)
fmt.Println(value)
}
}
/*
=== 0 ===
bar
string
BAR
=== 1 ===
baz
int
1
*/
signal¶
NotifyContext¶
プロセスのシグナル
package main
import (
"context"
"fmt"
"os"
"os/signal"
"time"
)
func main() {
// 割り込み処理を設定する
ctx, stop := signal.NotifyContext(
context.Background(),
// SIGTERMシグナル
syscall.SIGTERM,
// 中断シグナル
os.Interrupt,
// Killシグナル
os.Kill,
)
// 処理を終了する
defer stop()
...
}
strings¶
Builder
関数¶
渡された文字列を結合し、標準出力に出力する。
*実装例*
package main
import (
"fmt"
"strings"
)
func main() {
var builder strings.Builder
builder.WriteString("Hello ")
builder.WriteString("world!")
fmt.Println(builder.String()) // Hello world!
}