Go/EchoFramework

Echo Framework tutorial

Daniel007 2020. 3. 1. 23:50

환경 설정

Project_go 폴더 안에 bin, pkg, src 폴더 생성

mkdir Project_go
cd Projec_go
mkdir bin, pkg, src

터미널마다 GOPATH가 적용 됨

KDS-2:Project_go kimdaesung$ pwd
/Users/kimdaesung/Documents/Project_go
KDS-2:Project_go kimdaesung$ export GOPATH=`pwd`  // ₩ Caps 
KDS-2:Project_go kimdaesung$ export
declare -x Apple_PubSub_Socket_Render="/private/tmp/com.apple.launchd.lCNIEZrXfi/Render"
declare -x CLICOLOR="1"
declare -x CONDA_EXE="/anaconda3/bin/conda"
declare -x CONDA_SHLVL="0"
declare -x GOPATH="/Users/kimdaesung/Documents/Project_go"
declare -x HOME="/Users/kimdaesung"
declare -x LANG="ko_KR.UTF-8"
declare -x LOGNAME="kimdaesung"
declare -x LSCOLORS="DxFxCxGxBxegedabagaced"
declare -x OLDPWD="/Users/kimdaesung/Documents"
declare -x PATH="/Library/Frameworks/Python.framework/Versions/3.6/bin:/anaconda3/condabin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/usr/local/go/bin:/Applications/Wireshark.app/Contents/MacOS"
declare -x PWD="/Users/kimdaesung/Documents/Project_go"
declare -x SHELL="/bin/bash"
declare -x SHLVL="1"
declare -x SSH_AUTH_SOCK="/private/tmp/com.apple.launchd.ib2Ad0PPLY/Listeners"
declare -x TERM="xterm-256color"
declare -x TERM_PROGRAM="Apple_Terminal"
declare -x TERM_PROGRAM_VERSION="421.2"
declare -x TERM_SESSION_ID="47575C62-0E14-44DD-B87D-34D73DBE10FC"
declare -x TMPDIR="/var/folders/15/z55my5l97sj1pr0qtgscby080000gn/T/"
declare -x USER="kimdaesung"
declare -x XPC_FLAGS="0x0"
declare -x XPC_SERVICE_NAME="0"

Project_go 로 이동 후에 src 폴더 밑에 main 폴더 생성 후 main.go 파일 생성

echo framework 설치

src 폴더 에서


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

참조 : https://github.com/labstack/echo

v4 가 없으므로 모듈 수정함

e := echo.New() // initialed the echo object

e.use(middleware.Logger()) // loggerfor server log
e.use(middleware.Recover()) // recover from panics anywhere in the chain

실행 하였을때

KDS-2:main kimdaesung$ go run main.go 

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.1.10
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:1323
{"time":"2019-09-24T13:49:34.697161+09:00","id":"","remote_ip":"::1","host":"localhost:1323","method":"GET","uri":"/","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36","status":200,"error":"","latency":19588,"latency_human":"19.588µs","bytes_in":0,"bytes_out":13}
{"time":"2019-09-24T13:49:35.164318+09:00","id":"","remote_ip":"::1","host":"localhost:1323","method":"GET","uri":"/favicon.ico","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36","status":404,"error":"code=404, message=map[message:Not Found], internal=\u003cnil\u003e","latency":88569,"latency_human":"88.569µs","bytes_in":0,"bytes_out":24}
{"time":"2019-09-24T13:49:35.16856+09:00","id":"","remote_ip":"::1","host":"localhost:1323","method":"GET","uri":"/","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36","status":200,"error":"","latency":15298,"latency_human":"15.298µs","bytes_in":0,"bytes_out":13}
^Csignal: interrupt

  1. Project setup and Hello World

아래 와 같이 해도 나옴

package main

import (
    "fmt"
    "net/http"

    "github.com/labstack/echo"
)

func main() {
    fmt.Println("welcome to the server")

    e := echo.New()
// c라는 변수에 echo.Context라는 type임
    e.GET("/", func(c echo.Context) error {
    return c.String(http.StatusOK, "Hello from the web side!") // StatusOK(200)이면 받아오면 문자열 출력

    })
    e.Start(":8080") // start server("port number")

}

hello 함수를 바깥으로 뺀 이유는 많은 라이브러리들이 많이 사용되고 보기 좋다.

