Go の echo ってWebサーバーでサクッと REST しちゃう

Go言語勉強しながらWebサーバー立てたいなーどうしようかなーって思ってたときに
きっと色々候補はあるんだけど

[labstack/echo](https://github.com/labstack/echo)
> Echo is a fast :rocket: and unfancy micro web framework for Go.

っての見つけた。
Go言語そのものの勉強以外は最小限に抑えたいし、
RESTでjson返すだけならこれで十分かなって思った。
速いらしいし。

# Echo の使い方
[公式ドキュメント](https://github.com/labstack/echo)に大概載ってる。

## インストール
以前は `dgrijalava/jwt-go` を別途インストールしていましたが、
現在 ( 2019/06 ) は以下のコマンド一発でOKみたいです。

```
go get -u github.com/labstack/echo/...
```

## 動かす
```go:main.go
package main

import (
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"./handler"
)

func main() {
	// Echoのインスタンス作る
	e := echo.New()

	// 全てのリクエストで差し込みたいミドルウェア（ログとか）はここ
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	// ルーティング
	e.GET("/hello", handler.MainPage())
	
	// サーバー起動
	e.Start(":1323")    //ポート番号指定してね
}
```

```go:handler/handler.go
package handler

import (
	"net/http"
	"github.com/labstack/echo"
)

func MainPage() echo.HandlerFunc {
	return func(c echo.Context) error {     //c をいじって Request, Responseを色々する 
		return c.String(http.StatusOK, "Hello World")
	}
}
```

```bash
go run main.go
```

http://localhost:1323/hello にアクセスします。

多分ファイル分けたくなるだろうから最初から分けておいた。
他のフレームワークとあんまり変わらないかな。簡単。

ルーティングで `e.GET()` って書いてるけど、
もちろん `e.POST()` とか `e.PUT()` とか書けばメソッドで分けれますよ。

ただ、他のフレームワークと違ってルーティングの文字列は前方一致のみみたい。
正規表現は多分使えないんじゃないかな。

## Path parameter を使う
(2016/3/15 追記項目)
RESTと言えば Path parameter だよね。
リソースの場所を表さなきゃなんだから当然やね。

```go:main.go
e.GET("/hello/:username", handle.MainPage())    //セミコロンの所がプレースホルダになる
```
って感じでセミコロン使ってプレースホルダを宣言しといて、

ハンドラの方で

```go:handle.go

func MainPage() echo.HandlerFunc {
	return func(c echo.Context) error {
		username := c.Param("username")    //プレースホルダusernameの値取り出し
		return c.String(http.StatusOK, "Hello World " + username)
	}
}

```

ってやるだけやね。

ちなみに `c.Param("username")` の所を `c.P(0)` みたいにインデックスで指定も出来るっぽい。
でもどうせプレースホルダ宣言するし、どういうときにインデックス使うかちょっとわかんない。 

後クエリパラメータ(/hoge/?key=value みたいな、?付きのパラメータ)とか
フォームのデータとか送れるみたいだけど、
そっちもほとんど使い方一緒だから[公式ドキュメントの該当ページ](https://labstack.com/echo/guide/request/)見てね。

## Basic認証

認証はBasic認証のみ対応済み。
これも簡単。

main に

```go:main.go
e.Use(interceptor.BasicAuth())
```

みたいなのを追加したら、
interceptor パッケージを作って、その中に

```go:auth.go
package interceptor

import (
	"github.com/labstack/echo/middleware"
	"github.com/labstack/echo"
)

func BasicAuth() echo.MiddlewareFunc  {
    return middleware.BasicAuth(func(username string, password string, context echo.Context) (bool, error) {
        if username == "joe" && password == "correct" {
            return true,nil
        }
        return false,nil
    })
}
```

`username` と `password` の部分に Basic認証の情報が入ってくるので、
その値が正しいかどうかを判別します。

ココで `true` を返せば本来のリクエストされたハンドラへ処理がうつり、
`false` を返せば、401が返されます。

(@mztnnrt さんより、 [コメントにて 2018/09/03 現在の方法をご指摘いただいた内容](https://qiita.com/ezaki/items/62e806ae42828bb3567a?utm_campaign=email&utm_content=link&utm_medium=email&utm_source=public_comment#comment-5ae624cac79c313e103c) へ修正しました！ありがとうございますありがとうございます!


### 個別のページで認証の有無を変えたい場合は？

さっきの方法では全ページにミドルウェアを挟んでしまうので、
認証が必要でないページを作りたいときに困ってしまう。

あるリクエストだけに特定のミドルウェアを挟みたいときは

`e.GET()` のところで

```go:main.go
e.GET("/user/apps/:username", handle.MainPage(), interceptor.BasicAuth())
```

という感じで、ハンドラの後ろに足してあげるとOK。

公式ドキュメントでは「グループ機能制御出来るよ！」
って書いてあったけど良く分かんなかった。

また解決したら書き足すかも。

## json の書き出し

json の出力も

```go:handle.go

jsonMap := map[string]string{
			"foo": "bar",
			"hoge": "fuga",
		}

		return c.JSON(http.StatusOK, jsonMap)

```

みたいな感じで、`c.String` を `c.JSON` にしてマップなりなんなり渡すだけ。
余裕やね。

## CORS
json 出力できたら次はやっぱり CORS じゃんね。

```go:main.go

e.Use(standard.WrapMiddleware(cors.New(cors.Options{
		AllowedOrigins: []string{"http://example.com"},
	}).Handler))

```

ミドルウェアとしてこれをはさむだけで OK っぽい。

# echo を使ってみて。

他にもファイルアップロードとか websocket も使えるみたいだし、
簡単なデータのやりとりは一通りできる。

あと印象に残ってるのは、コードをちょっと読んでたら router の所に

> // NOTE: Slow zone...

ってコメントが入ってた。
スピードを上げることを意識してコーディングしてるんだなーって思った。
Go自体速いし、大した影響はないと思うんだけどね。

### 2017/04/30 編集
[shinriyo@github](http://qiita.com/shinriyo@github) さんにインストール方法など、記事を書いた当時と変わっていた部分を
編集していただきました。

### 2018/09/03 編集
@mztnnrt さんに Basic 認証について、記事を書いた当時と変更されていた部分についてコメントより指摘いただき、修正いたしました。

### 2019/04/22 編集
@naka_kyon さんに メソッド名に関する編集リクエストをいただき、適用いたしました。

### 2019/06/27
@TelBouzu さんに 現在の go get の書き方について編集リクエストをいただき、適用いたしました。

### 2019/12/12
@miiiiiiika さんに インストールパスの修正編集リクエストをいただき、適用いたしました。