Manual 2.0 BootCamp
Material por 8 horas Imersão Prática com Golang Este é um material em Golang que será apresentado "cara a cara" em uma oficina "de mãos dadas" que será realizada em 8 horas.
Prévia Go
Todo o conteúdo visa o nível básico do aluno, muitos exemplos práticos foram feitos com detalhes riqueza para tornar a vida mais fácil do que está iniciando. Se você sabe pouco e quase nada de programação não será problema todo manual foi feito para nivelar começando a avançado. Todas as dificuldades que tive quando comecei a tentar contemplar esse material. Vamos tentar melhorar o material o tempo todo para que possamos ter uma referência quando se trata de ir.
Espero que todos gostem e possam servir de base para aprender e ajudar vários Gophers possíveis.
O conteúdo e as referências usadas são do Site Oficial de Golang e do material que está sendo desenvolvido, que é uma compilação de toda a linguagem de Golang e pode ser conferida aqui em jeffotoni / Compilation.
Algumas apresentações que fiz podem ser vistas aqui Apresentações.
Há milhares de referências hoje em relação a Golang, vamos começar no começo e não poderíamos parar de falar sobre a Golang Tour. Bem, esse site aqui Play Golang ou Play no Go Space, podemos rodar o Golang online.
Temos um link muito interessante que pudemos pesquisar por pacotes escritos em Golang. Confira este link: Go Doc
Nós temos este link que nos apresenta como um manual todas as bibliotecas desenvolvidas no Golang Dev Docs
Aqui encontramos um incrível go, há várias listas como é, e às vezes é legal verificar algumas bibliotecas para nos ajudar com alguns projetos. awesome-go
Logo abaixo alguns canais que participo e posso me encontrar online.
Telegram:
- gobr
- gobh
Overview
Go é uma linguagem poderosa quando se trata de competição e alto desempenho, com uma arquitetura limpa e eficiente. Ela cresce ano após ano e todos os dias as comunidades crescem ainda mais.
Alguns paradigmas foram quebrados para torná-lo uma linguagem de alto desempenho, onde a competição é um dos seus pontos fortes. O Go facilita a criação de programas que aproveitam ao máximo as máquinas multicore e em rede, enquanto o novo sistema de tipos permite que você crie programas flexíveis e modulares.
É uma linguagem rápida e estaticamente compilada que se parece com uma linguagem interpretada dinamicamente. Este recurso Golang se torna uma linguagem única como o assunto é web.
Go é uma linguagem de programação compilada, competitiva, forte e estaticamente tipada. É uma linguagem de "Uso Geral" que pode ser usada para resolver vários problemas e em diferentes áreas. Problemas envolvendo concorrência, aplicações web, aplicações de alto desempenho, desenvolvimento de APIs, soquetes de comunicação etc ... É onde a linguagem está se tornando cada vez mais proeminente no mercado e nas comunidades.
Introdução conteúdos
Go está na versão 1.17.2 e caminhando para versão 2.0, todo ciclo de desenvolvimento de seu core respeita o arcaboço e designer do que foi proposto no inicio do surgimento da linguagem. O objetivo centrarl sobre Go é torna-lá cada vez mais produtiva e deixar a linguagem ainda mais simples para os que forem desenvolver em Go.
Durante o processo, a equipe de desenvolvimento Go apresentou quatro maneiras principais de simplificar a experiência geral de escrever programas Go: Remodelando, Redefinindo, Removendo e Restringindo chamado os quatro R's da simplificações.
Neste cenário percebe-se claramente que no designer de Go "Menos é exponencialmente Mais", e nasceu o termo "Jeito Go de fazer as coisas".
Aqui um post completo "Simplificando a Complexidade. O Inicio"
Espero que todos gostem e possam servir de base para aprender e ajudar vários Gophers possíveis.
O conteúdo e as referências usadas são do Site Oficial de Golang e grande parte é feita sobre a ótica prática do autor.
Fundamentos da Linguagem
Vídeo 1
1 - Compilada e estática
$ go build
$ go build -ldflags="-s -w" hello.go
Compilando para Lambda
$ GOOS=linux GOARCH=amd64 go build -o lambda lambda.go
Compilando para WebAssembly
$ GOARCH=wasm GOOS=js go build -o test.wasm hello.go
Compilando e gerando um .o file, e gera o seu assembly
$ GOOS=linux GOARCH=amd64 go tool compile -S hello.go
$ go tool compile -S hello.go > hello.S
$ go build -gcflags -S hello.go
Gerando o dump do assembly
$ go tool objdump hello > ref-assembly
Isso está no código para quando compilamos para diferenciar e compilar somente aqueles que contêm essas tags. Ao compilar basta informar a tag que você colocou no seu código.
// +build !windows
$ go build -o main main
Buildmode
$ go help buildmode
-buildmode=plugin
plugin/randseed.so
package main
import (
"crypto/md5"
"fmt"
"io"
"math/rand"
"strconv"
"time"
)
func RandSeed() string {
rand.Seed(time.Now().Unix())
return Md5(strconv.Itoa(rand.Intn(1000000) + rand.Intn(100000)))
}
func Md5(text string) string {
h := md5.New()
io.WriteString(h, text)
return (fmt.Sprintf("%x", h.Sum(nil)))
}
$ go build -buildmode=plugin -o plugin/randseed.so .plugin/randseed.go
package main
import "plugin"
import "io"
import "strings"
import "os"
func main() {
p, err := plugin.Open("../plugin/randseed.so")
if err != nil {
panic(err)
}
f, err := p.Lookup("RandSeed")
if err != nil {
panic(err)
}
randstr := f.(func() string)())
io.Copy(os.Stdout, strings.NewReader(randstr))
}
$ go run main.go
Compilando estática em C
#include
#include
int main(void)
{
int i;
printf("Gerando 10 valores aleatorios:\n\n");
for (i = 0; i < 10; i++)
{
/* gerando valores aleatórios entre zero e 100 */
printf("%d ", rand() % 100);
}
return 0;
}
$ gcc -static -o rand rand.c -lm
Variáveis ambiente importantes
$ GO111MODULE=on GOMAXPROCS=NUMERO-CPU go build
Vídeo 2
3 - Paradigma Concorrente
Canais são como filas que fornece acesso sincronizado automático entre goroutines. Esse entendimento nos levará a escrever códigos concorrentes.
O importante é sempre focar como eles se comportam. Um canal permite que uma goroutine sinalize outra goroutine sobre um determinado evento. A sinalização está no centro de tudo que você deve fazer com os canais. Pensar nos canais como um mecanismo de sinalização permitirá que você escreva um código melhor com um comportamento bem definido e mais preciso.
Para entender como funciona a sinalização, precisamos entender seus três atributos:
- Garantia de entrega
- Estado
- Com ou sem dados
Esses três atributos trabalham juntos para criar uma filosofia de design em torno da sinalização.
Garantia de entrega
A garantia de entrega é baseada em uma pergunta: "Preciso de uma garantia de que o sinal enviado por uma goroutine específica foi recebido?"
func main() {
ch := make(chan string)
go func() {
p := <-ch // Receive
}()
ch <- "goManual" // Send
}
Estado
O comportamento de um canal é diretamente influenciado pelo seu estado atual. O estado de um canal pode ser nulo, aberto ou fechado.
// ** nil channel
// Um chan é um estado com nil quando ele é declardo com zero value
var ch chan string
// Um chan pode ser alterado com nil e torna um estado de forma explicita
ch = nil
// ** open channel
// Um chan é de estado aberto quando é usado built-in function make.
ch := make(chan string)
// ** closed channel
// Um chan é fechado o estado quando é usado built-in function close.
close(ch)
Quando um canal está em um estado nulo , qualquer tentativa de envio ou recebimento no canal será bloqueada. Quando um canal está em estado aberto , os sinais podem ser enviados e recebidos. Quando um canal é colocado em um estado fechado , os sinais não podem mais ser enviados, mas ainda é possível receber sinais.
Esses estados fornecerão os diferentes comportamentos que você precisa para as diferentes situações que encontrar. Ao combinar Estado com Garantia de Entrega , você pode começar a analisar os custos / benefícios que você está incorrendo como resultado de suas escolhas de projeto. Em muitos casos, você também será capaz de identificar rapidamente os bugs apenas lendo o código, porque você entende como o canal vai se comportar.
▹ Paradigma Concorrente - Exemplo 1
▹ Paradigma Concorrente - Exemplo 2
Sinalização com dados
Quando você vai sinalizar com dados, existem três opções de configuração de canal que você pode escolher dependendo do tipo de garantia que você precisa.
Figura 3: sinalização com dados