package main

import (
    "fmt"
    "net/http"

    "github.com/labstack/echo"
)

func hello(c echo.Context) error {
    return c.String(http.StatusOK, "Hello from the web side!")

}

func main() {
    fmt.Println("welcome to the server")

    e := echo.New()

    e.GET("/", hello)

    e.Start(":8080") // start server("port number")

}

go run main.go
  1. URL Params Query Params and Json Response

    /*
    package main
    import (

     "fmt"
     "net/http"
    
     "github.com/labstack/echo"

    )

    func hello(c echo.Context) error {

     return c.String(http.StatusOK, "Hello from the web side!")

    }
    */
    func getCats(c echo.Context) error {

     catName := c.QueryParam("name")
     catType := c.QueryParam("type")

    return c.String(http.StatusOK, fmt.Sprintln("your cat name is : %s\n and his type is : %s", catName, catType))
    }

    func main() {

     fmt.Println("welcome to the server")
    
     e := echo.New()
    
     e.GET("/", hello)
     e.GET("/cats", getCats) // cats 파라미터 추가
    
     e.Start(":8080") // start server("port number")

    }

웹 페이지 주소 값에 localhost:8080/cats?name=mac&type=pro 를 입력해주게 되면

/*
func getCats(c echo.Context) error {
    catName := c.QueryParam("name")
    catType := c.QueryParam("type")
*/
    dataType := c.Param("data") 

    if dataType == "string" {
        return c.String(http.StatusOK, fmt.Sprintf("your cat name is : %s\n and his type is : %s", catName, catType))

    }
    if dataType == "json" {
        return c.JSON(http.StatusOK, map[string]string{
            "name": catName,
            "type": catType,
        })
    }
    return c.JSON(http.StatusBadRequest, map[string]string{
        "error": "you need to lets us know if you want json or string data",
    })
}

dataType이 json 일 때

dataType이 string일 때

  1. Parsing JSON From Request

cat

package main


import (
    "encoding/json" // 추가
    "fmt"
    "io/ioutil" // 추가
    "log"       // 추가
    "net/http"

    "github.com/labstack/echo"
)
// Cat struct 추가
type Cat struct {
    Name string `json:"name"`
    Type string `json:"type"`
}

func hello(c echo.Context) error {
    return c.String(http.StatusOK, "Hello from the web side!")

}

func getCats(c echo.Context) error {
    catName := c.QueryParam("name")
    catType := c.QueryParam("type")
    dataType := c.Param("data")

    if dataType == "string" {
        return c.String(http.StatusOK, fmt.Sprintf("your cat name is : %s\n and his type is : %s", catName, catType))

    }
    if dataType == "json" {
        return c.JSON(http.StatusOK, map[string]string{
            "name": catName,
            "type": catType,
        })
    }
    return c.JSON(http.StatusBadRequest, map[string]string{
        "error": "you need to lets us know if you want json or string data",
    })
}

func addCat(c echo.Context) error {
    cat := Cat{}

    defer c.Request().Body.Close()

    b, err := ioutil.ReadAll(c.Request().Body)
    if err != nil {
        log.Printf("Failed reading the request body for addCats: %s\n", err)
        return c.String(http.StatusInternalServerError, "")
    }

    err = json.Unmarshal(b, &cat)
    if err != nil {
        log.Printf("Failed unmarshaling in addCats: %s\n", err)
        return c.String(http.StatusInternalServerError, "")
    }

    log.Printf("this is your cat: %#v\n", cat)
    return c.String(http.StatusOK, "we got your cat!")
}

func main() {
    e.POST("/cats", addCat) 
}

Postman 에서 POST로 localhost:8080/cats 입력 후에 아래와 같은 json을 입력 하게 되면

{
  "name":"fishmaster",
  "type" : "cat-fish"
}

we got your cat! 을 반환하고 쉘 로그에는 아래와 같이 입력됨

2019/09/25 12:03:42 this is your cat: main.Cat{Name:"fishmaster", Type:"cat-fish"}

Dog 추가

- main 함수에 추가
e.POST("/dogs", addDog)

- Dog Struct 추가
type Dog struct {
    Name string `json:"name"`
    Type string `json:"type"`
}

