Install all Go project dependencies in one command
go get ./...
downloads all the dependencies
go get -u -v -f all
Install all Go project dependencies in one command
go get ./...
downloads all the dependencies
go get -u -v -f all
проверка Луна
код луна
https://go.dev/play/p/S_nrTTrk0Wq
package main
import (
"fmt"
"github.com/theplant/luhn"
)
func main() {
fmt.Println(
luhn.Valid(6174570601580),
luhn.Valid(422466257468622),
luhn.Valid(2352238521358),
luhn.Valid(7162683784855),
)
}
Using JWT for Authentication in a Golang Application
https://developer.vonage.com/blog/2020/03/13/using-jwt-for-authentication-in-a-golang-application-dr
https://disk.yandex.ru/d/IzZ3Y6P_EAAC6w
https://github.com/victorsteven/jwt-best-practices
package mux
import (
"compress/gzip"
"fmt"
"log"
"mime"
"net/http"
"path/filepath"
"strings"
)
func GzipAutoMiddleware(hf http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
accept := r.Header.Get("Accept-Encoding")
content := r.Header.Get("Content-Encoding")
if strings.Contains(content, "gzip") {
reader, e := gzip.NewReader(r.Body)
if e != nil {
e = fmt.Errorf("reading gzip body failed:%w", e)
log.Println(e)
http.Error(w, e.Error(), http.StatusBadRequest)
return
}
r.Body = reader
}
if strings.Contains(accept, "gzip") {
ext := filepath.Ext(r.RequestURI)
mime := mime.TypeByExtension(ext)
if r.RequestURI == "/" || strings.HasPrefix(mime, "text/") || ext == ".json" || ext == ".js" {
w.Header().Set("Content-Encoding", "gzip")
gw := gzip.NewWriter(w)
defer gw.Flush()
defer gw.Close()
w = &ResponseWriterBuf{
Rw: w,
Writer: gw,
}
}
}
hf(w, r)
}
}
Here’s an example of parsing into a struct:
type App struct { Id string `json:"id"` Title string `json:"title"` } data := []byte(` { "id": "k34rAT4", "title": "My Awesome App" } `) var app App err := json.Unmarshal(data, &app)
What you’re left with is app populated with the parsed JSON that was in data. You’ll also notice that the go term for parsing json is “Unmarshalling”.
Outputting from a struct works exactly as parsing but in reverse:
data, err := json.Marshal(app)
As with all structs in Go, it’s important to remember that only fields with a capital first letter are visible to external programs like the JSON Marshaller.
You can get rid of the JsonType as well and just use a slice:
package main
import (
"encoding/json"
"log"
)
func main() {
dataJson := `["1","2","3"]`
var arr []string
_ = json.Unmarshal([]byte(dataJson), &arr)
log.Printf("Unmarshaled: %v", arr)
}
// prints out:
// 2009/11/10 23:00:00 Unmarshaled: [1 2 3]
Code on play: https://play.golang.org/p/GNWlylavam
Пример тестирование API HTTP
package apiserver
import (
"bytes"
"encoding/json"
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"rest_api/internal/app/model"
"rest_api/internal/app/store/teststore"
"testing"
)
func TestServer_HandleCreateUser(t *testing.T) {
s := newServer(teststore.New())
testCases := []struct {
name string
payload interface{}
expectedCode int
}{
{
name: "valid",
payload: map[string]interface{}{
"name": "Name",
"email": "current@mail.com",
"password": "123456",
},
expectedCode: http.StatusCreated,
},
{
name: "empty payload",
payload: "invalid",
expectedCode: http.StatusBadRequest,
},
{
name: "invalid email",
payload: map[string]interface{}{
"name": "Name",
"email": "nenormalmail",
"password": "12345",
},
expectedCode: http.StatusUnprocessableEntity,
},
{
name: "invalid password",
payload: map[string]interface{}{
"name": "Name",
"email": "normal@mail.com",
"password":"12345",
},
expectedCode: http.StatusUnprocessableEntity,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b := &bytes.Buffer{}
json.NewEncoder(b).Encode(tc.payload)
rec := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/create", b)
s.ServeHTTP(rec, req)
assert.Equal(t, tc.expectedCode, rec.Code)
})
}
}
func TestServer_HandleAuthorizeUser(t *testing.T) {
s := newServer(teststore.New())
testCases := []struct{
name string
payload interface{}
expectedCode int
}{
{
name: "valid",
payload: map[string]interface{}{
"email": "test@example.com",
"password": "password",
},
expectedCode: http.StatusOK,
},
{
name: "invalid payload",
payload: "invalid",
expectedCode: http.StatusBadRequest,
},
{
name: "invalid user",
payload: map[string]interface{}{
"email": "test@example.com",
"password": "",
},
expectedCode: http.StatusForbidden,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
user := map[string]interface{}{
"name": "User",
"email": "test@example.com",
"password": "password",
}
resp := struct {
Token string `json:"token"`
}{}
b := &bytes.Buffer{}
json.NewEncoder(b).Encode(user)
rec := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/create", b)
s.ServeHTTP(rec, req)
json.NewEncoder(b).Encode(tc.payload)
rec = httptest.NewRecorder()
req, _ = http.NewRequest(http.MethodPost, "/authorize", b)
s.ServeHTTP(rec, req)
json.NewDecoder(rec.Body).Decode(resp)
recToken := resp.Token
assert.Equal(t, tc.expectedCode, rec.Code)
assert.NotNil(t, recToken)
})
}
}
func TestServer_HandleCreateArticle(t *testing.T) {
s := newServer(teststore.New())
testCases := []struct{
name string
payload interface{}
expectedCode int
}{
{
name: "valid",
payload: map[string]interface{}{
"article_header": "Test Article",
"article_text": "Article test text",
"author_id": 1,
},
expectedCode: http.StatusCreated,
},
{
name: "invalid",
payload: "invalid",
expectedCode: http.StatusBadRequest,
},
{
name: "invalid article text",
payload: map[string]interface{}{
"article_header": "Test Article",
"article_text": 7,
"author_id": 1,
},
expectedCode: http.StatusBadRequest,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
us := map[string]interface{}{
"name": "User",
"email": "user@mail.com",
"password": "123456",
}
resp := &struct {
User *model.User `json:"user"`
Token string `json:"token"`
}{}
b := &bytes.Buffer{}
json.NewEncoder(b).Encode(us)
rec := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/create", b)
s.ServeHTTP(rec, req)
json.NewDecoder(rec.Body).Decode(resp)
token := fmt.Sprintf("Baerer %s", resp.Token)
json.NewEncoder(b).Encode(tc.payload)
req, _ = http.NewRequest(http.MethodPost, "/private/create/article", b)
rec = httptest.NewRecorder()
req.Header.Add("Authorization", token)
s.ServeHTTP(rec, req)
assert.Equal(t, tc.expectedCode, rec.Code)
})
}
}
func TestServer_HandleFindArticleByHeading(t *testing.T) {
ts := teststore.New()
ts.Article().CreateArticle(model.TestArticle(t, 5))
s := newServer(ts)
testCases := []struct{
name string
payload interface{}
expectedCode int
}{
{
name: "valid",
payload: map[string]interface{}{
"article_heading": "TestArticle",
},
expectedCode: http.StatusOK,
},
{
name: "invalid article header",
payload: map[string]interface{}{
"article_heading": "Article",
},
expectedCode: http.StatusUnprocessableEntity,
},
{
name: "invalid request",
payload: map[string]interface{}{
"article_heading": 1,
},
expectedCode: http.StatusBadRequest,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b := &bytes.Buffer{}
json.NewEncoder(b).Encode(tc.payload)
rec := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/find/article", b)
s.ServeHTTP(rec, req)
assert.Equal(t, tc.expectedCode, rec.Code)
})
}
}
func TestServer_HandleChangeArticle(t *testing.T) {
ts := teststore.New()
article := model.TestArticle(t, 5)
ts.Article().CreateArticle(article)
s := newServer(ts)
testCases := []struct{
name string
payload interface{}
expectedCode int
}{
{
name: "valid",
payload: map[string]interface{}{
"id" : article.ID,
"article_header": "Updated TestArticle",
"article_text": "updated article text",
},
expectedCode: http.StatusOK,
},
{
name: "invalid",
payload: "invalid",
expectedCode: http.StatusBadRequest,
},
{
name: "invalid id",
payload: map[string]interface{}{
"id" : 1,
"article_header": "Updated TestArticle",
"article_text": "updated article text",
},
expectedCode: http.StatusUnprocessableEntity,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
us := map[string]interface{}{
"name": "User",
"email": "user@mail.com",
"password": "123456",
}
resp := &struct {
User *model.User `json:"user"`
Token string `json:"token"`
}{}
b := &bytes.Buffer{}
json.NewEncoder(b).Encode(us)
rec := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/create", b)
s.ServeHTTP(rec, req)
json.NewDecoder(rec.Body).Decode(resp)
token := fmt.Sprintf("Baerer %s", resp.Token)
json.NewEncoder(b).Encode(tc.payload)
req, _ = http.NewRequest(http.MethodPut, "/private/change/article", b)
rec = httptest.NewRecorder()
req.Header.Add("Authorization", token)
s.ServeHTTP(rec, req)
assert.Equal(t, tc.expectedCode, rec.Code)
})
}
}
func TestServer_HandleDeleteArticle(t *testing.T) {
ts := teststore.New()
article := model.TestArticle(t, 5)
ts.Article().CreateArticle(article)
s := newServer(ts)
testCases := []struct{
name string
payload interface{}
expectedCode int
}{
{
name: "valid",
payload: map[string]interface{}{
"id" : article.ID,
},
expectedCode: http.StatusOK,
},
{
name: "invalid",
payload: "invalid",
expectedCode: http.StatusBadRequest,
},
{
name: "invalid id",
payload: map[string]interface{}{
"id" : 1,
},
expectedCode: http.StatusUnprocessableEntity,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
us := map[string]interface{}{
"name": "User",
"email": "user@mail.com",
"password": "123456",
}
resp := &struct {
User *model.User `json:"user"`
Token string `json:"token"`
}{}
b := &bytes.Buffer{}
json.NewEncoder(b).Encode(us)
rec := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/create", b)
s.ServeHTTP(rec, req)
json.NewDecoder(rec.Body).Decode(resp)
token := fmt.Sprintf("Baerer %s", resp.Token)
json.NewEncoder(b).Encode(tc.payload)
req, _ = http.NewRequest(http.MethodDelete, "/private/delete/article", b)
rec = httptest.NewRecorder()
req.Header.Add("Authorization", token)
s.ServeHTTP(rec, req)
assert.Equal(t, tc.expectedCode, rec.Code)
})
}
}
https://github.com/backspacesh/notebook_api/blob/5bdd97a7b98f85a84d6af4bb51da9d128a520384/internal/app/apiserver/server_internal_test.go
package main
import (
"compress/gzip"
"io"
"log"
"net/http"
"strings"
)
type gzipWriter struct {
http.ResponseWriter
Writer io.Writer
}
func (w gzipWriter) Write(b []byte) (int, error) {
// w.Writer будет отвечать за gzip-сжатие, поэтому пишем в него
return w.Writer.Write(b)
}
func gzipHandle(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// проверяем, что клиент поддерживает gzip-сжатие
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
// если gzip не поддерживается, передаём управление
// дальше без изменений
log.Println("no support")
next.ServeHTTP(w, r)
return
}
// создаём gzip.Writer поверх текущего w
gz, err := gzip.NewWriterLevel(w, gzip.BestSpeed)
if err != nil {
log.Println("erroe")
io.WriteString(w, err.Error())
return
}
defer gz.Close()
w.Header().Set("Content-Encoding", "gzip")
// передаём обработчику страницы переменную типа gzipWriter для вывода данных
next.ServeHTTP(gzipWriter{ResponseWriter: w, Writer: gz}, r)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", defaultHandle())
http.ListenAndServe(":3000", gzipHandle(mux))
}
// HandlerSetURL - создаем запись для url
func defaultHandle() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// заголов ответа json
w.Header().Set("Content-Type", "application/json")
// заголов ответа 201
w.WriteHeader(http.StatusCreated)
w.Write([]byte("привет"))
}
}
https://gist.github.com/evgensr/e048f2a85ec77c2effc4d5a3d84be3f4