杨子鳄鱼 ● 外贸自建站

十年外贸建站经验,专注外贸独立站建设,欢迎咨询

0%

终有一天 我有属于我的天

Goのプログラミングでは、どんな型の引数でも受け取れる関数を作るのにinterface{}を使います。

1
2
3
4
5
6
func v(x interface{}){
fmt.Println(x)
}

// v(1) => 1
// v("abc") => abc

しかし、interface{}型な変数を、たとえばintを引数にとる関数に渡すと、エラーになります。

1
2
3
4
5
6
7
8
9
func main(){
var x interface{} = 1
fmt.Println( add2(x) )
//=> cannot use x (type interface {}) as type int in argument to add2: need type assertion
}

func add2(n int) int{
return n + 2
}

このようなことをしたい場合は、castするか、switch式を使うことで、型を変換して関数に渡すことができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main(){
var x interface{} = 1
var y interface{} = 5

// cast
if xi, ok := x.(int); ok{
fmt.Println( add2(xi) ) //=> 3
}

//switch
switch yi := y.(type){
case int:
// ここに入ってきた時は、yiの型はintとして扱われる
fmt.Println( add2(yi) ) //=> 5
case int64, int32, int8:
// ここに入ってきた時は、yiの型はint64, int32, int8のうち適切な型が選ばれている
}
}

func add2(n int) int{
return n + 2
}

パッケージ的なものを自作していると、

  • どんな型の変数も渡せる
  • 特定の型の関数を渡せる

ような関数が欲しくなることがあります。具体的には以下のように使える関数です

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func exec(
v interface{}, // value
f interface{}, // valueを引数に実行するfunc
) interface{} {
fv := reflect.ValueOf(f)
if fv.Kind() != reflect.Func{
panic("2'nd argument is not func.")
}
rv := reflect.ValueOf(v)
return fv.Call([]reflect.Value{rv})[0]
}

// 以下のように使える
exec(5, func(i int) int{ return i * ( i + 1) })
exec("Tom", func(s string) string{ return "hello! " + s })

// 第一引数と、第二引数の引数の型が違うと、buildはできるが実行時にエラーが起きる
exec("Tom", func(i int) int{ return i * ( i + 1) })

build時に、エラーを出してほしいのですが、なかなかそうもいかない…

https://qiita.com/umanoda/items/07887d33ef1155b26ed2

琴声何在 生死难猜 用一生去等待

文字列を結合する

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
a := "Hello"
b := a + " World"
fmt.Println(b)
}

繰り返し文字列を生成する

1
2
3
4
5
6
7
8
9
package main

import "fmt"
import "strings"

func main() {
s := "Hey "
fmt.Println(strings.Repeat(s, 3))
}

大文字・小文字に揃える

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"
import "strings"

func main() {
s := "I love GoLang"
fmt.Println(strings.ToUpper(s))
fmt.Println(strings.ToLower(s))
}

大文字と小文字の入れ替え

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"
import "strings"

func main() {
//うーん、かなりイマイチ
s := "i lOVE gOLANG"
sr := ""
for _, x := range s {
xs := string(x)
u := strings.ToUpper(xs)
l := strings.ToLower(xs)
if u == xs {
sr += l
} else if l == xs {
sr += u
} else {
sr += xs
}
}
fmt.Println(sr)
}

コマンドの実行結果を文字列に

1
2
3
4
5
6
7
8
9
package main

import "fmt"
import "os/exec"

func main() {
out, _ := exec.Command("date").Output()
fmt.Println(string(out))
}

複数行の文字列を作成する

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
s := `
This is a test.

GoLang, programming language developed at Google.
`
fmt.Println(s)
}

ヒアドキュメントの終端文字列をインデントする

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"
import . "github.com/MakeNowJust/heredoc/dot"

func main() {
s := D(`
This is a test.

GoLang, programming language developed at Google.
`)
fmt.Println(s)
}

複数行のコマンドの実行結果を文字列に設定する

exec.Command()を使います。 (echoはシェル組み込みコマンドのせいか実行されない。。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"
import "os/exec"

func main() {
s := D(`
date
echo "-----------------------------"
ps
`)
outs := ""
for _, s = range strings.Split(s, "\n") {
out, _ := exec.Command(s).Output()
outs += string(out)
}
fmt.Println(outs)
}

部分文字列を取り出す

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
s := "Apple Banana Orange"
fmt.Println(s[0:5]) // => Apple
fmt.Println(s[6:(6 + 6)]) // => Banana
fmt.Println(s[0:3]) // => App
fmt.Println(s[6]) // => 66
fmt.Println(s[13:19]) // => Orange
}

部分文字列を置き換える

(うーんいまいち。。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"
import "strings"

func main() {
s := "Apple Banana Orange"
ss := strings.Split(s, "")
srep := strings.Split("Vine ", "")
for i, _ := range srep {
ss[i] = srep[i]
}

fmt.Println(strings.Join(ss, ""))
}

文字列中の式を評価し値を展開する

intなら%dでもよいですが、型推定をよさげに行うのなら%vが使えます。

1
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
value := 123
fmt.Printf("value is %v\n", value)
}

文字列を1文字ずつ処理する

こちらによると、 string[n]だとbyte単位、rangeで回すとrune(utf-8文字)単位らしいです。

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
sum := 0
for _, c := range "Golang" {
sum = sum + int(c)
}
fmt.Println(sum)
}

文字列の先頭と末尾の空白文字を削除する

空白ならTrimSpace, 任意の文字でやりたければTrimでやれます。

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"
import "strings"