- addDog 함수 추가
func addDog(c echo.Context) error {
    dog := Dog{}

    defer c.Request().Body.Close()

    err := json.NewDecoder(c.Request().Body).Decode(&dog)
    if err != nil {
        log.Printf("Failed reading the request body for addCats: %s", err)
        return echo.NewHTTPError(http.StatusInternalServerError)
    }
    log.Printf("this is your dog: %#v\n", dog)
    return c.String(http.StatusOK, "we got your dog!")
}

Postman 에서 POST로 localhost:8080/dogs 입력 후에 아래와 같은 json을 입력 하게 되면

{
  "name":"doggymaster",
  "type" : "dog-fish"
}

we got your dog! 을 반환하고 쉘 로그에는 아래와 같이 입력됨

2019/09/25 12:28:19 this is your dog: main.Dog{Name:"doggymaster", Type:"dog-fish"}

hamsters 추가

- main 함수에 추가
    e.POST("hamsters", addHamster)

- Dog Struct 추가
type Hamster struct {
    Name string `json:"name"`
    Type string `json:"type"`
}

- addHamster 함수 추가
func addHamster(c echo.Context) error {
    hamster := Hamster{}

    err := c.Bind(&hamster)
    if err != nil {
        log.Printf("Failed reading the request body for addHamsters: %s", err)
        return echo.NewHTTPError(http.StatusInternalServerError)
    }
    log.Printf("this is your hamster: %#v\n", hamster)
    return c.String(http.StatusOK, "we got your hamster!")

}

Postman 에서 POST로 localhost:8080/hamsters 입력 후에 아래와 같은 json을 입력 하게 되면

{
  "name":"ham master",
  "type" : "master"
}

we got your dog! 을 반환하고 쉘 로그에는 아래와 같이 입력됨

2019/09/25 13:17:38 this is your hamster: main.Hamster{Name:"ham master", Type:"master"}

???Bind???

  1. Intro to Middlewares

그룹핑 할때 돕는 middleware

- main 함수에 추가
    g := e.Group("/admin") // 그룹핑
    g.GET("/main", mainAdmin) // Get 구현 

- mainAdmin 함수 생성
func mainAdmin(c echo.Context) error{
    return c.String(http.StatusOK, "you are on the secret admin main page")
}

Postman 에서 GET 으로 localhost:8080/admin/main 으로 보내면 아래와 같이 입력 됨

you are on the secret admin main page

https://echo.labstack.com/guide 에 들어가면 go lang echo의 middleware에 대한 정보를 얻을수 있음

- middleware 사용하기 위해서 import 에 추가
"github.com/labstack/echo/middleware"

- g group에 middleware의 logger 추가하는 방법 1
g := e.Group("/admin", middleware.logger())

- g가 middleware의 logger 추가하는 방법 2
g.Use(middleware.Logger()) 

- g가 middleware의 logger 추가하는 방법 3 핸들러 뒤에 작성
g.GET("/main", mainAdmin, middleware.Logger())

두번째 방법 추천

Postman 에서 실행 하게 되면 로그가 찍힘

{"time":"2019-09-25T13:52:36.524894+09:00","id":"","remote_ip":"::1","host":"localhost:8080","method":"GET","uri":"/admin/main","user_agent":"PostmanRuntime/7.11.0","status":200,"error":"","latency":19594,"latency_human":"19.594µs","bytes_in":46,"bytes_out":37}

middleware의 withConfig 옵션

내가 원하는대로 middleware를 구성할 수 있다. 문자열이 출력하는 형식, 로그를 출력 할 위치 를 정의할수 있음

g.Use(middleware.LoggerWithConfig())

파일 출력하는 예시

//this logs the server interaction
    g.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
        Format: "${time_rfc3339} ${status} ${method} ${host}${path} ${latency_human}" + "\n",
    }))

[2019-09-25T14:10:21+09:00] 200 GET localhost:8080/admin/main 27.528µs
[2019-09-25T14:10:22+09:00] 200 GET localhost:8080/admin/main 10.027µs
[2019-09-25T14:10:22+09:00] 200 GET localhost:8080/admin/main 4.08µs
[2019-09-25T14:10:23+09:00] 200 GET localhost:8080/admin/main 7.043µs
[2019-09-25T14:10:23+09:00] 200 GET localhost:8080/admin/main 5.659µs
[2019-09-25T14:10:24+09:00] 200 GET localhost:8080/admin/main 5.355µs
  1. Basic Authentication Middleware

