Echo Framework tutorial
환경 설정
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
- 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
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일 때
- 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???
- 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
- 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
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가 나온 것을 알수 있음
- 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)
- 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
- 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쿠키 값을 볼수 있음
- 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{
}))
- 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")
}