func main() {
s := " Hello, Golang! "
s = strings.TrimSpace(s)
fmt.Println(s)
}

文字列を整数に変換する (to_i)

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"
import "strconv"

func main() {
i := 1
s := "999"
si, _ := strconv.Atoi(s)
i = i + si
fmt.Println(i)
}

文字列を浮動小数点に変換する (to_f)

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"
import "strconv"

func main() {
s := "10"
sf, _ := strconv.ParseFloat(s, 64) //64 bit float
fmt.Println(sf)
}

8進文字列を整数に変換する

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"
import "strconv"

func main() {
s := "010"
so, _ := strconv.ParseInt(s, 8, 64) // base 8, 64bit
fmt.Println(so)
}

16進文字列を整数に変換する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

import "strconv"

func main() {
s := "ff" // 0xは含んではいけません。
sh, _ := strconv.ParseInt(s, 16, 64)
fmt.Println(sh)

s = "0xff" // 0xを含んでも良い
sh, _ = strconv.ParseInt(s, 0, 64)
fmt.Println(sh)

}

ASCII文字をコード値に(コード値をASCII文字に)変換する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import "fmt"

func main() {
s := "ABC"
fmt.Println(s[0])
fmt.Println(string(82))
}
```

### 文字列を中央寄せ・左詰・右詰する
```go
package main

import "fmt"
import "strings"

func main() {
//右詰、左詰めはformat文字列が対応しています。±で指定。
s := "Go"
fmt.Printf("%10s\n", s)
fmt.Printf("%-10s\n", s)

//センタリングはなさそうです。
l := 10
ls := (l - len(s)) / 2
cs := strings.Repeat(" ", ls) + s + strings.Repeat(" ", l-(ls+len(s)))
fmt.Println(cs)
}

“次”の文字列を取得する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import "fmt"

func main() {
fmt.Println(succ("9")) // => "10"
fmt.Println(succ("a")) // => "b"
fmt.Println(succ("AAA")) // => "AAB"
fmt.Println(succ("A99")) // => "B00"
fmt.Println(succ("A099")) // => "A100"
}

// 文字列を反転して返す
func reverse(s string) string {
ans := ""
for i, _ := range s {
ans += string(s[len(s)-i-1])
}
return string(ans)
}

// "次"の文字列を取得する
func succ(s string) string {
r := reverse(s)
ans := ""
carry := 1
lastLetter := string(r[0])
for i, _ := range r {
lastLetter = string(r[i])
a := lastLetter
if carry == 1 {
if lastLetter == "z" {
a = "a"
carry = 1
} else if lastLetter == "Z" {
a = "A"
carry = 1
} else if lastLetter == "9" {
a = "0"
carry = 1
} else {
if r[i] == 0 {
a = "1"
} else {
a = string(r[i] + 1)
carry = 0
}
}
}
ans += a
}
if carry == 1 {
if lastLetter == "9" {
ans += "1"
} else if lastLetter == "z" {
ans += "a"
} else if lastLetter == "Z" {
ans += "A"
}
}
return reverse(ans)
}

文字列を暗号化する

とりあえずMD5あたりの例を書いておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"
import "crypto/md5"
import "io"
import "bufio"

func main() {
h := md5.New()
io.WriteString(h, "hogehoge")

fmt.Print("input password >")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()

h2 := md5.New()
io.WriteString(h2, scanner.Text())

if h2.Sum(nil)[0] == h.Sum(nil)[0] {
fmt.Println("right")
} else {
fmt.Println("wrong")
}

}

文字列中で指定したパターンにマッチする部分を置換する

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"
import "strings"

func main() {
s := "Apple Banana Apple Orange"
s = strings.Replace(s, "Apple", "Pine", 1) // 最後の引数は回数
fmt.Println(s)
s = strings.Replace(s, "Apple", "Pine", -1) // <0で無制限
fmt.Println(s)
}

文字列中に含まれている任意文字列の位置を求める

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"
import "strings"

func main() {
s := "Apple Banana Apple Orange"
fmt.Println(strings.Index(s, "Apple")) // => 0
fmt.Println(strings.Index(s, "Banana")) // => 6
// 途中から検索する方法は無いのでスライスで渡す
fmt.Println(strings.Index(s[6:], "Apple") + 6) // => 13
// 後方検索はLastIndex()
fmt.Println(strings.LastIndex(s, "Apple")) // => 13
fmt.Println(strings.Index(s[:(len(s)-6)], "Apple")) // => 0
}

文字列の末端の改行を削除する

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"
import "strings"

func main() {
s := "Hello, Golang!\n"
s = strings.TrimRight(s, "\n")
fmt.Println(s)
}

カンマ区切りの文字列を扱う

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"
import "strings"

func main() {

s := "001,ASHITANI Tatsuji,Yokohama"
slice := strings.Split(s, ",")
for _, x := range slice {
fmt.Println(x)
}
}

任意のパターンにマッチするものを全て抜き出す

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"
import "regexp"

func main() {
s := "hoge:045-111-2222 boke:045-222-2222"
re, _ := regexp.Compile("(\\S+):([\\d\\-]+)")
ans := re.FindAllStringSubmatch(s, -1) // [マッチした全体,1個目のカッコ,2個目のカッコ,..]の配列
fmt.Println(ans)
}

漢字コードを変換する

下記はEUCの例です。 指定するコーディングは、EUCJP,ISO2022JP,ShiftJISのどれかです。
こちらでご指摘いただいた点を 修正しました。’EUCJP’は’japanese.EUCJP’と呼びます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package main