middleware를 이용해서 간단하게 구현 가능

- main함수에 추가
g.Use(middleware.BasicAuth(func(username, passwd string, c echo.Context) (bool, error) {
        //check in the DB
        if username == "daniel" && passwd == "1234" {
            return true, nil
        }
        return false, nil
    }))

Postman에서 GET방식으로 localhost:8080/admin/main , Authorization에 username passwd를 입력하면

body 에 you are on the secret admin main page 메시지가 뜨고 로그에는 200 로그가 뜸

[2019-09-25T19:15:12+09:00] 401 GET localhost:8080/admin/main 151.599µs // id, passwd 안넣었을 때
[2019-09-25T19:15:23+09:00] 401 GET localhost:8080/admin/main 39.308µs
[2019-09-25T19:16:46+09:00] 401 GET localhost:8080/admin/main 20.035µs
[2019-09-25T19:17:01+09:00] 401 GET localhost:8080/admin/main 19.153µs
[2019-09-25T19:18:23+09:00] 200 GET localhost:8080/admin/main 24.352µs // id, passwd 넣었을 때
[2019-09-25T19:18:42+09:00] 200 GET localhost:8080/admin/main 8.031µs
[2019-09-25T19:19:09+09:00] 200 GET localhost:8080/admin/main 6.175µs
[2019-09-25T19:19:11+09:00] 200 GET localhost:8080/admin/main 7.762µs
  1. Custom Middlewares

    • middleware 함수 생성
      //서버와 서버가 응답하는 함수
      func Serverheader(next echo.HandlerFunc) echo.HandlerFunc {
      return func(c echo.Context) error {

        c.Response().Header().Set(echo.HeaderServer, "GOSTUDY/1.0") // 헤더이름 지정 "GOSTUDY/1.0"
      
        return next(c)

      }
      }

    • 사용하기 위해서 main함수에 추가
      e.Use(Serverheader)

Header에 GOSTUDY/1.0 이 생긴걸 볼수 있음

//서버와 서버가 응답하는 함수
func Serverheader(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        c.Response().Header().Set(echo.HeaderServer, "GOSTUDY/1.0") // 헤더이름 지정 "GOSTUDY/1.0"
        c.Response().Header().Set("notReallyHeader", "thisHaveNoMeaning") // CustomHeader 추가

        return next(c)
    }
}

CustomHeader추가 시 Key에 CustomHeader가 나온 것을 알수 있음

  1. Cookies

쿠키에 쿠키값이 있는지 확인하고 검증된 값을 이미지로 렌더링 함

- coockies 그룹 생성
cookieGroup := e.Group("/cookie")

- 전에 g로 만들었던 그룹을 adminGroup으로 바꾸고 코드 변경
adminGroup := e.Group("/admin")

    adminGroup := e.Group("/admin")
    cookieGroup := e.Group("/cookie")

    // g := e.Group("/admin") // 그룹핑

    //this logs the server interaction
    adminGroup.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
        Format: "[${time_rfc3339}] ${status} ${method} ${host}${path} ${latency_human}" + "\n",
    }))

    adminGroup.Use(middleware.BasicAuth(func(username, passwd string, c echo.Context) (bool, error) {
        //check in the DB
        if username == "daniel" && passwd == "1234" {
            return true, nil
        }
        return false, nil
    }))

- cookieGroup Get 생성
cookieGroup.GET("/main", mainCookie)

- mainCookie  handler 생성
func mainCookie(c echo.Context) error {
    return c.String(http.StatusOK, "you are not yet on the secret cookie page")
}

- login endpoint 생성
e.GET("/login", login)

- login handler 생성
func login(c echo.Context) error {
    username := c.QueryParam("username")
    password := c.QueryParam("password")
    // check username and password against DB after hasing the password
    if username == "daniel" && password == "1234" {
        cookie := &http.Cookie{}

        // this is the same
        //cookie := new(http.Cookie)

        cookie.Name = "sessionID"
        cookie.Value = "some_string"
        cookie.Expires = time.Now().Add(48 * time.Hour) //timepackage 추가

        c.SetCookie(cookie)

        return c.String(http.StatusOK, "Your were logged in!")
    }
    return c.String(http.StatusUnauthorized, "Your username or password were wrong")
}