As três opções de canal são: sem buffer , Buffered> 1 ou Buffered = 1.
O tamanho do buffer nunca deve ser um número aleatório. Ele deve sempre ser calculado para alguma restrição bem definida. Não há infinito na computação, tudo deve ter alguma restrição bem definida, seja tempo ou espaço.
4 - Tipagem estática
Go é uma linguagem de programação compilada e pertence a família da linguagem C. Contudo, seu tempo de compilação é muito mais rápido do que outras linguagens da mesma família. Ela possui apenas 25 palavras-chave (palavras reservadas). Vamos dar uma olhada nessas palavras antes de começar.
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
▹ Tipagem estática - Exemplo 1
5 - Semântica é clara
▹ Semântica é clara - Exemplo 1
6 - Sintaxe é limpa
package main
import "fmt"
func main() {
io.Copy(os.Stdout, strings.NewReader(f.(func() string)()))
}
7 - É de uso Geral
package main
/*
#include
#include
void GoPrint(char* s) {
printf("%s\n", s);
}
*/
import "C"
func main() {
C.GoPrint(C.CString("Manual 2.0\n"))
}
$ go run main.go
8 - Plataformas: Windows, Linux, Mac e FreeBSD
Instalação
Introdução à Instalação
Em golang a instalação é muito simples e prática, para Linux, Mac e Windows.
Basta copiar os arquivos para o diretório correto para cada sistema operacional e exportar os caminhos para o ambiente e solicitar, golang está instalado.
Vamos dar uma olhada em como fazemos isso.
Instalação
Vamos baixar o arquivo, descompactá-lo e instalá-lo em /usr/local/go, se tivermos golang já instalado na máquina teremos que remover o existente para deixar nossa instalação como única. Vamos criar nosso diretório em nosso espaço de trabalho e testar para ver se tudo correu bem.
Linux
$ sudo rm -rf /usr/local/go
$ wget https://dl.google.com/go/go1.11.5.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
$GOPATH
$GOPATH é o golang em seu $HOME, isso é necessário para que seus projetos usem o pkg e construam corretamente. Isso era obrigatório para todas as versões anteriores à versão 1.11. O legal é que a partir de agora não teremos que criar projetos no $GOPATH, podemos criar em qualquer outro diretório que não esteja no $GOPATH.
Aqui está o link para a proposta de versão proposta: Módulos Go versionados ou Go 1.11 Modules
Vamos detalhar como trabalhar com o go mod, foi uma das melhores experiências que tive para projetos de versionamento usando Golang.
Vamos configurar nosso ambiente para rodar o Go. Adicione /usr/local/go/bin à variável de ambiente PATH. Você pode fazer isso adicionando esta linha ao seu /etc/profile (para uma instalação em todo o sistema) ou $HOME/.profile.
$ export PATH=$PATH:/usr/local/go/bin
Nota: as alterações feitas em um arquivo de perfil podem não se aplicar até a próxima vez que você fizer login no seu computador. Para aplicar as alterações imediatamente, basta executar os comandos do shell diretamente ou executá-los a partir do perfil usando um comando como o source $HOME/.profile.
$ echo "export GOPATH=$HOME/go" >> $HOME/.profile
$ echo "export PATH=$PATH:/usr/local/go/bin" >> $HOME/.profile
$ echo "export PATH=$PATH:$GOPATH/bin" >> $HOME/.profile
Teste nossa instalação
Vamos executar a versão para ver se tudo está correto.
$ go version
$ go version go1.11.5 linux/amd64
Verifique se o Go está instalado corretamente configurando um espaço de trabalho e construindo um programa simples, da seguinte maneira.
Crie seu diretório de área de trabalho, $HOME/go. (Se você quiser usar um diretório diferente, precisará definir a variável de ambiente $GOPATH.)
Em seguida, faça o diretório src/hello dentro de sua área de trabalho e, nesse diretório, crie um arquivo chamado hello.go que se pareça com:
Workspace
O espaço de trabalho é o nosso local de trabalho, onde organizaremos nossos diretórios com nossos projetos. Como mostrado acima, até o Go versão 1.11 fomos forçados a fazer tudo sob o Espaço de Trabalho. $GOPATH Down Projects.
$ export GOPATH=$HOME/go
$ mkdir $HOME/go
$ mkdir $HOME/go/src
$ mkdir $HOME/go/src/hello
$ vim $HOME/go/src/hello/hello.go
$GOPATH/
|-src
|-hello
|-hello.go
Projeto de Exemplo
$ export GOPATH=$HOME/go
$ mkdir $HOME/go/src/project1
$ mkdir $HOME/go/src/project1/my-pkg
$ mkdir $HOME/go/src/project1/my-cmd
$ mkdir $HOME/go/src/project1/my-vendor
$ mkdir $HOME/go/src/project1/my-logs
$ mkdir $HOME/go/src/project1/my-models
$ mkdir $HOME/go/src/project1/my-repo
$ mkdir $HOME/go/src/project1/my-handler
Projeto de Exemplo
$GOPATH/
|-src
|-github.com/user/project1/
|-cmd (do project1)
|-main.go
|-vendor
|-logs
|-models
|-repo
|-handler
|-github.com/user/project2/
....
....
A variável de ambiente $GOPATH informa a ferramenta Go onde sua área de trabalho está localizada.
$ go get github.com/user/project1
O comando go get recupera repositórios de origem da Internet e os coloca em sua área de trabalho. Os caminhos do pacote são importantes para a ferramenta Ir. Usar "github.com/..." significa que a ferramenta sabe como buscar seu repositório.
No cenário acima, tudo teria que ficar em nosso $ GOPATH para que nossos projetos funcionassem corretamente.
Fora do $GOPATH
Agora podemos fazer nossos projetos sem estar em $GOPATH, podemos, por exemplo, fazê-lo em qualquer diretório.
Projeto Fora do GOPATH
$ export GOPATH=$HOME/go
$ mkdir $HOME/2019/project1
$ mkdir $HOME/2019/project1/my-pkg
$ mkdir $HOME/2019/project1/my-cmd
$ mkdir $HOME/2019/project1/my-logs
$ mkdir $HOME/2019/project1/my-models
$ mkdir $HOME/2019/project1/my-repo
$ mkdir $HOME/2019/project1/my-handler
$HOME/
|-2019
|-github.com/user/project1/
|-cmd
|-main.go
|-vendor
|-logs
|-models
|-repo
|-handler
Podemos colocar nosso projeto em qualquer diretório agora.
$HOME/
|-any-directory
|-github.com/user/project1/
|-cmd
|-main.go
|-vendor
|-logs
|-models
|-repo
|-handler
Para o cenário acima, teremos que usar o go mod em nosso projeto para que todos os pacotes externos possam funcionar corretamente, assim poderemos gerenciá-los corretamente e versão. Mais informações podem ser encontradas aqui: Wiki Go Modules
Exemplo prático de como você irá proceder:
$ go mod init github.com/user/project1
Linguagem Golang
Para mais informações sobre a linguagem, acesse o site Effective Go.
Caso tenha alguma dúvida, confira a FAQ oficial clicando aqui.
Hello World
Vamos aprender como imprimir dados na tela - que na verdade é a saída padrão stdout - usando as funções print, println e fmt.Println.
package main
import "fmt"
func main() {
// println é uma função embutida (no tempo de execução)
// que pode eventualmente ser removida.
println("Hello, World!")
// fmt.Println é uma função da lib "fmt" uma biblioteca padrão Go,
// ela é bem completa e bem extensa.
fmt.Println("Hello, World!")
}
Comentários
Go fornece comentários de bloco estilo C e comentários de linha estilo C++. Comentários de linha são a norma.
Os comentários do bloco aparecem principalmente como comentários do pacote, mas são úteis dentro de uma expressão ou para desabilitar grandes faixas de código.
package main
import "fmt"
func main() {
// Comantário de linha
fmt.Println("Hello", "World ", 2022, " Go!")
/* Comentário de bloco
fmt.Println("Golang")
fmt.Println("é legal!")
*/
}
Keywords e Operadores
Go possui vários símbolos e palavras reservadas que não devem ser usadas em seu código para declarar identificadores. Isso irá causar um panic, que irá encerrar seu código na hora.
package main
import "fmt"
func main() {
// Isto irá gerar um erro de sintaxe,
// substitua por um identificador válido
go := "Gopher, irá gerar um erro!"
fmt.Println(go)
// Outro erro aqui
func := 2 + &&
fmt.Println(func)
}
Segue uma lista com as keywords disponíveis atualmente:
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
any | comparable |
E também uma lista com os símbolos disponíveis:
+ | & | += | &= | && | == | != | ( | ) |
- | | | -= | |= | || | < | <= | [ | ] |
* | ^ | *= | ^= | <- | > | >= | { | } |
/ | << | /= | <<= | ++ | = | := | , | ; |
% | >> | %= | >>= | -- | ! | ... | . | : |
&^ | &^= | ~ |
Para mais informações, clique aqui
Rune Literals
Uma rune literal representa uma rune constant, um valor inteiro que identifica um ponto de código Unicode.
Como o texto de origem do Go são caracteres Unicode codificados em UTF-8, vários bytes codificados em UTF-8 podem representar um único valor inteiro.
Por exemplo, o literal 'a' contém um único byte que representa um literal a, Unicode U + 0061, valor 0x61, enquanto 'ä' contém dois bytes (0xc3 0xa4).
package main
import "fmt"
func main() {
fmt.Println([]byte("\a"))
fmt.Println("\u65e5本\U00008a9e")
fmt.Println("\xff\u00FF")
fmt.Println("=>","\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e")
}
String Literals
Um string literal representa uma string constant obtida da concatenação de uma sequência de caracteres.
String literal interpretados são sequências de caracteres entre aspas duplas, . Entre as aspas, qualquer caractere pode aparecer, exceto nova linha e aspas duplas sem escape.
package main
import "fmt"
func main() {
fmt.Println("日本語", []byte("日本語"))
fmt.Println(`日本語`, []byte(`日本語`))
fmt.Println("\u65e5\u672c\u8a9e", []byte("\u65e5\u672c\u8a9e"))
}
Constantes
Constantes e variáveis são espaços de memória reservados para o tipo de dados que você deseja trabalhar.
Constantes são valores fixos que você utilizará em seu programa e variáveis são valores que precisam variar durante o tempo de execução do seu programa.
boolean | rune | integer |
floating-point | complex | string |
package main
import "fmt"
const (
PATH = "/myhome/app"
KB int = 1 << (10 * iota) // 1Kb
MALE bool = true
DOLLAR = 3.99
)
const a = 2
const Σ = 1 - 0.707i // (untyped complex constant)
const Φ = iota*1i - 1/1i // (untyped complex constant)
const Pi float64 = 3.14159265358979323846
func main() {
fmt.Println(PATH)
fmt.Println(KB)
fmt.Println(MALE)
fmt.Println(DOLLAR)
fmt.Println(a)
fmt.Println(Σ)
fmt.Println(Φ)
fmt.Println(Pi)
}
Iota
Em uma declaração de constante, o identificador pré-declarado iota representa constantes inteiras sucessivas não digitadas. Seu valor é o índice do respectivo constSpec nessa declaração de constante, começando em zero.
Ele pode ser usado para construir um conjunto de constantes relacionadas, como apresentado no exemplo.
package main
import "fmt"
const (
c0 = iota // c0 == 0
c1 // c1 == 1
c2 // c2 == 2
)
const (
u float64 = iota * 42 // u == 0.0 (float64 constant)
v // v == 42.0 (float64 constant)
w // w == 84.0 (float64 constant)
)
const x = iota // x == 0
const y = iota // y == 0
func main() {
fmt.Println(c0, c1, c2)
fmt.Println(u, v, w)
fmt.Println(x, y)
}
Variáveis
Uma variável é um local de armazenamento para armazenar um valor. O conjunto de valores permitidos é determinado pelo tipo da variável.
O tipo estático de uma variável é o tipo fornecido em sua declaração, o tipo fornecido na nova chamada ou literal composto, ou o tipo de um elemento de uma variável estruturada.
package main
import "fmt"
type T struct{ y int }
var x interface{} // x é nil e tem o tipo estático interface{}
var v *T // v tem valor nil e tipo estático *T
type tv T
func main() {
x = 42 // x tem valor 42 tipo dinâmico int
fmt.Println(x)
x = v // x tem valor (*T)(nil) e tipo dinâmico *T
fmt.Println(x)
x = T{y: 2}
fmt.Println(x)
vx := tv{y: 10}
fmt.Println(vx)
}
Declaração de Variáveis
A declaração de uma variável cria uma ou mais variáveis, ataxando identificadores correspondentes a elas, e atribuindo a cada uma um tipo e um valor inicial.
Se uma lista de expressões é fornecida, as variáveis são inicializadas com as expressões seguindo as regras para atribuições. Caso contrário, cada variável é inicializada com seu valor zero.
Se um tipo estiver presente, cada variável receberá esse tipo. Caso contrário, scada variável recebe o tipo do valor de inicialização correspondente na atribuição.
package main
import (
"fmt"
"math"
)
var d = math.Sin(0.5) // d é do tipo float64
var i = 42 // i é do tipo int
//var n = nil // Declaração ilegal
func main() {
a := "Go"
fmt.Println(d)
fmt.Println(i)
fmt.Println(a)
}
Comandos Go
Introdução Comandos Go
Em golang, temos um arsenal para nos ajudar quando se trata de compilar, testar, documentar, gerenciar perfis etc.
bug iniciar um relatório de bug
build compilar pacotes e dependências
clean remover arquivos de objetos e arquivos em cache
doc mostrar documentação para pacote ou símbolo
env imprimir informações do ambiente Go
fix atualizar pacotes para usar novas APIs
fmt gofmt (reformatar) fontes de pacotes
generate gerar arquivos Go processando a origem
get baixar e instalar pacotes e dependências
install compilar e instalar pacotes e dependências
list lista de pacotes ou módulos
mod manutenção de módulo
run compilar e executar o programa Go
test testar pacotes
tool executar ferramenta especifica do Go
version mostrar versão Go
vet relatar erros prováveis em pacotes
Use "go help" para mais informações sobre um comando.
Go Run
Uso:
go run [build flags] [-exec xprog] package [arguments...]
Executar compila e executa o pacote principal chamado Go. Normalmente, o pacote é especificado como uma lista de arquivos de origem .go, mas também pode ser um caminho de importação, um caminho do sistema de arquivos ou um padrão que corresponda a um único pacote conhecido, como em "go run ." ou "go run my/cmd".
Por padrão, 'go run' executa o binário compilado diretamente: 'a.out arguments ...'. Se o flag -exec é dado, 'go run' invoca o binário usando xprog:
Se o flag -exec não for fornecido, GOOS ou GOARCH for diferente do padrão do sistema e um programa chamado go_$GOOS_$GOARCH_exec puder ser localizado no caminho de procura atual, 'go run' chamará o binário usando esse programa, por exemplo 'go_nacl_386_exec a.out arguments ...'. Isso permite a execução de programas compilados quando um simulador ou outro método de execução estiver disponível.
O status de saída de Run não é o status de saída do binário compilado.
Para mais informações sobre os sinalizadores de construção, consulte 'go help build'. Para mais informações sobre como especificar pacotes, consulte 'go help packages'.
Veja abaixo um exemplo:
// testando println
package main
func main() {
println("Debugando meu sistema com println")
}
Go run:
go run println.go
Saída:
Debugando meu sistema com println
Go Build
Build compila os pacotes nomeados pelos caminhos de importação, junto com suas dependências, mas não instala os resultados.
Ao compilar pacotes, o build ignora os arquivos que terminam em '_test.go'.
O flag -o, permitido somente ao compilar um único pacote, força a compilação a gravar o executável ou objeto resultante no arquivo de saída nomeado, em vez do comportamento padrão descrito nos dois últimos parágrafos.
O flag -i instala os pacotes que são dependências do destino.
$ go build [-o output] [-i] [build flags] [packages]
Veja um exemplo:
package main
import "fmt"
func main() {
fmt.Println("Go Manual 2.0")
}
Saída:
Go Manual 2.0
Compilação normal
go build -o hello hello.go
Saída:
$ ls -lh
-rwxrwxr-x 1 root root **1,9M** jan 18 12:42 hello
-rw-rw-r-- 1 root root 75 jan 17 12:04 hello.go
Deixando o arquivo menor após a compilação
go build -ldflags="-s -w" hello.go
Saída:
$ ls -lh
-rwxrwxr-x 1 root root **1,3M** jan 18 12:42 hello
-rw-rw-r-- 1 root root 75 jan 17 12:04 hello.go
Go Install
Instalar pacotes e dependências.
Uso:
$ go install [-i] [build flags] [packages]
Instale compila e instala os pacotes nomeados pelos caminhos de importação.
O flag -i também instala as dependências dos pacotes nomeados.
Para mais informações sobre os sinalizadores de construção, consulte 'go help build'. Para mais informações sobre como especificar pacotes, consulte 'go help packages'.
Go Get
O comando 'go get' muda o comportamento dependendo se o comando go está sendo executado no modo ciente de módulo ou no modo GOPATH herdado. Este texto de ajuda, acessível como 'go help module-get', mesmo no modo GOPATH legado, descreve o 'go get' enquanto ele opera no modo ciente de módulo.
Uso:
$ go get [-d] [-m] [-u] [-v] [-insecure] [build flags] [packages]
Obtenha downloads dos pacotes nomeados pelos caminhos de importação, junto com suas dependências. Em seguida, instala os pacotes nomeados, como 'go install'.
Veja as bandeiras aceitas abaixo:
O flag -d instrui a parar após o download dos pacotes; isto é, instrui não instalar os pacotes.
O flag -f, válido apenas quando -u está energizado, força get -u para não verificar se cada pacote foi retirado do repositório de controle de origem implícito por seu caminho de importação. Isso pode ser útil se a fonte for uma bifurcação local do original.
O flag -fix instrui para executar a ferramenta de correção nos pacotes baixados antes de resolver dependências ou construir o código.
O flag -insecure permite buscar a partir de repositórios e resolver domínios personalizados usando esquemas inseguros, como HTTP. Use com cuidado.
O flag -t instrui também a baixar os pacotes necessários para construir os testes para os pacotes especificados.
O flag -u instrui a usar a rede para atualizar os pacotes nomeados e suas dependências. Por padrão, get usa a rede para verificar os pacotes ausentes, mas não os utiliza para procurar atualizações nos pacotes existentes.
O flag -v permite progresso detalhado e saída de depuração.
Exemplos:
$ go get -v github.com/guptarohit/asciigraph
$ go get -u github.com/mxk/go-sqlite
$ go get -v github.com/google/uuid
$ go get -v github.com/sirupsen/logru
Go Mod
Um módulo é uma coleção de pacotes Go relacionados. Módulos são a unidade de intercâmbio de código-fonte e controle de versão. O comando go tem suporte direto para trabalhar com módulos, incluindo gravação e resolução de dependências em outros módulos. Os módulos substituem a antiga abordagem baseada em GOPATH para especificar quais arquivos de origem são usados em uma determinada compilação.
Uso:
$ go mod [arguments]
Um módulo é definido por uma árvore de arquivos de código-fonte Go com um arquivo go.mod no diretório-raiz da árvore. O diretório que contém o arquivo go.mod é chamado de raiz do módulo. Normalmente, a raiz do módulo também corresponderá a uma raiz de repositório de código-fonte (mas, em geral, não precisa). O módulo é o conjunto de todos os pacotes Go na raiz do módulo e seus subdiretórios, mas excluindo subárvores com seus próprios arquivos go.mod.
O "caminho do módulo" é o prefixo do caminho de importação correspondente à raiz do módulo. O arquivo go.mod define o caminho do módulo e lista as versões específicas de outros módulos que devem ser usados ao resolver importações durante uma construção, fornecendo seus caminhos e versões de módulo.
Por exemplo, este go.mod declara que o diretório que o contém é a raiz do módulo com o caminho example.com/m, e também declara que o módulo depende de versões específicas de golang.org/x/text e gopkg.in/yaml.v2:
$ go mod init github.com/user/gomyproject
require (
golang.org/x/text v0.3.0
gopkg.in/yaml.v2 v2.1.0
)
O arquivo go.mod também pode especificar substituições e versões excluídas que só se aplicam ao construir o módulo diretamente; eles são ignorados quando o módulo é incorporado em uma construção maior. Para mais informações sobre o arquivo go.mod, consulte 'go help go.mod'.
Para iniciar um novo módulo, basta criar um arquivo go.mod na raiz da árvore de diretórios do módulo, contendo apenas uma instrução do módulo. O comando 'go mod init' pode ser usado para fazer isso:
$ go mod init github.com/user/gomyproject
Em um projeto que já utiliza uma ferramenta de gerenciamento de dependências existente, como godep, glide ou dep, o 'go mod init' também incluirá instruções requeridas que correspondam à configuração existente.
Uma vez que o arquivo go.mod existe, nenhuma etapa adicional é necessária: comandos como 'go build', 'go test' ou até 'go list' adicionarão automaticamente novas dependências conforme necessário para satisfazer as importações.
Os comandos são:
download baixar módulos no cache local
edit editar go.mod de ferramentas ou scripts
graph gráfico de requisitos do módulo de impressão
init inicializar novo módulo no diretório atual
tidy adicionar faltando e remover módulos não utilizados
vendor fazer cópia -------vendida------- de dependências
verify verificar dependências esperavam conteúdo
why explicar por que pacotes ou módulos são necessários
Use "go help mod" para mais informações sobre um comando.
Go Mod Init
Inicializar novo módulo no diretório atual.
Uso:
$ go mod init [module]
Init inicializa e grava um novo go.mod no diretório atual, criando, na verdade, um novo módulo com raiz no diretório atual. O arquivo go.mod não deve existir. Se possível, o init irá adivinhar o caminho do módulo a partir dos comentários de importação (consulte 'go help importpath') ou da configuração do controle de versão. Para substituir essa suposição, forneça o caminho do módulo como um argumento.
$ go mod init github.com/user/gomyproject2
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/didip/tollbooth v4.0.0+incompatible
github.com/go-sql-driver/mysql v1.4.1
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
)
Go Mod Vendor
O comando go mod mod vendor baixará todas as dependências para o diretório "vendor". Ao usar o go mod mod, os pacotes não estão no seu diretório.
$ cd gomyproject2
$ go mod vendor
Saída:
$ ls -lh vendor
total 8,0K
drwxrwxr-x 3 root root 4,0K jan 27 01:47 github.com
-rw-rw-r-- 1 root root 137 jan 27 01:47 modules.txt
GO111MODULE
O Go 1.11 inclui suporte preliminar para os módulos Go, incluindo um novo comando 'go get' que reconhece os módulos. Nós pretendemos continuar revisando este suporte, preservando a compatibilidade, até que ele possa ser declarado oficial (não mais preliminar), e então, em um ponto posterior, podemos remover o suporte para o trabalho no GOPATH e o antigo comando 'go get'.
A maneira mais rápida de aproveitar o novo suporte ao módulo Go 1.11 é verificar seu repositório em um diretório fora de GOPATH/src, criar um arquivo go.mod (descrito na próxima seção) e executar comandos go a partir desse arquivo árvore.
Para um controle mais refinado, o suporte a módulos no Go 1.11 respeita uma variável de ambiente temporária, GO111MODULE, que pode ser definida como um dos três valores de string: off, on ou auto (o padrão). Se GO111MODULE=off, então o comando go nunca usa o novo suporte ao módulo. Em vez disso, ele procura nos diretórios de fornecedores e no GOPATH para localizar dependências; agora nos referimos a isso como "modo GOPATH". Se GO111MODULE=on, então o comando go requer o uso de módulos, nunca consultando o GOPATH. Nós nos referimos a isso como o comando sendo ciente do módulo ou em execução no "modo de reconhecimento de módulo". Se GO111MODULE=auto ou não estiver definido, o comando go ativa ou desativa o suporte a módulos com base no diretório atual. O suporte a módulos é ativado somente quando o diretório atual está fora de GOPATH/src e ele contém um arquivo go.mod ou está abaixo de um diretório contendo um arquivo go.mod.
No modo ciente de módulo, GOPATH não define mais o significado de importações durante uma compilação, mas ainda armazena dependências baixadas (em GOPATH/pkg/mod) e comandos instalados (em GOPATH/bin, a menos que GOBIN esteja definido).
Confira abaixo como usamos o comando:
$ GO111MODULE=on go run myprogram.go
$ GO111MODULE=on go build myprogram.go
Quando nosso projeto não está em nosso $GOPATH não é necessário usar GO111MODULE, mas quando nosso projeto está em $GOPATH e nós queremos usar "go mod" nós precisamos informar isto ao compilador usando GO111MODULE...
Go Test
Pacotes de teste
Uso:
$ go test [build/test flags] [packages] [build/test flags & test binary flags]
O teste Go automatiza o teste dos pacotes nomeados pelos caminhos de importação. Imprime um resumo dos resultados do teste no formato:
=== RUN TestWhatever
--- PASS: TestWhatever (0.00s)
PASS
ok command-line-arguments 0.001s
O pacote de teste é executado lado a lado com o comando go test. O teste de pacote deve ter o sufixo "_test.go". Podemos dividir os testes em vários arquivos seguindo esta convenção. Por exemplo: "myprog1_test.go" e "myprog2_test.go".
Devemos colocar nossas funções de teste nesses arquivos de teste.
Cada função de teste é uma função pública exportada cujo nome começa com "Test", aceita um ponteiro para um objeto testing.T e não retorna nada. Como isso:
Exemplo um / myprog1_test:
package main
import "testing"
func TestWhatever(t *testing.T) {
// Teste aqui
}
$ go test -v
Saída:
=== RUN TestWhatever
--- PASS: TestWhatever (0.00s)
PASS
ok command-line-arguments 0.001s
O objeto T fornece vários métodos que podemos usar para indicar falhas ou erros de log.
Exemplo dois / myprog2_test:
package main
import "testing"
func TestSum(t *testing.T) {
x := 1 + 1
if x != 11 { // forçando o erro
t.Error("Erro! 1 + 1 não é igual a 2, recebi", x)
}
}
$ go test -v
Saída:
=== RUN TestSum
-- FAIL: TestSum (0.00s)
myprog1_test.go:12: Erro! 1 + 1 não é igual a 2, recebi 2
FAIL
FAIL command-line-arguments 0.001s
Neste exemplo, faremos um exame como seria em nossos projetos.
Neste programa vou passar parâmetro em tempo de compilação ou em nossa execução para facilitar e também servir como exemplo o uso de "ldflags" que podemos usar em **go run -ldflags** e go build -ldflags
De um check-in em nosso código abaixo / main.go:
import "strconv"
import (
"github.com/jeffotoni/goManualdevops/examples/tests/pkg/math"
)
var (
x, y string
xi, yi int
)
func init() {
xi, _ = strconv.Atoi(x)
yi, _ = strconv.Atoi(y)
}
func main() {
println(math.Sum(xi, yi))
}
Agora temos uma função Sum em um pacote que criamos em pkg/math/sum.go
package math
func Sum(x, y int) int {
return x + y
}
Criamos nosso arquivo de teste em pkg/math/sum_test.go
package math
import "testing"
func TestSum(t *testing.T) {
type args struct {
x int
y int
}
tests := []struct {
name string
args args
want int
}{
// TODO: adicionar casos de teste
{"test_1: ", args{2, 2}, 4},
{"test_2: ", args{-2, 6}, 4},
{"test_3: ", args{-4, 8}, 4},
{"test_4: ", args{5, 7}, 12},
{"test_5: ", args{8, 8}, 15}, // forçando o erro
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Sum(tt.args.x, tt.args.y); got != tt.want {
t.Errorf("Sum() = %v, want %v", got, tt.want)
}
})
}
}
$ cd pkg/math/
$ go test -v
Saída:
=== RUN TestSum
=== RUN TestSum/test_1:_
=== RUN TestSum/test_2:_
=== RUN TestSum/test_3:_
=== RUN TestSum/test_4:_
=== RUN TestSum/test_5:_
--- FAIL: TestSum (0.00s)
--- PASS: TestSum/test_1:_ (0.00s)
--- PASS: TestSum/test_2:_ (0.00s)
--- PASS: TestSum/test_3:_ (0.00s)
--- PASS: TestSum/test_4:_ (0.00s)
--- FAIL: TestSum/test_5:_ (0.00s)
sum_test.go:29: Sum() = 16, want 15
FAIL
exit status 1
FAIL github.com/jeffotoni/goManualdevops/examples/tests/pkg/pkg/math 0.001s
Converte para json a saída dos testes.
$ go test -v -json
Verifique sua saída na tela do seu terminal para ver a saída do json.
Agora que salvamos nosso pkg / math / sum.go, vamos fazer um main.go fazendo a chamada neste pacote. Mas primeiro vamos executar o go mod para gerenciar nossos pacotes e versões corretamente.
Verifique o comando abaixo:
$ go mod init github.com/jeffotoni/goManualdevops/examples/tests/pkg
Saída:
go: finding github.com/jeffotoni/goManualdevops/examples/tests/pkg/math latest
go: finding github.com/jeffotoni/goManualdevops/examples/tests latest
go: finding github.com/jeffotoni/goManualdevops/examples latest
go: finding github.com/jeffotoni/goManualdevops latest
go: downloading github.com/jeffotoni/goManualdevops v0.0.0-20190127023912-a2fa53299867
0
Agora podemos dar build ou run em nosso main.go. Vamos rodar para rodar usando o sinalizador "-ldflags" para passar o parâmetro para o nosso código em tempo de compilação.
$ go run -ldflags "-X main.x=2 -X main.y=3" main.go
Saída:
5
$ go run -ldflags "-X main.x=7 -X main.y=3" main.go
Saída:
10
Vamos reforçar nossos conhecimentos em: Json / Marsha, Unmarshal + structs
Json
O pacote json implementa a codificação e decodificação de JSON, conforme definido no RFC 7159 . O mapeamento entre os valores JSON e Go é descrito na documentação das funções Marshal e Unmarshal.
Introdução
JSON (JavaScript Object Notation) é um formato simples de troca de dados. Sintacticamente assemelha-se aos objetos e listas de JavaScript. É mais comumente usado para comunicação entre back-ends da web e programas JavaScript em execução no navegador, mas é usado em muitos outros lugares também. Sua home page, json.org, fornece uma definição clara e concisa do padrão.
Com o pacote json , é muito fácil ler e gravar dados JSON de seus programas Go.
Json Marshal
Marshal retorna a codificação JSON de v .
Marshal percorre o valor v recursivamente. Se um valor encontrado implementar a interface Marshaler e não for um ponteiro nulo, Marshal chama seu método MarshalJSON para produzir JSON. Se nenhum método MarshalJSON estiver presente, mas o valor implementar encoding.TextMarshaler, Marshal chama seu método MarshalText e codifica o resultado como uma string JSON.
package main
import (
"encoding/json"
"fmt"
"log"
)
type ApiLogin struct {
Name string `json:"name"`
Cpf string `json:"cpf"`
}
func main() {
a := ApiLogin{"Jefferson", "033.343.434-89"}
fmt.Println(a)
m, err := json.Marshal(a)
if err != nil {
log.Println(err)
}
// show bytes
fmt.Println(m)
// show string json
fmt.Println(string(m))
}
Json MarshalIndent
MarshalIndent é como Marshal, mas aplica o recuo para formatar a saída. Cada elemento JSON na saída começará em uma nova linha que começa com prefixo seguido por uma ou mais cópias de recuo de acordo com o aninhamento de recuo.
package main
import (
"encoding/json"
"fmt"
"log"
)
type ApiLogin struct {
Name string `json:"name"`
Cpf string `json:"cpf"`
}
func main() {
a := ApiLogin{"Jefferson", "033.343.434-89"}
// improving output for json format viewing
json, err := json.MarshalIndent(a, "", "\t")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(json))
}
Opção Omitempty
A opção "omitempty" especifica que o campo deve ser omitido da codificação se o campo tiver um valor vazio, definido como falso, 0, um ponteiro nulo, um valor de interface nulo e qualquer matriz, fatia, mapa ou cadeia vazia.
O pacote json acessa apenas os campos exportados de tipos de struct (aqueles que começam com uma letra maiúscula). Portanto, somente os campos exportados de uma estrutura estarão presentes na saída JSON.
Neste exemplo, trabalhamos com ponteiros para referenciar a estrutura dentro de outra estrutura, e outro ponto é que declaramos a estrutura dentro da própria estrutura. Com isso, temos maneiras diferentes de inicializar e preencher os campos de nossas estruturas. Vamos ver como isso funciona? Confira o exemplo abaixo.
package main
import (
"encoding/json"
"fmt"
"log"
)
type Login struct {
// Field appears in JSON as key "login".
Login string `json:"login"`
// Field appears in JSON as key "email" and
// the field is omitted from the object if its value is empty,
// as defined above.
Email string `json:"email,omitempty"`
// Field appears in JSON as key "nick" (the default), but
// the field is skipped if empty.
// Note the leading comma.
Nick string `json:",omitempty"`
// Field is ignored by this package.
Level int `json:"-"`
// Field appears in JSON as key "-".
LastEmail string `json:"-,"`
}
func main() {
l := Login{Login: "Austin", Email: "austin@go.com", Nick:
"", Level: 1000, LastEmail: "austin@gmail.com"}
fmt.Println(l)
m, err := json.Marshal(l)
if err != nil {
log.Println(err)
}
fmt.Println(string(m))
}
Json NewDecoder
json.NewEncoder(r.Body).Encode(&d)
Interface vazia
A interface{} (interface vazia) descreve uma interface com métodos zero. Cada tipo Go implementa pelo menos zero métodos e portanto, satisfaz a interface vazia.
Em interface vazia seu tipo especifica nenhum método.
Uma interface vazia pode conter valores de qualquer tipo.
- Interfaces vazias são usadas para declarar tipos que não serão conhecidos, semelhante ao que conhecemos em linguagens dinâmicas.
Confira o exemplo abaixo para utilização de interface vazia:
package main
import (
"fmt"
"math"
)
func main() {
var i interface{}
i = "Go Manual 2.0"
i = 2019
i = 9.5
r := i.(float64)
fmt.Println("Area do circulo: ", math.Pi*r*r)
switch v := i.(type) {
case int:
fmt.Println("Int * 2=", v*2)
case float64:
fmt.Println("Float64/2=", v/2)
case string:
h := len(v) / 2
fmt.Println("Quantidade/2 -> v[h:] + v[:h]=", v[h:]+v[:h])
default:
// i isn't one of the types above
}
}
Interface Method
A interface quando utilizada como methods Um tipo de interface é definida como um conjunto de assinaturas de métodos. {} (interface vazia) descreve uma interface com métodos zero. Cada tipo Go implementa pelo menos zero métodos e portanto, satisfaz a interface vazia.
Em interface vazia seu tipo especifica nenhum método.
Uma interface vazia pode conter valores de qualquer tipo.
- Interfaces vazias são usadas para declarar tipos que não serão conhecidos, semelhante ao que conhecemos em linguagens dinâmicas.
Confira o exemplo abaixo para utilização de interface vazia:
package main
import (
"fmt"
"math"
)
func main() {
var i interface{}
i = "Go Manual 2.0"
i = 2019
i = 9.5
r := i.(float64)
fmt.Println("Area do circulo: ", math.Pi*r*r)
switch v := i.(type) {
case int:
fmt.Println("Int * 2=", v*2)
case float64:
fmt.Println("Float64/2=", v/2)
case string:
h := len(v) / 2
fmt.Println("Quantidade/2 -> v[h:] + v[:h]=", v[h:]+v[:h])
default:
// i isn't one of the types above
}
}
Construindo APIs com net/http
Introdução http
Agora chegamos à melhor parte, colocamos em prática tudo o que aprendemos. Vamos conhecer o pacote net/http como um dos pacotes mais poderosos do Go, existem muitas especulações sobre ele, mas nós realmente faremos o nosso melhor naquilo que ele oferece com os recursos que ele oferece. Existem muitas implementações na net/http, várias rotas, frameworks, libs todas para minimizar o trabalho e agilizar várias tarefas ao codificar nossas apis. Nosso objetivo é criar APIs nativas.
Tudo em Go segue esse modelo, tem lib por muito, e quanto mais você dominar a linguagem, mais você terá o hábito de escolher melhor as bibliotecas ou desenvolver suas próprias bibliotecas. Vamos começar desenvolvendo nosso servidor de API, para que possamos consumi-lo mais tarde. O APIS como um servidor pode ser feito de várias maneiras, seja construindo o APIS no rEST, GraphQL, SOAP, XML-RPC e várias outras formas de comunicação, como RPC, Socket ou Websocket.
Temos uma biblioteca poderosa e vasta, tudo que temos em C ou C++ está em Go melhorado. Todo pacote net/http está trabalhando no Goroutine, este é um dos pilares do net/http.
Type Handler
Um manipulador responde a uma solicitação HTTP.
ServeHTTP deve escrever cabeçalhos de resposta e dados para o ResponseWriter e, em seguida, retornar. Retornando sinais de que o pedido está finalizado; não é válido usar o ResponseWriter ou ler o Request.Body após ou simultaneamente com a conclusão da chamada ServeHTTP.
Depois de implementado, o http.Handler pode ser passado para http.ListenAndServe, que chamará o método ServeHTTP em todas as solicitações recebidas. O http.Request contém todas as informações relevantes sobre uma solicitação http de entrada que está sendo atendida pelo seu http.Handler.
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
Confira o código abaixo:
package main
import (
"fmt"
"log"
"net/http"
)
type pingHandler struct{}
func (h pingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Go Manual 2.0 for Golang simple %s\n", r.URL.Path)
}
func main() {
log.Printf("\nServer run 8080\n")
err := http.ListenAndServe(":8080", pingHandler{})
log.Fatal(err)
}
Execute o curl:
$ curl -i -Xget localhost:8080/v1/api/ping
$ curl -i -Xget localhost:8080
O http.ResponseWriter é a interface através da qual você pode responder ao pedido. Ele implementa a interface io.Writer, então você pode usar métodos como fmt.Fprintf para escrever uma string formatada como o corpo da resposta, ou como io.Copy para escrever o conteúdo de um arquivo (ou qualquer outro io.Reader). O código de resposta pode ser definido antes de você começar a gravar dados usando o método WriteHeader.
O pacote http da Go transformou-se em uma das minhas coisas favoritas sobre a linguagem de programação Go. Inicialmente parece ser algo complexo, mas na realidade pode ser dividido em alguns componentes simples que são extremamente flexíveis em como eles podem ser usados.
Type Handlerfunc
O tipo HandlerFunc é um adaptador para permitir o uso de funções comuns como manipuladores HTTP. Se f é uma função com a assinatura apropriada, HandlerFunc (f) é um manipulador que chama f.
Muitas vezes, definir um tipo completo para implementar a interface http.Handler é um pouco exagerado, especialmente para funções ServeHTTP extremamente simples como a acima. O pacote http fornece uma função auxiliar, http.HandlerFunc, que envolve uma função que possui a assinatura func (w http.ResponseWriter, r http.Request), retornando um http.Handler que o chamará em todos os casos.
O seguinte se comporta exatamente como no exemplo anterior, mas usa http.HandlerFunc em vez de definir um novo tipo.
type HandlerFunc func(ResponseWriter, *Request)
Confira:
handlerApiPing := http.HandlerFunc(Ping)
Veja o código abaixo:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
handlerfunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Go Manual 2.0 for Golang simple two %s\n", r.URL.Path)
})
log.Printf("\nServer run 8080\n")
err := http.ListenAndServe(":8080", handlerfunc)
log.Fatal(err)
}
Func http Handlefunc
HandleFunc registra a função do manipulador para o padrão fornecido no DefaultServeMux. A documentação do ServeMux explica como os padrões são correspondidos.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
Confira os exemplos abaixo:
http.HandleFunc("/v1/api/ping", pingHandler)
http.HandleFunc("/v1/api/ping", func(w http.ResponseWriter, req *http.Request){})
package main
import (
"log"
"net/http"
)
func main() {
// our function
pingHandler := func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("\nGo Manual 2.0 BH for Golang HandleFunc!"))
}
// handleFunc
http.HandleFunc("/v1/api/ping", pingHandler)
http.HandleFunc("/v1/api/ping2", pingHandler)
http.HandleFunc("/v1/api/ping3", pingHandler)
// show run server
log.Printf("\nServer run 8080\n")
// Listen
log.Fatal(http.ListenAndServe(":8080", nil))
}
Func http Handle
Handle registra o manipulador para o padrão fornecido no DefaultServeMux.
func Handle(pattern string, handler Handler)
Confira o exemplo abaixo:
http.Handle("/v1/api/ping", http.HandlerFunc(Ping))
package main
import (
"log"
"net/http"
)
func main() {
// our function
pingHandler := func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("\nGo Manual 2.0 BH for Golang Handle tree!"))
}
// Handle and recive http.HandlerFunc
http.Handle("/v1/api/ping", http.HandlerFunc(pingHandler))
http.Handle("/v1/api/ping2", http.HandlerFunc(pingHandler))
http.Handle("/v1/api/ping3", http.HandlerFunc(pingHandler))
// http.Handle("/v1/api/ping", pingHandler) // error
// show run
log.Printf("\nServer run 8080\n")
// Listen
// log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(pingHandler))) ok
log.Fatal(http.ListenAndServe(":8080", nil))
}
Func http Error
Erro ao responder à solicitação com a mensagem de erro especificada e o código HTTP. Não termina o pedido; o chamador deve garantir que nenhuma outra gravação seja feita para w. A mensagem de erro deve ser texto simples.
func Error(w ResponseWriter, error string, code int)
Confira o exemplo abaixo:
json := `{"status":"error", "msg":"method not supported, only POST"}`
http.Error(w, json, http.StatusUnauthorized)
Constants Common HTTP Methods
Salvo indicação em contrário, estes são definidos no RFC 7231 seção 4.3.
const (
MethodGet = "GET"
MethodHead = "HEAD"
MethodPost = "POST"
MethodPut = "PUT"
MethodPatch = "PATCH" // RFC 5789
MethodDelete = "DELETE"
MethodConnect = "CONNECT"
MethodOptions = "OPTIONS"
MethodTrace = "TRACE"
)
Type ServeMux
Salvo indicação em contrário, estes são definidos no RFC 7231 seção 4.3.
const (
MethodGet = "GET"
MethodHead = "HEAD"
MethodPost = "POST"
MethodPut = "PUT"
MethodPatch = "PATCH" // RFC 5789
MethodDelete = "DELETE"
MethodConnect = "CONNECT"
MethodOptions = "OPTIONS"
MethodTrace = "TRACE"
)
Códigos de status HTTP, conforme registrados na IANA. Veja: https://httpstatuses.com/
const (
StatusContinue = 100 // RFC 7231, 6.2.1
StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
StatusProcessing = 102 // RFC 2518, 10.1
StatusOK = 200 // RFC 7231, 6.3.1
StatusCreated = 201 // RFC 7231, 6.3.2
StatusAccepted = 202 // RFC 7231, 6.3.3
StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
StatusNoContent = 204 // RFC 7231, 6.3.5
StatusResetContent = 205 // RFC 7231, 6.3.6
StatusPartialContent = 206 // RFC 7233, 4.1
StatusMultiStatus = 207 // RFC 4918, 11.1
StatusAlreadyReported = 208 // RFC 5842, 7.1
StatusIMUsed = 226 // RFC 3229, 10.4.1
StatusMultipleChoices = 300 // RFC 7231, 6.4.1
StatusMovedPermanently = 301 // RFC 7231, 6.4.2
StatusFound = 302 // RFC 7231, 6.4.3
StatusSeeOther = 303 // RFC 7231, 6.4.4
StatusNotModified = 304 // RFC 7232, 4.1
StatusUseProxy = 305 // RFC 7231, 6.4.5
StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
StatusPermanentRedirect = 308 // RFC 7538, 3
StatusBadRequest = 400 // RFC 7231, 6.5.1
StatusUnauthorized = 401 // RFC 7235, 3.1
StatusPaymentRequired = 402 // RFC 7231, 6.5.2
StatusForbidden = 403 // RFC 7231, 6.5.3
StatusNotFound = 404 // RFC 7231, 6.5.4
StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5
StatusNotAcceptable = 406 // RFC 7231, 6.5.6
StatusProxyAuthRequired = 407 // RFC 7235, 3.2
StatusRequestTimeout = 408 // RFC 7231, 6.5.7
StatusConflict = 409 // RFC 7231, 6.5.8
StatusGone = 410 // RFC 7231, 6.5.9
StatusLengthRequired = 411 // RFC 7231, 6.5.10
StatusPreconditionFailed = 412 // RFC 7232, 4.2
StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11
StatusRequestURITooLong = 414 // RFC 7231, 6.5.12
StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13
StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
StatusExpectationFailed = 417 // RFC 7231, 6.5.14
StatusTeapot = 418 // RFC 7168, 2.3.3
StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2
StatusUnprocessableEntity = 422 // RFC 4918, 11.2
StatusLocked = 423 // RFC 4918, 11.3
StatusFailedDependency = 424 // RFC 4918, 11.4
StatusUpgradeRequired = 426 // RFC 7231, 6.5.15
StatusPreconditionRequired = 428 // RFC 6585, 3
StatusTooManyRequests = 429 // RFC 6585, 4
StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5
StatusUnavailableForLegalReasons = 451 // RFC 7725, 3
StatusInternalServerError = 500 // RFC 7231, 6.6.1
StatusNotImplemented = 501 // RFC 7231, 6.6.2
StatusBadGateway = 502 // RFC 7231, 6.6.3
StatusServiceUnavailable = 503 // RFC 7231, 6.6.4
StatusGatewayTimeout = 504 // RFC 7231, 6.6.5
StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6
StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1
StatusInsufficientStorage = 507 // RFC 4918, 11.5
StatusLoopDetected = 508 // RFC 5842, 7.2
StatusNotExtended = 510 // RFC 2774, 7
StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)
DefaultMaxHeaderBytes é o tamanho máximo permitido dos cabeçalhos em uma solicitação HTTP. Isso pode ser substituído, definindo Server.MaxHeaderBytes.
const DefaultMaxHeaderBytes = 1 << 20 // 1 MB
DefaultMaxIdleConnsPerHost é o valor padrão de MaxIdleConnsPerHost de transporte.
const DefaultMaxIdleConnsPerHost = 2
TimeFormat é o formato de hora a ser usado ao gerar tempos em cabeçalhos HTTP. É como o tempo.RFC1123 mas codifica GMT como o fuso horário. O momento que está sendo formatado deve estar em UTC para Format para gerar o formato correto.
Para analisar esse formato de hora, consulte ParseTime.
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
Type ServeMux
O ServeMux é um multiplexador de solicitação HTTP. Ele corresponde a URL de cada solicitação recebida a uma lista de padrões registrados e chama o manipulador para o padrão que mais se aproxima do URL.
Os padrões nomeiam caminhos fixos e com raiz, como "/favicon.ico" ou subárvores com raiz, como "/images/" (observe a barra à direita). Padrões mais longos têm precedência sobre os mais curtos, de modo que, se houver manipuladores registrados para "/images/" e "/images/thumbnails/", o último manipulador será chamado para caminhos que iniciam "/images/thumbnails/" e o antigo receberá solicitações para qualquer outro caminho na subárvore "/images/".
Observe que, como um padrão que termina em uma barra nomeia uma subárvore com raiz, o padrão "/" corresponde a todos os caminhos não correspondidos por outros padrões registrados, não apenas à URL com o caminho == "/".
Se uma subárvore foi registrada e uma solicitação é recebida nomeando a raiz da subárvore sem sua barra final, o ServeMux redireciona essa solicitação para a raiz da subárvore (adicionando a barra à direita). Esse comportamento pode ser substituído por um registro separado para o caminho sem a barra final. Por exemplo, registrar "/images/" faz com que ServeMux redirecione uma solicitação para "/images" para "/images/", a menos que "/ images" tenha sido registrado separadamente.
Padrões podem, opcionalmente, começar com um nome de host, restringindo as correspondências aos URLs nesse host apenas. Os padrões específicos do host têm precedência sobre os padrões gerais, de modo que um manipulador pode se inscrever nos dois padrões "/ codesearch" e "codesearch.google.com/" sem assumir as solicitações de "http://www.google.com/.
O ServeMux também cuida da limpeza do caminho de solicitação de URL e do cabeçalho do Host, retirando o número da porta e redirecionando qualquer solicitação que contenha. ou .. elementos ou barras repetidas para um URL equivalente e mais limpo.
type ServeMux struct {
// contains filtered or unexported fields
}
Type NewServeMux
O NewServeMux aloca e retorna um novo ServeMux.
func NewServeMux() *ServeMux
Confira:
mux := http.NewServeMux()
Func ServeMux HandleFunc
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
HandleFunc registra a função do manipulador para o padrão fornecido.
Confira o código abaixo:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
// our function
pingHandler := func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("\nGo Manual 2.0 for Golang mux HandleFunc!"))
}
// handleFunc
mux.HandleFunc("/v1/api/ping", pingHandler) // ok
mux.HandleFunc("/v1/api/ping2", http.HandlerFunc(pingHandler)) // ok
mux.HandleFunc("/v1/api/ping3", pingHandler) // ok
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, "You're lost, go home Go Manual 2.0!")
})
log.Printf("\nServer run 8080\n")
// Listen
log.Fatal(http.ListenAndServe(":8080", mux))
}
Executar cURL:
$ curl -i -Xget localhost:8080/
Type ServeMux Handle
Handle registra o manipulador para o padrão fornecido. Se um manipulador já existir para o padrão, lide com pânicos.
func (mux *ServeMux) Handle(pattern string, handler Handler)
Confira:
mux := http.NewServeMux()
mux.Handle("/v1/api/ping", http.HandlerFunc(Ping))
package main
import (
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
// our function
pingHandler := func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("\nGo Manual 2.0 for Golang mux Handle()!"))
}
// handlerFunc
mux.Handle("/v1/api/ping", http.HandlerFunc(pingHandler)) // ok
// mux.Handle("/v1/api/ping2", pingHandler) // error
// mux.Handle("/v1/api/ping", mux.HandlerFunc(pingHandler)) // error
// mux.Handle("/", func(w http.ResponseWriter, r *http.Request) { // error
// w.WriteHeader(http.StatusNotFound)
// fmt.Fprintln(w, "You're lost, go home Go Manual 2.0!")
// })
log.Printf("\nServer run 8080\n")
// Listen
log.Fatal(http.ListenAndServe(":8080", mux))
}
Executar cURL:
$ curl -i -Xget localhost:8080/
Func ListenAndServe
HandleFunc registra a função do manipulador para o padrão fornecido no DefaultServeMux. A documentação do ServeMux explica como os padrões são correspondidos.
O ListenAndServe escuta o endereço de rede TCP addr e, em seguida, chama o Servir com o manipulador para manipular solicitações em conexões de entrada. As conexões aceitas são configuradas para ativar keep-alives de TCP.
O manipulador é normalmente nulo, caso em que o DefaultServeMux é usado.
ListenAndServe sempre retorna um erro não nulo.
Confira nosso primeiro exemplo:
package main
import (
"io"
"log"
"net/http"
)
func main() {
// our function
pingHandler := func(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "Go Manual 2.0, Golang for Go Manual 2.0!\n")
}
// handlerFunc
http.HandleFunc("/v1/api/ping", pingHandler)
// Listen
// log.Fatal(http.ListenAndServe(":8080", http.HandlerFunc(pingHandler))) ok
log.Fatal(http.ListenAndServe(":8080", nil)) // ok
}
Neste cenário apis, o programa está escutando na porta determinada pela função ListenAndServe, aguardando que as solicitações sejam recebidas para que possam responder às solicitações recebidas.
$ curl -i -XPOST localhost:8080/v1/api/ping
Saída:
HTTP/1.1 200 OK
Date: Fri, 01 Feb 2019 17:01:23 GMT
Content-Length: 29
Content-Type: text/plain; charset=utf-8
Go Manual 2.0, Golang for Go Manual 2.0!
Func ListenAndServeTLS
ListenAndServeTLS atua de forma idêntica ao ListenAndServe, exceto que ele espera conexões HTTPS. Além disso, arquivos contendo um certificado e uma chave privada correspondente para o servidor devem ser fornecidos. Se o certificado for assinado por uma autoridade de certificação, o certFile deverá ser a concatenação do certificado do servidor, de quaisquer intermediários e do certificado da autoridade de certificação.
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error
Antes de ter que gerar as chaves, .pem ou .crt e o arquivo .key. Vamos gerar todos os openssl em execução.
Verifique os códigos abaixo:
# generating .key and .csr
$ openssl req -nodes -newkey rsa:2048 -keyout server.key -out server.csr -subj "/C=BR/ST=Minas/L=Belo Horizonte/O=s3wf Ltd./OU=IT/CN=localhost"
# generating server .crt or .pem
$ openssl x509 -req -sha256 -in server.csr -signkey server.key -out server.crt -days 365
Em breve, geramos server.crt, server.csr, server.key.
Agora vamos para a nossa api abaixo:
package main
import (
"io"
"log"
"net/http"
)
var (
addr = ":443"
)
func main() {
http.HandleFunc("/v1/api/ping", func(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "Go Manual 2.0, Golang for Go Manual 2.0 TLS!\n")
})
// show
log.Printf("Server Run %s TLS / https://localhost%s", addr, addr)
// conf listen TLS
err := http.ListenAndServeTLS(addr, "server.crt", "server.key", nil)
log.Fatal(err)
}
Abaixo do mesmo código, no entanto, modificando o escute TLS usando http.HandlerFunc ()
Confira o código abaixo:
package main
import (
"io"
"log"
"net/http"
)
var (
addr = ":443"
)
func main() {
pingHandler := func(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "Go Manual 2.0, Golang for Go Manual 2.0 TLS!\n")
}
// show
log.Printf("Server Run %s TLS / https://localhost%s", addr, addr)
// conf listen TLS
err := http.ListenAndServeTLS(addr, "server.crt", "server.key", http.HandlerFunc(pingHandler))
log.Fatal(err)
}
// curl --insecure -i -XGET https://localhost:8443/v1/api/ping
// curl -k -i -XGET https://localhost:8443/v1/api/ping
$ curl --insecure -i -XGET https://localhost:443/v1/api/ping
or
$ curl -k -i -XGET https://localhost:443/v1/api/ping
Agora vamos usar algumas propriedades do pacote tls e vamos fazer uma configuração, já que nós já aprendemos o mux, vamos usá-lo também. No começo parece confuso, mas na verdade é simples, vamos dar uma olhada.
Veja o código abaixo:
package main
import (
"crypto/tls"
"io"
"log"
"net/http"
)
var (
addr = ":443"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/v1/api/ping",
func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
io.WriteString(w, "Go Manual 2.0, Golang for Go Manual 2.0 TLS MUX!\n")
})
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}
srv := &http.Server{
Addr: addr,
Handler: mux,
TLSConfig: cfg,
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
}
// show
log.Printf("Server Run %s TLS / https://localhost%s", addr, addr)
// conf listen TLS
err := srv.ListenAndServeTLS("server.crt", "server.key")
log.Fatal(err)
}
// curl --insecure -i -XGET https://localhost:443/v1/api/ping
// curl -k -i -XGET https://localhost:443/v1/api/ping
Executar cURL:
$ curl --insecure -i -XGET https://localhost:443/v1/api/ping
or
$ curl -k -i -XGET https://localhost:443/v1/api/ping
Agora vamos colocar algumas funções que farão a diferença quando rodarmos nossa API para alta performance, vamos tentar não usar a biblioteca fmt para gravar no monitor, vamos usar io e buff. Bem desempenho é algo absurdamente mais rápido.
De um verificado no código completo abaixo:
package main
import (
"bufio"
"io"
"log"
"net/http"
"os"
)
var (
addr = ":8080"
)
// write bufio to optimization
func write(text string) {
// var writer *bufio.Writer
writer := bufio.NewWriter(os.Stdout)
writer.WriteString(text)
writer.Flush()
}
func main() {
// our function
pingHandler := func(w http.ResponseWriter, req *http.Request) {
json := `{"status":"success", "msg":"Go Manual 2.0, Golang for Go Manual 2.0!"}`
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusUnauthorized)
io.WriteString(w, json)
}
// handlerFunc
http.HandleFunc("/v1/api/ping", pingHandler)
// show
write("\033[0;33mServer Run Port " + addr + "\033[0m\n")
// Listen
log.Fatal(http.ListenAndServe(addr, nil))
}
// Go in action
// @jeffotoni
// 2019-01-01
package main
import (
"bufio"
"io"
"log"
"net/http"
"os"
"strings"
"time"
)
var (
addr = ":8080"
)
// show log on screen
func logf(method, uri, nameHandle string, timeHandler time.Duration) {
expre := "\033[5m%s \033[0;103m%s\033[0m \033[0;93m%s\033[0m \033[1;44m%s\033[0m"
log.Printf(expre, method, uri, nameHandle, timeHandler)
}
// write bufio to optimization
func write(text string) {
// var writer *bufio.Writer
writer := bufio.NewWriter(os.Stdout)
writer.WriteString(text)
writer.Flush()
}
func Ping(w http.ResponseWriter, r *http.Request) {
// start time
start := time.Now()
if http.MethodPost == strings.ToUpper(r.Method) {
json := `{"status":"success", "msg":"Go Manual 2.0, Golang for Go Manual 2.0!"}`
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
io.WriteString(w, json)
} else {
json := `{"status":"error", "msg":"method not supported, only POST"}`
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusUnauthorized)
io.WriteString(w, json)
}
logf(r.Method,
r.RequestURI,
"Ping",
time.Since(start))
}
func main() {
// handlerFunc
http.HandleFunc("/v1/api/ping", Ping)
// show
write("\033[0;33mServer Run " +
"Port " +
addr + "\033[0m\n")
// Listen
log.Fatal(http.ListenAndServe(addr, nil))
}
$ curl -i -XPOSt localhost:8080/v1/api/ping
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Fri, 01 Feb 2019 22:04:57 GMT
Content-Length: 58
{"status":"success", "msg":"Go Manual 2.0, Golang for Go Manual 2.0!"}
$ curl -i -XGET localhost:8080/v1/api/ping
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Date: Fri, 01 Feb 2019 22:05:46 GMT
Content-Length: 59
{"status":"error", "msg":"method not supported, only POST"}
Other Muxes
Existem inúmeras substituições para o http.ServeMux, como o gorilla/mux, que fornecem coisas como extrair automaticamente variáveis de caminhos, declarar facilmente quais métodos de http são permitidos em um nó de extremidade e muito mais. A maioria desses substitutos implementará o http.Handler como o http.ServeMux, e aceita o http.Handlers como argumentos, sendo fáceis de usar em conjunto com o restante das coisas sobre as quais falarei neste post.
Vamos escrever nosso próprio http.HandlerFunc, vamos criar algo simples só para podermos entender o que acontece com as nossas apis.
Confira o código abaixo:
package main
import (
"fmt"
"log"
"net/http"
)
type numberDumperString string
type numberDumperInt int
// http HandlerFunc
func (n numberDumperString) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Go Manual 2.0, Golang is Life, Here's your number: %s\n", n)
}
// http HandlerFunc
func (n numberDumperInt) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Go Manual 2.0, Golang is Life, Here's your number: %d\n", n)
}
func main() {
mux := http.NewServeMux()
mux.Handle("/one", numberDumperString("one"))
mux.Handle("/two", numberDumperString("two"))
mux.Handle("/three", numberDumperInt(3))
mux.Handle("/four", numberDumperInt(4))
mux.Handle("/five", numberDumperInt(5))
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
fmt.Fprintln(w, "That's not a supported number new Ghoper!")
})
// show run
log.Printf("\nServer run 8080\n")
// listen
err := http.ListenAndServe(":8080", mux)
log.Fatal(err)
}
Executar cURL:
$ curl -i -Xget localhost:8080/one
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 01:25:39 GMT
Content-Length: 51
Content-Type: text/plain; charset=utf-8
Go Manual 2.0, Golang is Life, Here's your number: one
$ curl -i -Xget localhost:8080/two
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 01:25:39 GMT
Content-Length: 51
Content-Type: text/plain; charset=utf-8
Go Manual 2.0, Golang is Life, Here's your number: two
$ curl -i -Xget localhost:8080/three
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 01:25:39 GMT
Content-Length: 51
Content-Type: text/plain; charset=utf-8
Go Manual 2.0, Golang is Life, Here's your number: three
$ curl -i -Xget localhost:8080/four
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 01:25:39 GMT
Content-Length: 51
Content-Type: text/plain; charset=utf-8
Go Manual 2.0, Golang is Life, Here's your number: four
$ curl -i -Xget localhost:8080/eleven
HTTP/1.1 404 Not Found
Date: Sat, 02 Feb 2019 01:26:57 GMT
Content-Length: 42
Content-Type: text/plain; charset=utf-8
That's not a supported number new Ghoper!
Testing Http endpoints
O teste de endpoints http é extremamente fácil no Go e não exige que você realmente ouça em nenhuma porta! O pacote httptest fornece alguns utilitários úteis, incluindo o NewRecorder, que implementa o http.ResponseWriter e permite que você efetue efetivamente uma solicitação http, chamando o ServeHTTP diretamente. Veja um exemplo de um teste para o nosso numberDumperInt e numberDumperString implementados anteriormente, comentado exatamente com o que está acontecendo:
Vamos testar a API acima, para ver o comportamento e como é fácil usar testes usando endpoints...
Confira o código abaixo:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
. "testing"
)
func TestNumberDumperInt(t *T) {
// We first create the http.Handler we wish to test
n := numberDumperInt(3)
// We create an http.Request object to test with. The http.Request is
// totally customizable in every way that a real-life http request is, so
// even the most intricate behavior can be tested
r, _ := http.NewRequest("GET", "/one", nil)
// httptest.Recorder implements the http.ResponseWriter interface, and as
// such can be passed into ServeHTTP to receive the response. It will act as
// if all data being given to it is being sent to a real client, when in
// reality it's being buffered for later observation
w := httptest.NewRecorder()
// Pass in our httptest.Recorder and http.Request to our numberDumper. At
// this point the numberDumper will act just as if it was responding to a
// real request
n.ServeHTTP(w, r)
// httptest.Recorder gives a number of fields and methods which can be used
// to observe the response made to our request. Here we check the response
// code
if w.Code != 200 {
t.Fatalf("wrong code returned: %d", w.Code)
}
// We can also get the full body out of the httptest.Recorder, and check
// that its contents are what we expect
body := w.Body.String()
if body != fmt.Sprintf("Go Manual 2.0, Golang is Life, Here's your number: 3\n") {
t.Fatalf("wrong body returned: %s", body)
}
}
Dessa forma, é fácil criar testes para seus componentes individuais que você usa para criar seu aplicativo, mantendo os testes próximos da funcionalidade que estão testando. Nota: se você precisar executar um servidor de teste em seus testes, o httptest também fornecerá uma maneira de criar um servidor escutando em uma porta aberta aleatória para uso em testes também.
Executar go:
$ go test
Saída:
PASS
ok net-http/tests-endpoints 0.002s
Http Shutdown Gracefully
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"sync"
"time"
)
// HTMLServer represents the web service that serves up HTML
type GoServerHttp struct {
server *http.Server
wg sync.WaitGroup
}
func indexHandler(w http.ResponseWriter, req *http.Request) {
w.Write([]byte(`
Golang/Go Manual 2.0
body {
background-color: #424242;
color: #F6F6F6;
text-align: center;
font-family: Helvetica, Arial, sans-serif;
font-size: 20px;
}
h1, h2, h3 {
margin: 0;
line-height: 1.5;
}
.print-container {
background-color: rgba(0, 0, 0, .3);
padding: 15px;
margin: 30px auto;
width: 50%;
border-radius: 4px;
}
{{ .Name }}
Manual Golang for Go Manual 2.0!
`))
}
func Ping(w http.ResponseWriter, req *http.Request) {
w.Write([]byte(`{"status":"success","msg":"Go Manual 2.0 for Golang StartServer!"}`))
}
func main() {
// DefaultServeMux
mux := http.NewServeMux()
// POST handler /api/v1/ping
handlerApiPing := http.HandlerFunc(Ping)
// handler ping
mux.Handle("/v1/api/ping", handlerApiPing)
// templates/index html
// if you want to activate this handler, the directory templates
// where the html file is located must
// be sent next to the binary to work, as it needs to parse the html
// mux.HandleFunc("/", tpl.ShowHtml)
// this handler implements the version
// that does not need the html file
mux.Handle("/", http.HandlerFunc(indexHandler))
// Create the HTML Server
ApiServer := GoServerHttp{
server: &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 20 * time.Second,
MaxHeaderBytes: 1 << 25, //32Mb
},
}
go func() {
log.Printf("\nServer run :8080\n")
// service connections
if err := ApiServer.server.ListenAndServe(); err != nil {
log.Printf("listen: %s\n", err)
}
}()
var errs = make(chan error, 2)
go func() {
// Setting up signal capturing
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
errs <- fmt.Errorf("Notify here: %s", <-c)
}()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
// Waiting for SIGINT (pkill -2)
//<-errs
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds.
//quit := make(chan os.Signal)
//signal.Notify(quit, os.Interrupt)
//<-quit
<-stop
log.Println("Shutdown Server ...")
// ... here is the code to close all
// ...
// ....
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := ApiServer.server.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown now:", err)
// ... here is the code to close all error context
// ...
// ....
}
// execute finish
log.Println("Server exist")
<-errs
}
Vá para o navegador e digite:
http://localhost:8080
Fazendo uma solicitação na sua API:
$ curl -i -Xget localhost:8080/v1/api/ping
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 02:32:32 GMT
Content-Length: 62
Content-Type: text/plain; charset=utf-8
{"status":"success","msg":"Go Manual 2.0 for Golang StartServer!"}%
Depois de parar o programa, com CTRL + C, veja o que vai acontecer.
2019/02/02 00:55:30
Server run :8080
^C2019/02/02 00:55:31 Shutdown Server ...
2019/02/02 00:55:31 Server exist
2019/02/02 00:55:31 listen: http: Server closed
Agora vamos tentar usar o comando kill.
$ ps aux | grep "name-api"
$ kill -SIGINT
Olhe a saída:
2019/02/02 00:52:24
Server run :8080
2019/02/02 00:55:10 Shutdown Server ...
2019/02/02 00:55:10 listen: http: Server closed
2019/02/02 00:55:10 Server exist
Middleware
A exibição de pontos de extremidade é boa, mas muitas vezes há funcionalidades que você precisa executar para cada solicitação antes que o manipulador do ponto de extremidade real seja executado. Por exemplo, log de acesso. Um componente de middleware é aquele que implementa o http.Handler, mas, na verdade, passará o pedido para outro http.Handler depois de executar algum conjunto de ações. O http.ServeMux que analisamos anteriormente é na verdade um exemplo de middleware, já que ele passa a solicitação para outro http.Handler para o processamento real.
Existem várias maneiras de implementar um Middleware, mas o conceito por trás de tudo é o mesmo para todos, sempre teremos que retornar um retorno http.HandlerFunc, todas as libs fizeram dessa forma, existem implementações muito elegantes e diversas libs na internet para fazer isso.
Vamos implementar nossos Middlewares e ver como isso funciona na prática.
Venha comigo, agora que as coisas começam a esfriar.
Veja um exemplo do nosso exemplo anterior com alguns middlewares de registro:
Confira o código abaixo:
package main
import (
"io"
"log"
"net/http"
"time"
)
// color terminal
var Expre = "\033[5m%s \033[0;103m%s\033[0m \033[0;93m%s\033[0m \033[1;44m%s\033[0m"
func Ping(w http.ResponseWriter, r *http.Request) {
json := `{"status":"success","msg":"pong"}`
w.Write([]byte(json))
}
// This middleware is responsible for holding up when we have a
// very large number of accesses in a very small time interval,
// depending on the capacity of your traffic, cpu and memory.
// It is one of the favorite middlewares, it is very powerful,
// not only determines the number of clients, but it does
// not have to lose in the requests sent.
func MaxClients(n int) Adapter {
sema := make(chan struct{}, n)
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
sema <- struct{}{}
defer func() { <-sema }()
h.ServeHTTP(w, r)
})
}
}
// This middleware is only a simulation, to implement the
// jwt in Go is very quiet, I will
// demonstrate in other topics below.
func AuthJwt() Adapter {
//s1 := logg.Start()
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//if gjwt.CheckJwt(w, r) {
if r.Header.Get("X-KEY") == "123" {
h.ServeHTTP(w, r)
} else {
msgjson := `{"status":"error","message":"error in Jwt!"}`
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusUnauthorized)
io.WriteString(w, msgjson)
//logg.Show(r.URL.Path, strings.ToUpper(r.Method), "error", s1)
}
})
}
}
// Middleware Logger
func Logger(name string) Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
h.ServeHTTP(w, r)
log.Printf(
"%s %s %s %s",
r.Method,
r.RequestURI,
name,
time.Since(start),
)
})
}
}
// Middleware Logger
func LoggerColor(name string) Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
h.ServeHTTP(w, r)
log.Printf(
Expre,
r.Method,
r.RequestURI,
name,
time.Since(start),
)
})
}
}
type Adapter func(http.Handler) http.Handler
// Middleware
func Middleware(h http.Handler, adapters ...Adapter) http.Handler {
for _, adapter := range adapters {
h = adapter(h)
}
return h
}
func main() {
mux := http.NewServeMux()
handlerApiPing := http.HandlerFunc(Ping)
// generate token jwt
// handler token
mux.Handle("/v1/api/ping",
Middleware(handlerApiPing,
Logger("ping"),
))
mux.Handle("/v1/api/login",
Middleware(handlerApiPing,
AuthJwt(),
//Logger("login"),
LoggerColor("login"),
))
// show run server
log.Printf("\nServer run :8080\n")
// Listen
log.Fatal(http.ListenAndServe(":8080", mux))
}
$ go run api-server-middleware.go
$ curl -i -Xget localhost:8080/v1/api/login -H "X-KEY: 123"
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 03:37:12 GMT
Content-Length: 33
Content-Type: text/plain; charset=utf-8
{"status":"success","msg":"pong"}%
$ curl -i -Xget localhost:8080/v1/api/login -H "X-KEY: 123454"
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Date: Sat, 02 Feb 2019 03:43:39 GMT
Content-Length: 44
{"status":"error","message":"error in Jwt!"}%
$ curl -i -Xget localhost:8080/v1/api/ping
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 03:43:57 GMT
Content-Length: 33
Content-Type: text/plain; charset=utf-8
{"status":"success","msg":"pong"}%
http DetectContentType
DetectContentType implementa o algoritmo descrito em mimesniff para determinar o Content-Type dos dados fornecidos. Considera no máximo os primeiros 512 bytes de dados. DetectContentType sempre retorna um tipo MIME válido: se não puder determinar um mais específico, ele retorna "application / octet-stream".
func DetectContentType(data []byte) string
Vamos agora visualizar um código que simplesmente abrirá o arquivo para descobrir seu tipo de conteúdo.
Vemos que podemos usar a função http.DetectContentType para trabalhar juntos, mesmo sem ser uma API diretamente.
Confira o código abaixo:
import (
"fmt"
"net/http"
"os"
)
func main() {
// Open File
f, err := os.Open("./jeff-super.jpeg")
if err != nil {
panic(err)
}
defer f.Close()
// Get the content
contentType, err := GetFileContentType(f)
if err != nil {
panic(err)
}
fmt.Println("Content Type: " + contentType)
}
func GetFileContentType(out *os.File) (string, error) {
// Only the first 512 bytes are used to sniff the content type.
buffer := make([]byte, 512)
_, err := out.Read(buffer)
if err != nil {
return "", err
}
// Use the net/http package's handy DectectContentType function. Always returns a valid
// content-type by returning "application/octet-stream" if no others seemed to match.
contentType := http.DetectContentType(buffer)
return contentType, nil
}
Saída:
Content Type: image/jpeg
net/http Client
Introdução
O pacote http fornece implementações de cliente e servidor HTTP.
http.Transport
Para controle sobre proxies, configuração TLS, keep-alives, compactação e outras configurações, crie um Transport:
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")
http.Client
Para controle sobre cabeçalhos de cliente HTTP, diretiva de redirecionamento e outras configurações, crie um Cliente:
client := &http.Client{
CheckRedirect: redirectPolicyFunc,
}
resp, err := client.Get("http://example.com")
// ...
req, err := http.NewRequest("GET", "http://example.com", nil)
// ...
req.Header.Add("If-None-Match", `W/"wyzzy"`)
resp, err := client.Do(req)
// ...
Clientes e Transportes são seguros para uso simultâneo por múltiplas goroutines e para eficiência devem ser criados apenas uma vez e reutilizados.
http.Get
resp, err := http.Get("http://example.com/")
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...
http.Post
resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...
http.NewRequest
func NewRequest(method, url string, body io.Reader) (*Request, error)
NewRequest retorna um novo pedido, dado um método, URL e corpo opcional.
Se o corpo fornecido também for um io.Closer, o Request.Body retornado será definido como corpo e será fechado pelos métodos do Cliente, Post, PostForm e Transport.RoundTrip.
NewRequest retorna um pedido adequado para uso com Client.Do ou Transport.RoundTrip. Para criar uma solicitação para uso com o teste de um Manipulador do Servidor, use a função NewRequest no pacote net / http / httptest, use ReadRequest ou atualize manualmente os campos Solicitação. Consulte a documentação do tipo de solicitação para saber a diferença entre os campos de solicitação de entrada e saída.
Se body for do tipo *bytes.Buffer, *bytes.Reader ou *strings.Reader, o ContentLength da solicitação retornada será definido com seu valor exato (em vez de -1), GetBody será preenchido (portanto, redirecionamentos 307 e 308 poderão reproduzir body) e Body é definido como NoBody se o ContentLength for 0.
Context.WithCancel
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
WithCancel retorna uma cópia do pai com um novo canal Done. O canal Done do contexto retornado é fechado quando a função de cancelamento retornada é chamada ou quando o canal Done do contexto pai é fechado, o que ocorrer primeiro.
Cancelar este contexto libera recursos associados a ele, portanto, o código deve cancelar, assim que as operações executadas neste Contexto forem concluídas.
package main
import (
"context"
"fmt"
)
func main() {
// gen generates integers in a separate goroutine and
// sends them to the returned channel.
// The callers of gen need to cancel the context once
// they are done consuming generated integers not to leak
// the internal goroutine started by gen.
gen := func(ctx context.Context) <-chan int {
dst := make(chan int)
n := 1
go func() {
for {
select {
case <-ctx.Done():
return // returning not to leak the goroutine
case dst <- n:
n++
}
}
}()
return dst
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel when we are finished consuming integers
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
break
}
}
}
Este exemplo demonstra o uso de um contexto cancelável para evitar um vazamento de goroutine. No final da função de exemplo, a goroutine iniciada por gen retornará sem vazamento.
net/http Server Pages
Introdução
http.FileServer
func FileServer(root FileSystem) Handler
O FileServer retorna um manipulador que atende a solicitações HTTP com o conteúdo do sistema de arquivos com raiz na raiz.
Para usar a implementação do sistema de arquivos do sistema operacional, use http.Dir:
http.Handle("/", http.FileServer(http.Dir("/tmp")))
Como um caso especial, o servidor de arquivos retornado redireciona qualquer solicitação terminada em "/index.html" para o mesmo caminho, sem o "index.html" final.
package main
import (
"log"
"net/http"
)
func main() {
// Simple static webserver:
log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("/usr/share/doc"))))
}
package main
import "net/http"
func main() {
// http://localhost:8085/login.html
// diretorio fisico
fs := http.FileServer(http.Dir("web/"))
// mostra no browser localhost:8080/static
http.Handle("/", http.StripPrefix("/", fs))
http.ListenAndServe(":8085", nil)
}
// Go Api server
// @jeffotoni
package main
import (
"fmt"
"net/http"
"text/template"
)
type Login struct {
Title string
MsgError string
IfLabelone string
Labelone string
}
func HandlerLoginHtml(w http.ResponseWriter, r *http.Request) {
// template lendo HTML
tmpl := template.Must(template.ParseFiles("web/login.html"))
login := Login{
MsgError: "",
IfLabelone: "",
Title: "Manual2.0",
Labelone: "logar!",
}
tmpl.Execute(w, login)
}
func HandlerAdminHtml(w http.ResponseWriter, r *http.Request) {
}
func HandlerAuth(w http.ResponseWriter, r *http.Request) {
json := `{"status":"ok", "message":"tudo ocorreu bem na Auth..."}`
w.WriteHeader(http.StatusOK)
w.Write([]byte(json))
}
func main() {
mux := http.NewServeMux()
// retorna HTML
mux.HandleFunc("/login", HandlerLoginHtml)
// auth
mux.HandleFunc("/v1/api/auth", HandlerAuth)
// fisico
fs := http.FileServer(http.Dir("./web"))
// vitual
mux.Handle("/", http.StripPrefix("/", fs))
fmt.Println("Server Run: 8085")
http.ListenAndServe(":8085", mux)
}