import "fmt"
import "golang.org/x/text/encoding/japanese"
import "golang.org/x/text/transform"
import "strings"
import "io"
import "os"
import "bytes"

func main() {
KconvFwrite() // ファイル書き込み
KconvFread() // ファイル読み出し
KconvToBuffer() // バッファに書き込み
KconvFromBuffer() // バッファから読み出し
}

// ファイル書き込み
func KconvFwrite() {
s := "漢字です" // UTF8
f, _ := os.Create("EUC.txt")
r := strings.NewReader(s)
w := transform.NewWriter(f, japanese.EUCJP.NewEncoder()) // Encoder->f
io.Copy(w, r) // r -> w(->Encoder->f)
f.Close()
}

// ファイル読み出し
func KconvFread() {
f, _ := os.Open("EUC.txt")
b := new(bytes.Buffer)
r := transform.NewReader(f, japanese.EUCJP.NewDecoder()) // f -> Decoder
io.Copy(b, r) // (f->Decoder)->b
fmt.Println(b.String())
f.Close()
}

// バッファに書き込み
func KconvToBuffer() {
s := "漢字です" // UTF8
b := new(bytes.Buffer)
r := strings.NewReader(s)
w := transform.NewWriter(b, japanese.EUCJP.NewEncoder()) // Encoder->f
io.Copy(w, r) // r -> w(->Encoder->f)

st := b.String()
for i := 0; i < len(st); i++ {
fmt.Println(st[i])
}
fmt.Println(b.String())

}

// バッファから読み出し
func KconvFromBuffer() {
str_bytes := []byte{180, 193, 187, 250, 164, 199, 164, 185}
s := bytes.NewBuffer(str_bytes).String() // "漢字です" in EUC

sr := strings.NewReader(s)
b := new(bytes.Buffer)
r := transform.NewReader(sr, japanese.EUCJP.NewDecoder()) // sr -> Decoder
io.Copy(b, r) // (sr->Decoder)->b
fmt.Println(b.String())
}

マルチバイト文字の数を数える

こちらが詳しいです。 len()だとbyteカウント、[]runeに変換するとutf-8カウント。

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
s := "日本語"
fmt.Println(len(s)) // => 9
fmt.Println(len([]rune(s))) // => 3
}

マルチバイト文字列の最後の1文字を削除する

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
s := "日本語"
sc := []rune(s)
fmt.Println(string(sc[:(len(sc) - 1)])) // => "日本"
}

https://ashitani.jp/golangtips/tips_string.html#string_Split

我害怕,看到你,独自一人绝望; 更害怕,看不到你,不能和你一起迷惘……

go言語の encoding/csv パッケージの使い方。
CSVファイルの読み書きと、オプション、エラーハンドリングについて書いてます。

一行ずつ読み込む(Read)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import (
"encoding/csv"
"fmt"
"io"
"log"
"strings"
)

func main() {
s := `名前,年齢,身長,体重
Tanaka,31,190cm,97kg
Suzuki,46,180cm,79kg
Matsui,45,188cm,95kg
`
r := csv.NewReader(strings.NewReader(s))

for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}

// recordは配列
fmt.Printf("%#v\n", record)

// []string{"名前", "年齢", "身長", "体重"}
// []string{"Tanaka", "31", "190cm", "97kg"}
// []string{"Suzuki", "46", "180cm", "79kg"}
// []string{"Matsui", "45", "188cm", "95kg"}
}
}

ファイルから読み込む

ファイルから読み込む場合は csv.NewReader*os.File を渡す

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"encoding/csv"
"log"
"os"
)

func main() {
f, err := os.Open("file.csv")
if err != nil {
log.Fatal(err)
}

r := csv.NewReader(f)

for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Println(record)
}
}

csv.Readerのオプション

Comma: 区切り文字を変更
Comment: コメント行の先頭になる文字を指定
FieldsPerRecord: 各行のフィールド数を指定
LazyQuotes: ダブルクオートを厳密にチェックするかどうか
TrimLeadingSpace: 先頭の空白文字を無視する
ReuseRecord: スライスの再利用
TrailingComma: Deprecated(非推奨)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import (
"encoding/csv"
"fmt"
"io"
"log"
"strings"
)

func main() {
s := `名前;年齢;身長;体重
# コメント行
Tanaka;31;190cm;97kg
# コメント行
Suzuki;46;180cm;79kg
# コメント行
Matsui;45;188cm;95kg
`

r := csv.NewReader(strings.NewReader(s))
r.Comma = ';' // 区切り文字を , から ; に変更
r.Comment = '#' // 先頭が # の場合はコメント行として扱う
r.FieldsPerRecord = 4 // 各行のフィールド数。多くても少なくてもエラーになる
r.LazyQuotes = true // true の場合、"" が値の途中に "180"cm のようになっていてもエラーにならない
r.TrimLeadingSpace = true // true の場合は、先頭の空白文字を無視する
r.ReuseRecord = true // true の場合は、Read で戻ってくるスライスを次回再利用する。パフォーマンスが上がる

for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}

// recordは配列
fmt.Printf("%#v\n", record)

// []string{"名前", "年齢", "身長", "体重"}
// []string{"Tanaka", "31", "190cm", "97kg"}
// []string{"Suzuki", "46", "180cm", "79kg"}
// []string{"Matsui", "45", "188cm", "95kg"}
}
}

エラーハンドリング(csv.ParseError)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
"encoding/csv"
"fmt"
"log"
"strings"
)