- cookiecheck handler 작성
func checkCookie(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        cookie, err := c.Cookie("sessionID")
        if err != nil {
            if strings.Contains(err.Error(), "named cookie not present") {
                return c.String(http.StatusUnauthorized, "you dont have any cookie")
            }

            log.Println(err)
            return err
        }

        if cookie.Value == "some_string" {
            return next(c)
        }

        return c.String(http.StatusUnauthorized, "you dont have the right cookie, cookie")
    }
}

- cookiecheck 기능 추가
cookieGroup.Use(checkCookie)
  1. JWT Authentication

claims : The second segment of the token

import 에 "github.com/dgrijalva/jwt-go" 추가
jwt "github.com/dgrijalva/jwt-go"
- jwtClaims sturct 생성 

type JwtClaims struct {
    Name        string    `json:"name"`
    jwt.StandardClaims
}

-  jwt tocken 함수 생성
func createJwtToken() (string, error) {
    claims := JwtClaims{
        "daniel",
        jwt.StandardClaims{
            Id: "main_user_id",
            ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
        },
    }

    rawToken := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)

    token, err := rawToken.SignedString([]byte("mySecret"))
    if err != nil {
        return "", err
    }

    return token, nil
}

- login 함수에 jwt tocken 추가
token, err := createJwtToken()
        if err != nil {
            log.Println("Error Creating JWT token", err)
            return c.String(http.StatusInternalServerError, "something went wrong")
        }
        return c.JSON(http.StatusOK, map[string]string{
            "message": "You were logged int!",
            "tocken":  token,
        })

Postman에서 Get에 localhost:8080/login?username=daniel&password=1234 을 보내면 아래와 같이 토큰 값이 복사됨

{
    "message": "You were logged int!",
    "tocken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZGFuaWVsIiwiZXhwIjoxNTY5NTUwNTY2LCJqdGkiOiJtYWluX3VzZXJfaWQifQ.hjmO7ighkwyBid9gx2Ins8DFy8jmVihAu0Jzo5F0TD5JVLmRVC57BUE81Squa7K4MMg_0ah6nrYHTrg8oEY5hw"
}





- main 함수에 jwt 그룹 생성
jwtGroup := e.Group("/jwt")

- jwtGroup GET 생성
jwtGroup.GET("/main", mainJWT)

- mainJWT handler 구현
func mainJwt(c echo.Context) error {
    return c.String(http.StatusOK, "you are on the top secret jwt page!")
}

- main함수에 jwtGroup 미들웨어 추가

jwtGroup.Use(middleware.JWTWithConfig(middleware.JWTConfig{
        SigningMethod: "HS512",
        SigningKey:    []byte("mySecret"),
    }))

Headers 에 Authorization / Bearer [토큰값] 을 넣어주게 되면 아래와 같이 뜸

- client에 접속하기 위해서 mainJwt 함수 수정

func mainJwt(c echo.Context) error {
    user := c.Get("user")
    token := user.(*jwt.Token)

    claims := token.Claims.(jwt.MapClaims)

    log.Println("User Name: ", claims["name"], "User ID: ", claims["jti"])

    return c.String(http.StatusOK, "you are on the top secret jwt page!")
}

Postman에서 GET localhost:8080/jwt/main을 보내면 로그가 userid와 passwd가 찍히는걸 알수 있음

2019/09/26 15:08:13 User Name:  daniel User ID:  main_user_id
  1. JWT Token From a Cookie Side Quest

테스트

- main함수 안에 TokenLookup, AuthScheme 추가

jwtGroup.Use(middleware.JWTWithConfig(middleware.JWTConfig{
        SigningMethod: "HS512",
        SigningKey:    []byte("mySecret"),
        TokenLookup: "header:MyHeader",
        AuthScheme: "iloveDogs",
    }))

KEY에 MyHeader를 입력 하고 VALUE에 iloveDogs를 입력 하고 뒤에 토큰값을 입력하면 실행됨

실제 적용

- TokenLookup 수정
jwtGroup.Use(middleware.JWTWithConfig(middleware.JWTConfig{
        SigningMethod: "HS512",
        SigningKey:    []byte("mySecret"),
        TokenLookup:   "cookie:JWTCookie",
    }))