http.NotFound
func NotFound(w ResponseWriter, r *Request)
NotFound responde à solicitação com um erro HTTP 404 não encontrado.
Disable http.FileServer
http.Dir
Um Dir implementa o FileSystem usando o sistema de arquivos nativo restrito a uma árvore de diretórios específica.
Enquanto o método FileSystem.Open toma '/' - caminhos separados, o valor da string de um Dir é um nome de arquivo no sistema de arquivos nativo, não um URL, então é separado por filepath.Separator, que não é necessariamente '/'.
Observe que o Dir permitirá o acesso a arquivos e diretórios que começam com um ponto, o que pode expor diretórios sensíveis, como um diretório .git ou arquivos confidenciais, como .htpasswd. Para excluir arquivos com um período inicial, remova os arquivos / diretórios do servidor ou crie uma implementação personalizada do FileSystem.
Um Dir vazio é tratado como ".".
type Dir string
func (Dir) Open
func (d Dir) Open(name string) (File, error)
http.StripPrefix
func StripPrefix(prefix string, h Handler) Handler
O StripPrefix retorna um manipulador que atende a solicitações HTTP removendo o prefixo fornecido do caminho do URL de solicitação e chamando o manipulador h. O StripPrefix manipula uma solicitação para um caminho que não começa com o prefixo respondendo com um erro HTTP 404 não encontrado.
package main
import (
"net/http"
)
func main() {
// To serve a directory on disk (/tmp) under an alternate URL
// path (/tmpfiles/), use StripPrefix to modify the request
// URL's path before the FileServer sees it:
http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}
net/http RPC
O pacote rpc fornece acesso aos métodos exportados de um objeto através de uma rede ou outra conexão de E/S. Um servidor registra um objeto, tornando-o visível como um serviço com o nome do tipo do objeto. Após o registro, os métodos exportados do objeto estarão acessíveis remotamente. Um servidor pode registrar vários objetos (serviços) de tipos diferentes, mas é um erro registrar vários objetos do mesmo tipo.
O servidor pode manipular solicitações em uma única conexão chamando ServeConn. Mais normalmente, ele cria um ouvinte de rede e chama Aceitar ou, para um ouvinte HTTP, HandleHTTP e http.Serve.
Um cliente que deseja usar o serviço estabelece uma conexão e, em seguida, chama o NewClient na conexão. A função de conveniência Dial (DialHTTP) executa as duas etapas para uma conexão de rede bruta (uma conexão HTTP). O objeto Client resultante possui dois métodos, Call and Go, que especificam o serviço e o método a ser chamado, um ponteiro contendo os argumentos e um ponteiro para receber os parâmetros de resultado.
O método Call espera que a chamada remota seja concluída enquanto o método Go inicia a chamada de forma assíncrona e sinaliza a conclusão usando o canal Done da estrutura de chamada.
A menos que um codec explícito seja configurado, o pacote encoding / gob é usado para transportar os dados.
Aqui está um exemplo simples. Um servidor deseja exportar um objeto do tipo Arith:
package server
import "errors"
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
rpcserver.go
// Go in Action
// @jeffotoni
package main
import (
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
var PORT_RPC = ":22334"
type Args struct {
Json string
}
type Receive struct{}
func (t *Receive) Json(args *Args, reply *string) error {
if len(args.Json) <= 0 {
*reply = `{"status":"error", "msg":"json field is required"}`
return nil
}
*reply = "ok"
fmt.Println("publisher: ", args.Json)
return nil
}
func main() {
re := new(Receive)
serverRpc := rpc.NewServer()
serverRpc.Register(re)
serverRpc.HandleHTTP(rpc.DefaultRPCPath, rpc.DefaultDebugPath)
listener, e := net.Listen("tcp", PORT_RPC)
if e != nil {
log.Println("listen error:", e)
return
}
// to listen
for {
if conn, err := listener.Accept(); err != nil {
log.Println("accept error: ", err.Error())
return
} else {
log.Printf("New connection established in rpc server\n")
serverRpc.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
}
rpcclient.go
// Go in Action
// @jeffotoni
package main
import (
"flag"
"fmt"
"log"
"net"
"net/rpc/jsonrpc"
"strconv"
"time"
)
type Args struct {
Json string
}
func main() {
host := flag.String("host", "127.0.0.1", "")
port := flag.String("port", "22334", "")
request := flag.String("req", "10000", "")
flag.Parse()
TCPHOST := *host + ":" + *port
client, err := net.Dial("tcp", TCPHOST)
if err != nil {
log.Fatal("dialing client:", err)
return
}
req, _ := strconv.Atoi(*request)
if req <= 0 {
log.Fatal("Requests must be greater than 0")
}
start := time.Now()
fmt.Println("\033[0;32mRun Tests...\033[0;0m")
fmt.Println("\033[0;33mRequests: ", req)
fmt.Println("Port: ", *port)
fmt.Printf("\033[0;0m")
var reply string
args := &Args{}
c := jsonrpc.NewClient(client)
for i := 0; i < req; i++ {
args = &Args{`{"versão": "1.1","host": "exemplo.org","key":"jeff_` + strconv.Itoa(i) + `",
"level":"info", "project":"my-project-here","short_message": "one msg here...",
"nível": 5, "some_info": "foo"}`}
err = c.Call("Receive.Json", args, &reply)
if err != nil {
log.Fatal("capture json error:", err)
}
// fmt.Printf("Result: %s\n", reply)
}
end := time.Now()
diff := end.Sub(start)
fmt.Println("Time: ", diff)
}
Configuração Docker Postgres
Postgres
docker pull postgres
docker volume create pgdata
docker run --name postgres -e \
POSTGRES_PASSWORD=12345 -v (pwd)/sql:/tmp -v pgdata:/var/lib/postgresql/data -d postgres
docker run -it -v /tmp:/tmp --rm postgres \
psql -d apis3produto -h 172.17.0.2 -U gopher -f /tmp/table.sql
docker run -it -v /tmp:/tmp --rm \
postgres pg_dump -Fc -Z9 -d apis3produto -h 172.17.0.2 -U gopher -f /tmp/table2.sql
docker run -it --rm postgres \
psql -h 172.17.0.2 -U postgres -c "create database apis3produto -O gopher -E UTF-8"
docker exec -it postgres bash
createuser gopher -U postgres
createdb apis3produto -U postgres -O gopher -E UTF-8 -T template0
psql -d template1 -U postgres
# alter user gopher password '12345'
# CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
drop table public.user;
docker run pgadmin
PgAdmin
docker pull dpage/pgadmin4
docker run -p 9000:80 \
-e "PGADMIN_DEFAULT_EMAIL=jeff.otoni@s3wf.com.br" \
-e "PGADMIN_DEFAULT_PASSWORD=1234" \
-d dpage/pgadmin4
http://localhost:9000/login?next=%2F
Configuração Docker Composer
Build API no Docker
docker build -t apis3produto -f Dockerfile .
docker build -t jeffotoni/apis3produto -f Dockerfile .
docker build --no-cache -t jeffotoni/apis3produto -f Dockerfile .
docker run -d -p 8081:8081 --name apis3produto jeffotoni/apis3produto
docker logs -f apis3produto
Docker Compose
docker-compose stop
docker-compose rm --force
docker-compose up -d
docker-compose ps
docker-compose logs -f apis3produto
Prometheus
docker stop prometheus
docker rm prometheus
docker run -d -p 9191:9090 -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
--restart=always --name prometheus prom/prometheus
Rabbit
docker pull rabbitmq
docker run -d --hostname api-rabbit --name api-rabbit rabbitmq:3
docker run -d --hostname my-rabbit \
--name rabbit13 -p 8282:15672 -p 5672:5672 -p 25676:25676 rabbitmq:3-management
Dockerfile
# tart by building the application.
# Build em apis3produto com distroless
FROM golang:1.17.2 as builder
WORKDIR /go/src/apis3produto
ENV GO111MODULE=on
COPY . .
#RUN go install -v ./...
RUN GOOS=linux go build -ldflags="-s -w" -o apis3produto main.go
RUN cp apis3produto /go/bin/apis3produto
#RUN ls -lh
# Now copy it into our base image.
FROM gcr.io/distroless/base
COPY --from=builder /go/bin/apis3produto /
CMD ["/apis3produto"]
Docker Compose da apis3produto
version: '3.5'
services:
apis3produto:
image: jeffotoni/apis3produto
container_name: apis3produto
hostname: apis3produto
domainname: apis3produto.local.com
environment:
- "TZ=America/Sao_Paulo"
- "API_ENV=prod"
networks:
apis3produtocompose:
aliases:
- apis3produto.local.com
ports:
- 8081:8081
- 6010:6010
env_file:
- ./apis3produto_env.env
restart: always
prometheus:
image: prom/prometheus:v2.7.1
container_name: prometheus
#hostname: prometheus
command:
- '--storage.tsdb.path=/var/lib/prometheus/'
- '--storage.tsdb.retention=180d'
- '--config.file=/etc/prometheus/prometheus.yml'
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- /data/volumes/prometheus:/var/lib/prometheus
networks:
- apis3produtocompose
ports:
- 9090:9090
environment:
- "TZ=America/Sao_Paulo"
postgres:
image: postgres
container_name: postgres
volumes:
- /pgdata:/var/lib/postgresql/data
- ./sql:/tmp
networks:
apis3produtocompose:
aliases:
- postgres.local.com
ports:
- 5432:5432
environment:
- "TZ=America/Sao_Paulo"
- "POSTGRES_PASSWORD=12345"
rabbit1:
image: "rabbitmq:3-management"
#hostname: "rabbit1"
container_name: "rabbit1"
environment:
RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG"
RABBITMQ_DEFAULT_USER: "rabbitmq"
RABBITMQ_DEFAULT_PASS: "rabbitmq"
RABBITMQ_DEFAULT_VHOST: "/"
networks:
apis3produtocompose:
aliases:
- rabbit1.local.com
ports:
- "8282:15672"
- "5672:5672"
- "25676:25676"
labels:
NAME: "rabbitmq1"
volumes:
- "./enabled_plugins:/etc/rabbitmq/enabled_plugins"
#- "./rabbitmq.config:/etc/rabbitmq/rabbitmq.config:ro"
#- "./autocluster-0.4.1.ez:/usr/lib/rabbitmq/lib/rabbitmq_server-3.5.5/plugins/autocluster-0.4.1.ez"
environment:
- "TZ=America/Sao_Paulo"
networks:
apis3produtocompose:
driver: bridge
apis3produto
Api Server Produto
O projeto é o desenvolvimento de diversas API's para comunicação com Marketplaces. Cada API estará em diretórios individuais e isolados, conforme a necessidade.
Endpoints
DOMAIN: localhost:8081
| Endpoint | Method |
|------------------------|:------:|
| /ping | POST |
| /auth | POST |
| /sendjson | POST |
| /produto | POST |
| /produto | PUT |
| /produto | GET |
| /produto/{id} | GET |
| /produto/{id} | DELETE |
Possíveis Responses
| Code | Mensagem |
|------|:------------------------:|
| 200 | |
| 201 | "Criado" |
| 204 | "Sem conteúdo" |
| 400 | "Requisição mal-formada" |
| 500 | "Erro interno " |
/ping
O endpoint Ping está aberto para testar se o servidor está respondendo.
Request
$ curl -i -XPOST \
localhost:8081/ping
/auth
O endpoint auth é para fazer o login e authenticação no server.
Request
curl -i -H "Content-Type:application/json" \
-H "Authorization: Basic 12345=:54321" \
-d '{"login":"jeff.otoni@s3wf.com.br","password":"123456"} \
-XPOST localhost:8081/auth
Test Stress /auth
hey -n 10000 -c 200 \
-m POST \
-T "application/json" \
-T "Authorization: Basic 12345=:54321" \
-d '{"login":"jeff.otoni@s3wf.com.br","password":"123456"}' \
http://localhost:8081/auth
Enpoint /sendjson
Este endpoit é para teste de envios de json e com autenticação do token...
curl -i \
-H "Authorization: Bearer $TOKEN_2" \
-XPOST localhost:8081/sendjson \
-d @json/produto.json
Test Stress /sendjson
hey -n 10000 -c 200 \
-m POST \
-T "application/json" \
-T "Authorization: Bearer $TOKEN_2" \
-D json/produto.json
http://localhost:8081/sendjson
/produto
O método Post terá dois efeitos: Inserir novo produto e atualizar produto que já existe.
O produto existe, ele atualiza automaticamente. O produto não existe, ele cria um novo produto, imagens se tiver, especificação e categorias se existir.
Na atualização as imagens estão sendo excluídas, e novas imagens são criadas a partir do *Json* enviado com o produto.
Post
Este endpoint insere um novo produto, e se existir produto ele faz um update.
Json Enviado
{
"product":{
"sku":"1000",
"name":"Pc Completo Gamer Com Monitor Lcd! 4gb, Wifi + 30 Jogos!",
"description":"descricao do produto Pc Completo Gamer Com Monitor Lcd! 4gb, Wifi + 30 Jogos!",
"status":"enabled",
"removed":false,
"qty":20,
"price":699,
"promotional_price":1600.99,
"cost":null,
"weight":49.12,
"height":17.49,
"width":72.49,
"length":111,
"brand":"ITATIAIA",
"ean":"7892946327425",
"nbm":null,
"categories":[
{
"code":"10103002",
"name":"Informatica"
}
],
"images":[
"https://http2.mlstatic.com/pc-completo-gamer-com-monitor-lcd-4gb-wifi-30-jogos-D_NQ_NP_942100-MLB31092984544_062019-F.webp",
"https://http2.mlstatic.com/pc-completo-gamer-com-monitor-lcd-4gb-wifi-30-jogos-D_NQ_NP_942100-MLB31092984544_062019-F2.webp",
"https://http2.mlstatic.com/pc-completo-gamer-com-monitor-lcd-4gb-wifi-30-jogos-D_NQ_NP_942100-MLB31092984544_062019-F3.webp",
"https://http2.mlstatic.com/pc-completo-gamer-com-monitor-lcd-4gb-wifi-30-jogos-D_NQ_NP_942100-MLB31092984544_062019-F4.webp",
"https://http2.mlstatic.com/pc-completo-gamer-com-monitor-lcd-4gb-wifi-30-jogos-D_NQ_NP_942100-MLB31092984544_062019-F5.webp"
],
"specifications":[
{
"key":"PROCESSADOR",
"value":"I7"
}
]
}
}
Request
$ curl -i -XPOST \
-H "Authorization: Bearer $TOKEN_2" \
-H "Content-Type:application/json" \
--data @json/produto.json
localhost:8081/produto
Get Todos Produtos Limite 50
Este endpoint lista todos com um limite de 50.
Request
curl -i -XGET \
-H "Content-Type:application/json" \
-H "Authorization: Bearer $TOKEN_2" \
localhost:8081/produto
Put
Request
$ curl -i -XPUT \
-H "Authorization: Bearer $TOKEN_2" \
-H "Content-Type:application/json" \
--data @jsonValid/produto.json
localhost:8081/produto
/produto/{id}
Get Por Produto
O método Get irá listar o Json do produto.
Request
curl -i -XGET \
-H "Content-Type:application/json" \
-H "Authorization: Bearer $TOKEN_2" \
localhost:8081/produto/{id}
Delete
O método Delete irá setar como excluído o produto, apesar dele existir na base, o sistema irá reconhecê-lo como não existente.
Request
curl -i -XDELETE \
-H "Content-Type:application/json" \
-H "Authorization: Bearer $TOKEN_2" \
localhost:8081/produto/{id}
Responses
Response igual a [200]
HTTP/1.1 200 OK
Response diferentes de [200]
{
"msg":"error"
}