func main() {
s := `名前,年齢,身長,体重
Tanaka,31,190cm,97kg
Suzuki,46,180cm,79kg
Matsui,45,188cm,95kg
`
r := csv.NewReader(strings.NewReader(s))

records, err := r.ReadAll()
if err != nil {
if e, ok := err.(*csv.ParseError); ok {
n := 0
switch e.Err {
case csv.ErrBareQuote:
// ダブルクオート途中で使用されていて LazyQuotes を true にしていない場合のエラー
// 例えば、 Tan"aka,31,190cm,97kg のように 途中に " がある場合
n = 1
case csv.ErrQuote:
// 先頭がダブルクオートで始まっていて、末尾がダブルクオートになっていない場合のエラー
// 例えば、 "Tanaka,31,190cm,97kg のように閉じるための " がない場合
n = 2
case csv.ErrFieldCount:
// FieldsPerRecordで指定した数と異なる場合のエラー
n = 3
}
log.Fatal("\nエラー: ", n, "\n", e.Err,
"\nStartLine:", e.StartLine, "\nLine:", e.Line, "\nColumn:", e.Column)
}
log.Fatal(err)
}

fmt.Println(records)
}

読み込んだShift-JISファイルをUTF8にする

encoding/japanesetext/transform が必要なのでダウンロードする

1
$ go get golang.org/x/text/encoding/japanese golang.org/x/text/transform
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
"encoding/csv"
"fmt"
"log"
"os"

"golang.org/x/text/encoding/japanese"
"golang.org/x/text/transform"
)

func main() {
f, err := os.Open("file-sjis.csv")
if err != nil {
log.Fatal(err)
}

r := csv.NewReader(transform.NewReader(f, japanese.ShiftJIS.NewDecoder()))

for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}

fmt.Println(record)
}
}

一度にすべて読み込む(ReadAll)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"encoding/csv"
"fmt"
"log"
"strings"
)

func main() {
s := `名前,年齢,身長,体重
Tanaka,31,190cm,97kg
Suzuki,46,180cm,79kg
Matsui,45,188cm,95kg
`
r := csv.NewReader(strings.NewReader(s))

record, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}

fmt.Printf("%#v\n", record)
// [][]string{
// []string{"名前", "年齢", "身長", "体重"},
// []string{"Tanaka", "31", "190cm", "97kg"},
// []string{"Suzuki", "46", "180cm", "79kg"},
// []string{"Matsui", "45", "188cm", "95kg"},
// }
}

一行ずつ書き込む(Write)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
"encoding/csv"
"log"
"os"
)

func main() {
records := [][]string{
[]string{"名前", "年齢", "身長", "体重"},
[]string{"Tanaka", "31", "190cm", "97kg"},
[]string{"Suzuki", "46", "180cm", "79kg"},
[]string{"Matsui", "45", "188cm", "95kg"},
}

f, err := os.Create("file.csv")
if err != nil {
log.Fatal(err)
}

w := csv.NewWriter(f)

// オプション
w.Camma = ',' // デフォルトはカンマ区切りで出力される。変更する場合はこの rune 文字を変更する
w.UseCRLF = true // 改行文字を CRLF(\r\n) にする

for _, record := range records {
if err := w.Write(record); err != nil {
log.Fatal(err)
}
}

w.Flush() // バッファに残っているデータをすべて書き込む

if err := w.Error(); err != nil {
log.Fatal(err)
}
}

書き込み結果

1
2
3
4
5
$ cat file.csv
名前,年齢,身長,体重
Tanaka,31,190cm,97kg
Suzuki,46,180cm,79kg
Matsui,45,188cm,95kg

すべて一度に書き込む(WriteAll)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
"encoding/csv"
"log"
"os"
)

func main() {
records := [][]string{
[]string{"名前", "年齢", "身長", "体重"},
[]string{"Tanaka", "31", "190cm", "97kg"},
[]string{"Suzuki", "46", "180cm", "79kg"},
[]string{"Matsui", "45", "188cm", "95kg"},
}

f, err := os.Create("file.csv")
if err != nil {
log.Fatal(err)
}

w := csv.NewWriter(f)

w.WriteAll(records)

w.Flush()

if err := w.Error(); err != nil {
log.Fatal(err)
}
}

CSVを構造体にマッピングする

以下のように書いてみたけど、reflect 使うとさらに面倒そう。
なので gocsv (gocsvのgodocページ) を使えば、読み込んだCSVを構造体にマッピングできて、 その逆で構造体からCSVに変換するのも楽にできそう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package main

import (
"encoding/csv"
"fmt"
"io"
"log"
"strconv"
"strings"
)

type People struct {
Name string
Age int
Height string
Weight string
}

func main() {
s := `名前,年齢,身長,体重
Tanaka,31,190cm,97kg
Suzuki,46,180cm,79kg
Matsui,45,188cm,95kg
`
r := csv.NewReader(strings.NewReader(s))

var p []People

for i := 0; ; i++ {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
if i == 0 {
continue
}

var people People
for i, v := range record {
switch i {
case 0:
people.Name = v
case 1:
people.Age, err = strconv.Atoi(v)
if err != nil {
log.Fatal(err)
}
case 2:
people.Height = v
case 3:
people.Weight = v
}
}
p = append(p, people)
}

fmt.Println(p)
}

https://golang.hateblo.jp/entry/golang-encoding-csv

当我们搬开别人架下的绊脚石时,也许恰恰是在为自己铺路。

Basically, using laravel pipelines you can pass an object between several classes in a fluid way to perform any type of task and finally return the resulting value once all the “tasks” have been executed.