- login 함수 추가
        jwtcookie := &http.Cookie{}

        jwtcookie.Name = "JWTCookie"
        jwtcookie.Value = "some_string"
        jwtcookie.Expires = time.Now().Add(48 * time.Hour)

        c.SetCookie(jwtcookie)

localhost:8080/login?username=daniel&password=1234 를 입력하게 되면 쿠기값에 이미 만들어 놓은 쿠기값과 jwt쿠키 값을 볼수 있음

  1. Serving a Website

Pluton-Single Page Bootstrap Temple 다운로드

프로젝트 폴더 안에 static 폴더 생성

- main 함수에 middleware static 추가
e.Use(middleware.Static("./"))
- GET함수 추가
e.GET("hello", hello)



KDS-2:src kimdaesung$ pwd
/Users/kimdaesung/Documents/Project_go/src
KDS-2:src kimdaesung$ go install main/
KDS-2:src kimdaesung$ ls
github.com golang.org main
KDS-2:src kimdaesung$ cd ..
KDS-2:Project_go kimdaesung$ ls
bin               jwt-go-master     jwt-go-master.zip pkg               src               static
KDS-2:Project_go kimdaesung$ cp bin/main static/
KDS-2:Project_go kimdaesung$ cd static/
KDS-2:static kimdaesung$ ./main 

localhost:8080 으로 접속하면 web이 나옴

- main함수 에서 수정
e.Use(middleware.Static("../static"))

KDS-2:static kimdaesung$ cd ../
KDS-2:Project_go kimdaesung$ pwd
/Users/kimdaesung/Documents/Project_go
KDS-2:Project_go kimdaesung$ go install main
KDS-2:Project_go kimdaesung$ rm static/main 
KDS-2:Project_go kimdaesung$ cd bin/
KDS-2:bin kimdaesung$ ./main
welcome to the server







- main 함수에서 middleware 수정
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{

}))
  1. Refactoring

코드 재조정

main.go에 있는 코드들을 각각 관리하기 쉽게 나눔

KDS-2:Project_go kimdaesung$ cd src/
KDS-2:src kimdaesung$ ls
api        github.com golang.org main       router     webserver
KDS-2:src kimdaesung$ tree api/
api/
├── adminGroup.go
├── cookieGroup.go
├── handlers
│   ├── adminhandlers.go
│   ├── authenticationhandlers.go
│   ├── cathandlers.go
│   ├── cookiehandlers.go
│   ├── doghandlers.go
│   ├── generalhandlers.go
│   ├── hamsterhandlers.go
│   └── jwthandlers.go
├── jwtGroup.go
├── mainGroup.go
└── middlewares
    ├── adminMiddlewares.go
    ├── cookieMiddlewares.go
    ├── jwtMiddlewares.go
    └── mainMiddlewares.go

2 directories, 16 files
KDS-2:src kimdaesung$ tree router/
router/
└── router.go

0 directories, 1 file
KDS-2:src kimdaesung$ tree webserver/
webserver/
└── webserver.go

0 directories, 1 file

파일별 내용

API/handlers

adminhandlers.go

package handlers

import (
    "net/http"

    "github.com/labstack/echo"
)

func MainAdmin(c echo.Context) error {
    return c.String(http.StatusOK, "horay you are on the secret amdin main page!")
}

authenticationhandlers.go

package handlers

import (
    "log"
    "net/http"
    "time"

    "github.com/dgrijalva/jwt-go"
    "github.com/labstack/echo"
)

type JwtClaims struct {
    Name string `json:"name"`
    jwt.StandardClaims
}

func Login(c echo.Context) error {
    username := c.QueryParam("username")
    password := c.QueryParam("password")

    // check username and password against DB after hashing the password
    if username == "daniel" && password == "1234" {
        cookie := &http.Cookie{}

        // this is the same
        //cookie := new(http.Cookie)

        cookie.Name = "sessionID"
        cookie.Value = "some_string"
        cookie.Expires = time.Now().Add(48 * time.Hour)

        c.SetCookie(cookie)

        // create jwt token
        token, err := createJwtToken()
        if err != nil {
            log.Println("Error Creating JWT token", err)
            return c.String(http.StatusInternalServerError, "something went wrong")
        }

        return c.JSON(http.StatusOK, map[string]string{
            "message": "You were logged in!",
            "token":   token,
        })
    }

    return c.String(http.StatusUnauthorized, "Your username or password were wrong")
}

