1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-18 18:37:05 +00:00

add a jwt tutorial + go client

This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-11-04 21:12:13 +02:00
parent ed38047385
commit 579c3878f0
22 changed files with 818 additions and 163 deletions

View File

@@ -0,0 +1,12 @@
# Go Client
```sh
$ go run .
```
```sh
2020/11/04 21:08:40 Access Token:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTAwYzI3ZDEtYjVhYS00NjU0LWFmMTYtYjExNzNkZTY1NjI5Iiwicm9sZXMiOlsiYWRtaW4iXSwiaWF0IjoxNjA0NTE2OTIwLCJleHAiOjE2MDQ1MTc4MjAsImp0aSI6IjYzNmVmMDc0LTE2MzktNGJhZi1hNGNiLTQ4ZDM4NGMxMzliYSIsImlzcyI6Im15YXBwIn0.T9B0zG0AHShO5JfQgrMQBlToH33KHgp8nLMPFpN6QmM"
2020/11/04 21:08:40 Todo Created:
model.Todo{ID:"cfa38d7a-c556-4301-ae1f-fb90f705071c", UserID:"a00c27d1-b5aa-4654-af16-b1173de65629", Title:"test todo title", Body:"test todo body contents", CreatedAt:1604516920}
```

View File

@@ -0,0 +1,109 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
)
// Client is the default http client instance used by the following methods.
var Client = http.DefaultClient
// RequestOption is a function which can be used to modify
// a request instance before Do.
type RequestOption func(*http.Request) error
// WithAccessToken sets the given "token" to the authorization request header.
func WithAccessToken(token []byte) RequestOption {
bearer := "Bearer " + string(token)
return func(req *http.Request) error {
req.Header.Add("Authorization", bearer)
return nil
}
}
// WithContentType sets the content-type request header.
func WithContentType(cType string) RequestOption {
return func(req *http.Request) error {
req.Header.Set("Content-Type", cType)
return nil
}
}
// WithContentLength sets the content-length request header.
func WithContentLength(length int) RequestOption {
return func(req *http.Request) error {
req.Header.Set("Content-Length", strconv.Itoa(length))
return nil
}
}
// Do fires a request to the server.
func Do(method, url string, body io.Reader, opts ...RequestOption) (*http.Response, error) {
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
for _, opt := range opts {
if err = opt(req); err != nil {
return nil, err
}
}
return Client.Do(req)
}
// JSON fires a request with "v" as client json data.
func JSON(method, url string, v interface{}, opts ...RequestOption) (*http.Response, error) {
buf := new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(v)
if err != nil {
return nil, err
}
opts = append(opts, WithContentType("application/json; charset=utf-8"))
return Do(method, url, buf, opts...)
}
// Form fires a request with "formData" as client form data.
func Form(method, url string, formData url.Values, opts ...RequestOption) (*http.Response, error) {
encoded := formData.Encode()
body := strings.NewReader(encoded)
opts = append([]RequestOption{
WithContentType("application/x-www-form-urlencoded"),
WithContentLength(len(encoded)),
}, opts...)
return Do(method, url, body, opts...)
}
// BindResponse binds a response body to the "dest" pointer and closes the body.
func BindResponse(resp *http.Response, dest interface{}) error {
contentType := resp.Header.Get("Content-Type")
if idx := strings.IndexRune(contentType, ';'); idx > 0 {
contentType = contentType[0:idx]
}
switch contentType {
case "application/json":
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(dest)
default:
return fmt.Errorf("unsupported content type: %s", contentType)
}
}
// RawResponse simply returns the raw response body.
func RawResponse(resp *http.Response) ([]byte, error) {
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}

View File

@@ -0,0 +1,69 @@
package main
import (
"fmt"
"log"
"net/http"
"net/url"
"myapp/api"
"myapp/domain/model"
)
const base = "http://localhost:8080"
func main() {
accessToken, err := authenticate("admin", "admin")
if err != nil {
log.Fatal(err)
}
log.Printf("Access Token:\n%q", accessToken)
todo, err := createTodo(accessToken, "test todo title", "test todo body contents")
if err != nil {
log.Fatal(err)
}
log.Printf("Todo Created:\n%#+v", todo)
}
func authenticate(username, password string) ([]byte, error) {
endpoint := base + "/signin"
data := make(url.Values)
data.Set("username", username)
data.Set("password", password)
resp, err := Form(http.MethodPost, endpoint, data)
if err != nil {
return nil, err
}
accessToken, err := RawResponse(resp)
return accessToken, err
}
func createTodo(accessToken []byte, title, body string) (model.Todo, error) {
var todo model.Todo
endpoint := base + "/todos"
req := api.TodoRequest{
Title: title,
Body: body,
}
resp, err := JSON(http.MethodPost, endpoint, req, WithAccessToken(accessToken))
if err != nil {
return todo, err
}
if resp.StatusCode != http.StatusCreated {
rawData, _ := RawResponse(resp)
return todo, fmt.Errorf("failed to create a todo: %s", string(rawData))
}
err = BindResponse(resp, &todo)
return todo, err
}