The most clear example about how pipelines works resides in one of the most used components of the framework itself. I’m talking about middlewares.

Middleware provide a convenient mechanism for filtering HTTP requests entering your application… This is how a basic middleware looks like:

1
2
3
4
5
6
7
8
<?php
app(Pipeline::class)
->send($content)
->through($pipes)
->via(‘customMethodName’) // <---- This one :)
->then(function ($content) {
return Post::create(['content' => $content]);
});

These “middlewares” are in fact just pipes by where the request is going to be sent thru, in order to perform any needed task. Here you can check if the request is an HTTP request, a JSON request, if there is any user authenticated, etc.

If you take a quick look to the Illuminate\Foundation\Http\Kernel class, you’ll see how the middlewares are executed by using a new instance of the Pipeline class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
/**
* Send the given request through the middleware / router.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
$this->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}

You can read in the code something like: a new pipeline that sends the request through a list of middlewares and then dispatch the router.

Don’t worry if this seems a little overwhelmed to you. Let’s try to clarify the concept with the follow example.

Working on a class that requires to run multiple tasks

Consider this situation. Let’s say you are building a forum where people can create threads and leave comments. But your client ask you to auto-remove tags or edit them on every piece of content when it’s created.

So this is what you are asked to do:

  • Replace link tags with plain text.
  • Replace bad words with “*”
  • Remove script tags entirely from the content

Probably you end up creating classes to handle each of these “tasks”.

1
2
3
4
5
6

$pipes = [
RemoveBadWords::class
ReplaceLinkTags::clas
RemoveScriptTags::class
];

What we are going to do is to pass the given “content” to each task and then return the result to the next one. We can do this using a pipeline.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
public function create(Request $request)
{
$pipes = [
RemoveBadWords::class,
ReplaceLinkTags::clas,
RemoveScriptTags::class
];
$post = app(Pipeline::class)
->send($request->content)
->through($pipes)
->then(function ($content) {
return Post::create(['content' => 'content']);
});
// return any type of response
}

Each “task” class should have a “handle” method to perform the action. Maybe it would be a good idea to have a contract to be implemented by each class:

1
2
3
4
5
6
7
<?php
namespace App;
use Closure;
interface Pipe
{
public function handle($content, Closure $next);
}
1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace App;
use Closure;
class RemoveBadWords implements Pipe
{
public function handle($content, Closure $next)
{
// Here you perform the task and return the updated $content
// to the next pipe
return $next($content);
}
}

The method used to perform the task should receive two parameters, the first one would be the passable object, and the second one would be a closure where the object is going to be redirected to after running the last pipe.

You can use a custom method name instead of ‘handle’. Then you need to specify the method name to be used by the pipeline, like so

1
2
3
4
5
6
7
8
<?php
app(Pipeline::class)
->send($content)
->through($pipes)
->via(‘customMethodName’) // <---- This one :)
->then(function ($content) {
return Post::create(['content' => $content]);
});

What happens at the end ?

What should happen here is that the post content is going to be modified by each one of the $pipes and at the end, this resulting content is going to be stored.

1
2
3
4
5
6
$post = app(Pipeline::class)
->send($request->all())
->through($pipes)
->then(function ($content) {
return Post::create(['content' => $content]);
});

Final words

Remember, there are tons of ways you can approach this type of issues. What you decide to do it’s up to you. But it is good to know that you have this tool in your arsenal to be used if necessary. I hope this example gives you a better understanding of what these “laravel pipelines” are and how to use them. You can also take a look at api laravel documents if you want to know more about how this

https://jeffochoa.me/understanding-laravel-pipelines

孤独的孩子,悄悄在风中长大了。

Suppose I’m building a web app in Go. In my app, I want to send a message to my users. I can send a message to the users via email or SMS. This would be a perfect use case for interfaces.

For this hypothetical web app, I create the following main.go file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

type User struct {
Name string
Email string
}

type UserNotifier interface {
SendMessage(user *User, message string) error
}

func main() {
user := &User{"Dirk", "dirk@email.com"}

fmt.Printf("Welcome %s\n", user.Name)
}

The User struct represents a user.
You can see I created a UserNotifier of type interface that has a single function: SendMessage().
To implement this interface, I have to create a struct that implements the SendMessage() function.

1
2
3
4
5
6
7
type EmailNotifier struct {
}

func (notifier EmailNotifier) SendMessage(user *User, message string) error {
_, err := fmt.Printf("Sending email to %s with content %s\n", user.Name, message)
return err
}

As you can see, I created a new EmailNotifier struct. Then I implemented the SendMessage() method on the struct. Note that in this example, the EmailNotifier simply prints a message. In the real world, you would probably call an email API such as Mailgun.

And that’s it, the interface is now implemented.

The next thing to do is send an email to the user using the UserNotifier interface.

1
2
3
4
5
6
7
8
func main() {
user := User{"Dirk", "dirk@email.com"}
fmt.Printf("Welcome %s\n", user.Name)

var userNotifier UserNotifier
userNotifier = EmailNotifier{}
userNotifier.SendMessage(&user, "Interfaces all the way!")
}

When running this program, the SendMessage() of the EmailNotifier is called correctly.

1
2
3
4
go build -o main main.go
./main
Welcome Dirk
Sending email to Dirk with content Interfaces all the way!

Let’s implement an SMS interface as well.

1
2
3
4
5
6
7
type SmsNotifier struct {
}

func (notifier SmsNotifier) SendMessage(user *User, message string) error {
_, err := fmt.Printf("Sending SMS to %s with content %s\n", user.Name, message)
return err
}

One cool feature is we can store the notifier in the user struct. In this way, each user will have a personal notifier.

1
2
3
4
5
type User struct {
Name string
Email string
Notifier UserNotifier
}

You can then add a handy method to the User struct that notifies the user using the UserNotifier interface.

1
2
3
func (user *User) notify(message string) error {
return user.Notifier.SendMessage(user, message)
}

Finally, I created two users in the main() function and called the notify() method.

1
2
3
4
5
6
7
func main() {
user1 := User{"Dirk", "dirk@email.com", EmailNotifier{}}
user2 := User{"Justin", "bieber@email.com", SmsNotifier{}}

user1.notify("Welcome Email user!")
user2.notify("Welcome SMS user!")
}

The final result worked as expected.

1
2
3
4
go build -o main main.go
./main
Sending email to Dirk with content Welcome Email user!
Sending SMS to Justin with content Welcome SMS user!

你爱上小溪 是因为没有见过大海 我已见过银河 但我只爱一颗星

问题

golang如何在一个平台编译另外一个平台可以执行的文件。比如在mac上编译Windows和linux可以执行的文件。那么我们的问题就设定成:如何在mac上编译64位linux的可执行文件。

解决方案

golang的交叉编译要保证golang版本在1.5以上,本解决方案实例代码1.9版本执行的。

hello.go

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Printf("hello, world\n")
}

在mac上编译64位linux的命令编译命令

1
GOOS=linux GOARCH=amd64 go build hello.go

参数解析

这里用到了两个变量:

  • GOOS:目标操作系统
  • GOARCH:目标操作系统的架构

扩展阅读

在网络上的诸多教程中可能会看到下面的编译命令

1
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build hello.go

其中CGO_ENABLED=0的意思是使用C语言版本的GO编译器,参数配置为0的时候就关闭C语言版本的编译器了。自从golang1.5以后go就使用go语言编译器进行编译了。在golang1.9当中没有使用CGO_ENABLED参数发现依然可以正常编译。当然使用了也可以正常编译。比如把CGO_ENABLED参数设置成1,即在编译的过程当中使用CGO编译器,我发现依然是可以正常编译的。

实际上如果在go当中使用了C的库,比如import “C”默认使用go build的时候就会启动CGO编译器,当然我们可以使用CGO_ENABLED=0来控制go build是否使用CGO编译器。

她曾那么可爱。然后一切都变了。

I’m a big fan of the Chrome Extension API. It is a simple (and cool) way to extend my favorite browser using HTML and JavaScript. One of the features I’ve been excited about for a while now is the DevTools API. This has been in “experimental” status for a while but is now (mostly) available in the main release.

The DevTools API allows you to integrate extensions directly into your DevTools. There are multiple samples of this API. But there aren’t any samples that detail how to create panels. There is documentation for creating panels, but it wasn’t clear how to use that API in a real extension. I dug at it for a while and finally figured it out. What follows is what I had to do to make panel creation work. It may not be the right way, but it worked. First, you must set a devtools_page value in your manifest.json. Here is my sample manifest.

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "ColdFire",
"version": "1.0.2",
"manifest_version": 2,
"description": "Adds ColdFire support.",
"devtools_page": "devtools.html",
"permissions": [
"tabs",
"http://*/*",
"https://*/*"
]
}