func createJwtToken() (string, error) {
    claims := JwtClaims{
        "daniel",
        jwt.StandardClaims{
            Id:        "main_user_id",
            ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
        },
    }

    rawToken := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)

    token, err := rawToken.SignedString([]byte("mySecret"))
    if err != nil {
        return "", err
    }

    return token, nil
}

cathandlers.go

package handlers

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"

    "github.com/labstack/echo"
)

type Cat struct {
    Name string `json:"name"`
    Type string `json:"type"`
}

func GetCats(c echo.Context) error {
    catName := c.QueryParam("name")
    catType := c.QueryParam("type")

    dataType := c.Param("data")

    if dataType == "string" {
        return c.String(http.StatusOK, fmt.Sprintf("your cat name is: %s\nand his type is: %s\n", catName, catType))
    }

    if dataType == "json" {
        return c.JSON(http.StatusOK, map[string]string{
            "name": catName,
            "type": catType,
        })
    }

    return c.JSON(http.StatusBadRequest, map[string]string{
        "error": "you need to lets us know if you want json or string data",
    })
}

func AddCat(c echo.Context) error {
    cat := Cat{}

    defer c.Request().Body.Close()

    b, err := ioutil.ReadAll(c.Request().Body)
    if err != nil {
        log.Printf("Failed reading the request body for addCats: %s\n", err)
        return c.String(http.StatusInternalServerError, "")
    }

    err = json.Unmarshal(b, &cat)
    if err != nil {
        log.Printf("Failed unmarshaling in addCats: %s\n", err)
        return c.String(http.StatusInternalServerError, "")
    }

    log.Printf("this is your cat: %#v\n", cat)
    return c.String(http.StatusOK, "we got your cat!")
}

cookiehandlers.go

package handlers

import (
    "net/http"

    "github.com/labstack/echo"
)

func MainCookie(c echo.Context) error {
    return c.String(http.StatusOK, "you are on the secret cookie page!")
}

doghandlers.go

package handlers

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

    "github.com/labstack/echo"
)

type Dog struct {
    Name string `json:"name"`
    Type string `json:"type"`
}

func AddDog(c echo.Context) error {
    dog := Dog{}

    defer c.Request().Body.Close()

    err := json.NewDecoder(c.Request().Body).Decode(&dog)
    if err != nil {
        log.Printf("Failed processing addDog request: %s\n", err)
        return echo.NewHTTPError(http.StatusInternalServerError)
    }

    log.Printf("this is your dog: %#v", dog)
    return c.String(http.StatusOK, "we got your dog!")
}

generalhandlers.go

package handlers

import (
    "net/http"

    "github.com/labstack/echo"
)

func Hello(c echo.Context) error {
    return c.String(http.StatusOK, "Hello from the web side!")
}

hamsterhandlers.go

package handlers

import (
    "log"
    "net/http"

    "github.com/labstack/echo"
)

type Hamster struct {
    Name string `json:"name"`
    Type string `json:"type"`
}

func AddHamster(c echo.Context) error {
    hamster := Hamster{}

    err := c.Bind(&hamster)
    if err != nil {
        log.Printf("Failed processing addHamster request: %s\n", err)
        return echo.NewHTTPError(http.StatusInternalServerError)
    }

    log.Printf("this is your hamster: %#v", hamster)
    return c.String(http.StatusOK, "we got your hamster!")
}

jwthandlers.go

package handlers

import (
    "log"
    "net/http"

    "github.com/dgrijalva/jwt-go"
    "github.com/labstack/echo"
)

func MainJwt(c echo.Context) error {
    user := c.Get("user")
    token := user.(*jwt.Token)

    claims := token.Claims.(jwt.MapClaims)

    log.Println("User Name: ", claims["name"], "User ID: ", claims["jti"])

    return c.String(http.StatusOK, "you are on the top secret jwt page!")
}

API/

adminGroup.go

package api

import (
    "api/handlers"

    "github.com/labstack/echo"
)

func AdminGroup(g *echo.Group) {
    g.GET("/main", handlers.MainAdmin)
}

cookieGroup.go