the critical part here is devtools_page. You specify a HTML file that will be executed every time the devtools UI is opened. Here is my HTML file:

1
2
3
4
5
6
7
8
<!doctype html>
<html>
<head>
<script src="devtools.js"></script>
</head>
<body>
</body>
</html>

Not much, right? As far as I can tell, the actual HTML for this file is never going to be rendered - anywhere. Instead, the entire point of this file is to load a JavaScript script. If so, it would be nice if Google would let us simply point devtools_page to a JavaScript script instead. Now let’s look at the script.

1
2
3
4
5
console.log("hello from devtools");
chrome.devtools.panels.create("ColdFire",
"coldfusion10.png",
"panel.html",
function(panel) { console.log("hello from callback"); });

The main part of this script - for now - is just one line. It runs the panels.create API to define a new panel. The first argument is the title. The second is the icon. The third is the HTML file for the panel. Finally, the last argument is a callback to execute when the panel is created. There is no documentation on what size the icon should be. It seems to be 32x32.

Now let’s look at panel.html. I’ve only just begun development so for right now it is pretty simple.

1
2
3
4
5
6
7
8
9
<html>
<head>
<script src="panel.js"></script>
</head>
<body>
<h2>ColdFire</h2>

</body>
</html>

And last but not least, the JavaScript file loaded by the panel:

1
console.log("panel.js");

So - what about all those console messages? As I tried to get things working, I used console messages all over the place so I could - hopefully - see what was going on. I had very little luck with this. I’ve blogged before about how to use console with Chrome Extensions, but that tip only works when you have a background script. My test extension did not. What finally worked was this:

Click the button that detaches Chrome Dev Tools
With the Chrome Dev Tools in its own window and currently focused, use the keyboard combo to open Chrome Dev Tools
This opens a second Chrome Dev Tools and I could see all my log messages there.

I hope this helps. As I said, I just got this working so I probably have a lot more to learn.

https://www.raymondcamden.com/2012/07/15/How-to-add-a-panel-to-Chrome-Dev-Tools

你如今的气质里,藏着你走过的路,读过的书,和爱过的人。

Webからエンタープライズアプリケーションまで、RESTfulというアーキテクチャーが使われています。ステートレス設計として、別々のソフトウェアコンポーネント間の通信を提供する強力な方法であります。 Web開発者として、RESTful APIを作るために、主にPythonを使いますが、最近Goプログラミング言語に出会い、興味を持ってきました。Goプログラミング言語(golangでも呼ばれる)は、Googleのプロジェクトで、マイクロサービスアーキテクチャに適当な多くの機能も提供するので、人気が増えています。

本記事、golangを使ってシンプルなRESTful APIをどう作るか説明したいと思います。

仕様

RESTful APIは、通常CRUDができます。CRUDとは、データベースの四つの主要な機能、「作成(Create)」「読み出し(Read)」「更新(Update)」「削除(Delete)」ですが、今回は、簡単にするために、読み出しの機能しか作りません。そして、データがJSONの形式で書きます。

そして、このエンドポイントがあります:

  • / -> ホームページですが、とりあえずテキストしかを見せない
  • /items/ -> Itemのリスト、Itemデータを全部見せる
  • /items/{id} -> 特定のItemの情報
    それぞれのItemは、「title」と「description」のデータがあって、簡単にするために、データベースを使用するのではなく、データを事前に定義します。

開発プロセス

最初は、必要なパッケージをインストールします。ルーティングのために、Muxというパッケージを使っていきます。CMD / Terminalを開けて、このコマンドを実行してください:

1
go get -u github.com/gorilla/mux

その後、main.goというファイルを作成して、このコードを入れてください:

1
2
3
4
5
6
7
8
9
package main

import (
"fmt"
"log"
"net/http"

"github.com/gorilla/mux"
)

すべてのgolangを使って作られたプログラムがパッケージで構成されています。この部分では、何のパッケージが必要かが書いてあります。この場合には、四つのパッケージが必要です。

ルーティング

同じファイルで、さっきの書いたコードの下に、このコードを入れてください:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Controller for the / route (home)
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is the home page. Welcome!")
}

// Controller for the /items route
func returnAllItems(w http.ResponseWriter, r *http.Request) {
// Code for returning all items here
}

// Controller for the /items/{id} route
func returnSingleItem(w http.ResponseWriter, r *http.Request) {
// Code for returning a single item here
}

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()
}

main()という関数は、このプログラムをスタートすると、自動的に実行されます。その中に、handleRequests()という関数が実行されます。handleRequests()の関数で、それぞれのエンドポイントがアクセスされると、何が起こるかが書いてあります。例えば、/のエンドポイントがアクセスされると、homePageという関数が実行されるとか、/itemsのエンドポイントがアクセスされると、returnAllItemsという関数が実行されます。

そして、このコード:

1
log.Fatal(http.ListenAndServe(":8000", myRouter))

の意味は、このプログラムが8000のポートで、アクセスできます。これで、もう使えるなので、スタートしてみましょう!

CMD / Terminal で、このコマンドを実行してください:

1
go run main.go

サポート関数

これから、二つのパッケージが必要なので、インポートしましょう。最初のimport部分をこれに変更してください:

1
2
3
4
5
6
7
8
9
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"

"github.com/gorilla/mux"
)

encoding/jsonとstrconvというパッケージを追加しました。もうすぐこの二つを使います。

それから、二つのサポート関数を書きましょう:

1
2
3
4
5
6
7
8
9
10
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)
}

両方の関数は、JSONの形式でHTTP requestに答える役割があります。

Itemデータ構造

Itemのデータ構造を書きましょう。importの部分の下に、このコードを入れてください:

1
2
3
4
5
// Item representation
type Item struct {
Title string `json:"title"`
Description string `json:"description"`
}

仕様に基づいて、それぞれのItemは、「title」と「description」のデータがあります。そして、その下に、これを入れて下さい:

1
2
3
4
5
// 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だけ書きましたが、自由にいくつでもいいですよ。

コア関数

最後に、コア関数(returnAllItemsとreturnSingleItem)を書きます。

1
2
3
4
// Controller for the /items route
func returnAllItems(w http.ResponseWriter, r *http.Request) {
respondWithJson(w, http.StatusOK, itemList)
}

基本的に、/itemsのエンドポイントをアクセスすると、returnAllItems()という関数がすべてのItemをJSONの形式で答えます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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 int
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request 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])
}

そして、returnSingleItem()という関数が、idのURLパラメーターに基づいて特定のItemの情報をJSONの形式で答えます。

例えば、localhost:8000/items/1をアクセスしたら、1がidになります。それから、そのidは、indexとしてitemListという配列からデータを取ってJSONの形式で答えます。idは1の場合は、二つ目のデータが取られます。そして、idは0の場合は、一つ目のデータが取られます。

idは、数だけであるべきであり、Itemの数以上になってはいけないので、二つのチェックを書きました。これで、終わりです。作ってみましょう!

テスト

CMD / Terminal で、さっきのrunを閉じて、もう一回実行してください:

1
go run main.go

https://qiita.com/sbenemerito/items/d94b811f14ce0a546a3f

幸有我来山未孤

使用场景

反向代理的概念不多做解释,此处应用反向代理主要原因如下:

  • 应用服务器不必暴露在公网,躲在防火墙后,通过内网连接代理服务器(proxy server)
  • DNS设置简单,因为有两个不同应用服务器,对应不同的子域名(a.example.com, b.example.com),只需设置DNS *.example.com 到代理服务器IP即可
  • SSL证书管理方便,只需申请一个通配符的证书 *.example.com 配置在代理服务器

环境