package api

import (
    "api/handlers"

    "github.com/labstack/echo"
)

func CookieGroup(g *echo.Group) {
    g.GET("/main", handlers.MainCookie)
}

jwtGroup.go

package api

import (
    "api/handlers"

    "github.com/labstack/echo"
)

func JwtGroup(g *echo.Group) {
    g.GET("/main", handlers.MainJwt)
}

mainGroup.go

package api

import (
    "api/handlers"

    "github.com/labstack/echo"
)

func MainGroup(e *echo.Echo) {
    e.GET("/login", handlers.Login)
    e.GET("/yallo", handlers.hello)
    e.GET("/cats/:data", handlers.GetCats)

    e.POST("/cats", handlers.AddCat)
    e.POST("/dogs", handlers.AddDog)
    e.POST("/hamsters", handlers.AddHamster)
}

API/Middlewares

adminMiddlewares.go

package middlewares

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

func SetAdminMiddlewares(g *echo.Group) {
    // this logs the webserver interaction
    g.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
        Format: "[${time_rfc3339}]  ${status}  ${method} ${host}${path} ${latency_human}" + "\n",
    }))

    g.Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
        // check in the DB
        if username == "daniel" && password == "1234" {
            return true, nil
        }

        return true, nil
    }))
}

cookieMiddlewares.go

package middlewares

import (
    "strings"
    "net/http"
    "log"

    "github.com/labstack/echo"
)

func SetCookieMiddlewares(g *echo.Group) {
    g.Use(checkCookie)
}

func checkCookie(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        cookie, err := c.Cookie("sessionID")
        if err != nil {
            if strings.Contains(err.Error(), "named cookie not present") {
                return c.String(http.StatusUnauthorized, "you dont have any cookie")
            }

            log.Println(err)
            return err
        }

        if cookie.Value == "some_string" {
            return next(c)
        }

        return c.String(http.StatusUnauthorized, "you dont have the right cookie, cookie")
    }
}

jwtMiddlewares.go

package middlewares

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

func SetJwtMiddlewares(g *echo.Group) {
    g.Use(middleware.JWTWithConfig(middleware.JWTConfig{
        SigningMethod: "HS512",
        SigningKey: []byte("mySecret"),
    }))
}

mainMiddlewares.go

package middlewares

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

func SetMainMiddlewares(e *echo.Echo) {
    e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
        Root: "../static",
    }))

    e.Use(serverHeader)
}

func serverHeader(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        c.Response().Header().Set(echo.HeaderServer, "BlueBot/1.0")
        c.Response().Header().Set("notReallyHeader", "thisHaveNoMeaning")

        return next(c)
    }
}

router/router.go

package router

import (
    "api"
    "api/middlewares"

    "github.com/labstack/echo"
)

func New() *echo.Echo {
    e := echo.New()

    // create groups
    adminGroup := e.Group("/admin")
    cookieGroup := e.Group("/cookie")
    jwtGroup := e.Group("/jwt")

    // set all middlewares
    middlewares.SetMainMiddlewares(e)
    middlewares.SetAdminMiddlewares(adminGroup)
    middlewares.SetCookieMiddlewares(cookieGroup)
    middlewares.SetJwtMiddlewares(jwtGroup)

    // set main routes
    api.MainGroup(e)

    // set group routes
    api.AdminGroup(adminGroup)
    api.CookieGroup(cookieGroup)
    api.JwtGroup(jwtGroup)

    return e
}

webserber/webserver.go

package webserver

import (
    "log"
    "router"
    "sync"

    "github.com/labstack/echo"
)

var initWebServer sync.Once
var ws *WebServer

type WebServer struct {
    router *echo.Echo
}

func Instance() *WebServer {
    initWebServer.Do(func() {
        ws = &WebServer{}
        ws.router = router.New()
    })

    return ws
}

func (w *WebServer) Start() {
    // start the server

    var wg sync.WaitGroup

    wg.Add(1)
    go func() {
        defer wg.Done()
        log.Println("Start HTTP server on port :8000")
        log.Fatal(w.router.Start(":8000"))
    }()

    wg.Wait()
}

main/main.go

package main

import (
    "fmt"
    "router"
)

func main() {
    fmt.Println("Welcome to the webserver")
    e := router.New()

    e.Start(":8000")
}