系统环境都是Ubuntu 18.04.2,服务器都是使用Nginx,代理服务器配置SSL,使用HTTPS对外访问(HTTP强制跳转到HTTPS),应用服务器使用Laravel,HTTP对内网访问。

配置

应用服务器配置

应用服务器的nginx照常配置即可,可以设置server_name为对应的域名。

代理服务器配置

代理服务器从网上抄来一份,主要是设置proxy_pass,下面只是location block:

1
2
3
4
5
6
7
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://ip;
}

注意要加 proxy_set_header X-Forwarded-Proto $scheme 这一条,让后面被代理的应用服务器知道对应的协议是什么,这样应用服务器生成url(如css,js文件)使用相应的协议。如果代理服务器确定使用https,那$scheme替换成https也可以。

问题

本以为这样就搞定了,但是在用域名测试的时候发现网站不能正常加载,浏览器有如下错误:

1
Mixed Content: The page at '<URL>' was loaded over HTTPS, but requested an insecure stylesheet '<URL>'. This request has been blocked; the content must be served over HTTPS.

在页面中混用了HTTPS和HTTP,浏览器会block掉不安全的HTTP链接。而HTTP链接主要是用laravel的asset生成的css, js文件,css文件没有加载上,所以就不能正常加载了。

https://laravel.com/docs/5.5/helpers#method-asset
多方查询,有推荐使用proxy_redirect来替换HTTP为HTTPS,但是试用不成功。

也有说这些资源文件用uri路径来表示,\css\example.css 这样,不带协议。

或者页面增加一个meta,让HTTP升级为安全的HTTPS:

1
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />

这样在生产环境没问题,但是需要改动很多代码,而且开发、测试环境也需要ssl证书,很不方便。

在查询到几篇文章中,有提到后端是tomcat,需要修改tomcat的配置来接受X-Forwarded-Proto 这个Header来处理。我思考是不是laravel也需要类似设置,google一下发现了这个:

https://laravel.com/docs/5.5/requests#configuring-trusted-proxies
When running your applications behind a load balancer that terminates TLS / SSL certificates, you may notice your application sometimes does not generate HTTPS links. Typically this is because your application is being forwarded traffic from your load balancer on port 80 and does not know it should generate secure links.

Laravel从5.5开始,内置了一个 TrustProxies模块来解决这个问题,专门应对使用了Load Balancer等情况。

所以按照文档,修改 protected $proxies = ‘‘,因为应用服务器在内网,所以改成也无所谓。
Laravel 5.5之前的版本,可以自行用composer来安装这个插件。

https://github.com/fideloper/TrustedProxy

测试之后,问题解决。

http://alexlove77.github.io/2019/06/18/ngingx-reverse-proxy-https/

知我者谓我心忧,不知我者谓我何求,悠悠苍天,此何人哉

Sending Headers Only

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"net/http"
)

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", "A Go Web Server")
w.WriteHeader(200)
}

Rendering Plain Text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"net/http"
)

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
}

Rendering JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"encoding/json"
"net/http"
)

type Profile struct {
Name string
Hobbies []string
}

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"Alex", []string{"snowboarding", "programming"}}

js, err := json.Marshal(profile)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.Write(js)
}

Rendering XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"encoding/xml"
"net/http"
)

type Profile struct {
Name string
Hobbies []string `xml:"Hobbies>Hobby"`
}

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"Alex", []string{"snowboarding", "programming"}}

x, err := xml.MarshalIndent(profile, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/xml")
w.Write(x)
}

Serving a File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"net/http"
"path"
)

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
// Assuming you want to serve a photo at 'images/foo.png'
fp := path.Join("images", "foo.png")
http.ServeFile(w, r, fp)
}

Important: http.ServeFile() does not automatically sanitize the file path. So if you’re constructing a file path from untrusted user input, you must sanitize the input with filepath.Clean() before using it to avoid directory traversal attacks.

Rendering a HTML Template

1
2
3
4
File: templates/index.html

<h1>Hello {{ .Name }}</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
"html/template"
"net/http"
"path"
)

type Profile struct {
Name string
Hobbies []string
}

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"Alex", []string{"snowboarding", "programming"}}

fp := path.Join("templates", "index.html")
tmpl, err := template.ParseFiles(fp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if err := tmpl.Execute(w, profile); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

Rendering a HTML Template to a String

Instead of passing in the http.ResponseWriter when executing your template (like in the above snippet) use a buffer instead:

1
2
3
4
5
6
7
...
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, profile); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
templateString := buf.String()
...

Using Layouts and Nested Templates

File: templates/layout.html

1
2
3
4
5
6
7
8
<html>
<head>
<title>{{ template "title" . }}</title>
</head>
<body>
{{ template "content" . }}
</body>
</html>

File: templates/index.html

1
2
3
4
5
6
{{ define "title" }}An example layout{{ end }}

{{ define "content" }}
<h1>Hello {{ .Name }}</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
{{ end }}

File: main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"html/template"
"net/http"
"path"
)

type Profile struct {
Name string
Hobbies []string
}

func main() {
http.HandleFunc("/", foo)
http.ListenAndServe(":3000", nil)
}

func foo(w http.ResponseWriter, r *http.Request) {
profile := Profile{"Alex", []string{"snowboarding", "programming"}}

lp := path.Join("templates", "layout.html")
fp := path.Join("templates", "index.html")

// Note that the layout file must be the first parameter in ParseFiles
tmpl, err := template.ParseFiles(lp, fp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if err := tmpl.Execute(w, profile); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

https://www.alexedwards.net/blog/golang-response-snippets