Weitere ähnliche Inhalte Ähnlich wie JAX 2024: Go-über-den-Wolken und in der Cloud (20) JAX 2024: Go-über-den-Wolken und in der Cloud5. 5 Fakten zu Go
1. statisches Typsystem
2. Garbage Collection
3. keine Vererbung
4. Concurrency eingebaut
5. native Ausführung
Linux, Win, z/OS, 386, amd64, ARM, WebAssembly, ...
7. Cloud Native Technologien befähigen
skalierbare Anwendungen
zu entwickeln und betreiben,
in modernen, dynamischen Umgebungen.
Cloud Native Computing Foundation
13. Projekt DogOPaufsetzen
go mod init crossnative/dogop // Go Modul initialisieren
go get github.com/go-chi/chi/v5 // Chi Dependency einbinden
Projektstruktur
go.mod // Modul Deskriptor mit Dependencies
go.sum // Checksummen der Dependencies
14. Projekt DogOPaufsetzen
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
}
1
2
3
4
5
6
7
8
r := chi.NewRouter()
9
r.HandleFunc("GET /", func(w http.ResponseWriter, req *h
10
w.Write([]byte("Hello DogOp!"))
11
})
12
http.ListenAndServe(":8080", r)
13
14
r := chi.NewRouter()
package main
1
2
import (
3
"net/http"
4
"github.com/go-chi/chi/v5"
5
)
6
7
func main() {
8
9
r.HandleFunc("GET /", func(w http.ResponseWriter, req *h
10
w.Write([]byte("Hello DogOp!"))
11
})
12
http.ListenAndServe(":8080", r)
13
}
14
r.HandleFunc("GET /", func(w http.ResponseWriter, req *h
w.Write([]byte("Hello DogOp!"))
})
package main
1
2
import (
3
"net/http"
4
"github.com/go-chi/chi/v5"
5
)
6
7
func main() {
8
r := chi.NewRouter()
9
10
11
12
http.ListenAndServe(":8080", r)
13
}
14
http.ListenAndServe(":8080", r)
package main
1
2
import (
3
"net/http"
4
"github.com/go-chi/chi/v5"
5
)
6
7
func main() {
8
r := chi.NewRouter()
9
r.HandleFunc("GET /", func(w http.ResponseWriter, req *h
10
w.Write([]byte("Hello DogOp!"))
11
})
12
13
}
14
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
r.HandleFunc("GET /", func(w http.ResponseWriter, req *h
w.Write([]byte("Hello DogOp!"))
})
http.ListenAndServe(":8080", r)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15. Bauen und Ausführen
// 1. Kompilieren in Binary
go build -o build/dogop .
// 2. Binary ausführen
./build/dogop
17. REST API fürRechner
Request
POST /api/quote
content-type: application/json
{
"age": 8,
"breed": "chow"
}
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"age": 8,
"breed": "chow",
"tariffs": [
{
"name": "Dog OP _ Basic",
"rate": 12.4
}
]
}
18. REST API fürRechner
func main() {
r.HandleFunc("POST /api/quote", HandleQuote)
}
func HandleQuote(w http.ResponseWriter, r *http.Request) {
1
// 1. JSON Request lesen
2
var q Quote
3
json.NewDecoder(r.Body).Decode(&q)
4
5
// 2. Tarif berechnen
6
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
7
quote.Tariffs = []Tariff{tariff}
8
9
// 3. JSON Response schreiben
10
json.NewEncoder(w).Encode(quote)
11
}
12
13
14
r := chi.NewRouter()
15
16
17
18
r.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
19
w.Write([]byte("Hello DogOp!"))
20
})
21
22
http.ListenAndServe(":8080", r)
23
24
func HandleQuote(w http.ResponseWriter, r *http.Request) {
}
1
// 1. JSON Request lesen
2
var q Quote
3
json.NewDecoder(r.Body).Decode(&q)
4
5
// 2. Tarif berechnen
6
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
7
quote.Tariffs = []Tariff{tariff}
8
9
// 3. JSON Response schreiben
10
json.NewEncoder(w).Encode(quote)
11
12
13
func main() {
14
r := chi.NewRouter()
15
16
r.HandleFunc("POST /api/quote", HandleQuote)
17
18
r.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
19
w.Write([]byte("Hello DogOp!"))
20
})
21
22
http.ListenAndServe(":8080", r)
23
}
24
// 1. JSON Request lesen
var q Quote
json.NewDecoder(r.Body).Decode(&q)
func HandleQuote(w http.ResponseWriter, r *http.Request) {
1
2
3
4
5
// 2. Tarif berechnen
6
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
7
quote.Tariffs = []Tariff{tariff}
8
9
// 3. JSON Response schreiben
10
json.NewEncoder(w).Encode(quote)
11
}
12
13
func main() {
14
r := chi.NewRouter()
15
16
r.HandleFunc("POST /api/quote", HandleQuote)
17
18
r.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
19
w.Write([]byte("Hello DogOp!"))
20
})
21
22
http.ListenAndServe(":8080", r)
23
}
24
// 2. Tarif berechnen
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
quote.Tariffs = []Tariff{tariff}
func HandleQuote(w http.ResponseWriter, r *http.Request) {
1
// 1. JSON Request lesen
2
var q Quote
3
json.NewDecoder(r.Body).Decode(&q)
4
5
6
7
8
9
// 3. JSON Response schreiben
10
json.NewEncoder(w).Encode(quote)
11
}
12
13
func main() {
14
r := chi.NewRouter()
15
16
r.HandleFunc("POST /api/quote", HandleQuote)
17
18
r.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
19
w.Write([]byte("Hello DogOp!"))
20
})
21
22
http.ListenAndServe(":8080", r)
23
}
24
// 3. JSON Response schreiben
json.NewEncoder(w).Encode(quote)
func HandleQuote(w http.ResponseWriter, r *http.Request) {
1
// 1. JSON Request lesen
2
var q Quote
3
json.NewDecoder(r.Body).Decode(&q)
4
5
// 2. Tarif berechnen
6
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
7
quote.Tariffs = []Tariff{tariff}
8
9
10
11
}
12
13
func main() {
14
r := chi.NewRouter()
15
16
r.HandleFunc("POST /api/quote", HandleQuote)
17
18
r.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
19
w.Write([]byte("Hello DogOp!"))
20
})
21
22
http.ListenAndServe(":8080", r)
23
}
24
func HandleQuote(w http.ResponseWriter, r *http.Request) {
// 1. JSON Request lesen
var q Quote
json.NewDecoder(r.Body).Decode(&q)
// 2. Tarif berechnen
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
quote.Tariffs = []Tariff{tariff}
// 3. JSON Response schreiben
json.NewEncoder(w).Encode(quote)
}
func main() {
r := chi.NewRouter()
r.HandleFunc("POST /api/quote", HandleQuote)
r.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello DogOp!"))
})
http.ListenAndServe(":8080", r)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Achtung, noch ohne Fehlerhandling! 💣
20. Struct statt Klasse
type Tariff struct {
Name string `json:"name"`
Rate float64 `json:"rate"`
}
1
2
3
4
5
type Quote struct {
6
Age int `json:"age"`
7
Breed string `json:"breed"`
8
Tariffs []Tariff `json:"tariffs"`
9
}
10
type Quote struct {
Age int `json:"age"`
Breed string `json:"breed"`
Tariffs []Tariff `json:"tariffs"`
}
type Tariff struct {
1
Name string `json:"name"`
2
Rate float64 `json:"rate"`
3
}
4
5
6
7
8
9
10
type Tariff struct {
Name string `json:"name"`
Rate float64 `json:"rate"`
}
type Quote struct {
Age int `json:"age"`
Breed string `json:"breed"`
Tariffs []Tariff `json:"tariffs"`
}
1
2
3
4
5
6
7
8
9
10
// Struct erzeugen
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
21. Fehler💣
func HandleQuote(w http.ResponseWriter, r *http.Request) {
}
1
// 1. JSON Request lesen
2
var q Quote
3
json.NewDecoder(r.Body).Decode(&q) // 💣 Fehler möglich!
4
5
// 2. Tarif berechnen
6
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
7
quote.Tariffs = []Tariff{tariff}
8
9
// 3. JSON Response schreiben
10
json.NewEncoder(w).Encode(quote) // 💣 Fehler möglich!
11
12
json.NewDecoder(r.Body).Decode(&q) // 💣 Fehler möglich!
json.NewEncoder(w).Encode(quote) // 💣 Fehler möglich!
func HandleQuote(w http.ResponseWriter, r *http.Request) {
1
// 1. JSON Request lesen
2
var q Quote
3
4
5
// 2. Tarif berechnen
6
tariff := Tariff{Name: "Dog OP _ Basic", Rate: 12.4}
7
quote.Tariffs = []Tariff{tariff}
8
9
// 3. JSON Response schreiben
10
11
}
12
24. Fehler💣
func HandleQuote(w http.ResponseWriter, r *http.Request) {
}
1
// 1. JSON Request lesen
2
var q Quote
3
4
// Potentieller Fehler
5
err := json.NewDecoder(r.Body).Decode(&q)
6
7
// Auf Fehler prüfen
8
if err != nil {
9
// Fehler behandeln
10
http.Error(w, "Could not decode quote.😔", http.StatusBadRequest)
11
return
12
}
13
14
// ...
15
16
// Potentieller Fehler
err := json.NewDecoder(r.Body).Decode(&q)
func HandleQuote(w http.ResponseWriter, r *http.Request) {
1
// 1. JSON Request lesen
2
var q Quote
3
4
5
6
7
// Auf Fehler prüfen
8
if err != nil {
9
// Fehler behandeln
10
http.Error(w, "Could not decode quote.😔", http.StatusBadRequest)
11
return
12
}
13
14
// ...
15
}
16
// Auf Fehler prüfen
if err != nil {
}
func HandleQuote(w http.ResponseWriter, r *http.Request) {
1
// 1. JSON Request lesen
2
var q Quote
3
4
// Potentieller Fehler
5
err := json.NewDecoder(r.Body).Decode(&q)
6
7
8
9
// Fehler behandeln
10
http.Error(w, "Could not decode quote.😔", http.StatusBadRequest)
11
return
12
13
14
// ...
15
}
16
func HandleQuote(w http.ResponseWriter, r *http.Request) {
// 1. JSON Request lesen
var q Quote
// Potentieller Fehler
err := json.NewDecoder(r.Body).Decode(&q)
// Auf Fehler prüfen
if err != nil {
// Fehler behandeln
http.Error(w, "Could not decode quote.😔", http.StatusBadRequest)
return
}
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
26. HTTPHandlertesten
func TestHandleQuote(t *testing.T) {
}
1
// 1. HTTP Recorder erstellen
2
recorder := httptest.NewRecorder()
3
4
// 2. Request erstellen (mit Body)
5
body := `
6
{
7
"age": 8,
8
"breed": "chow"
9
}
10
`
11
req, _ := http.NewRequest("GET", "/api/quote", strings.NewReader(body))
12
13
// 3. Handler Funktion aufrufen
14
HandleQuote(recorder, req)
15
16
// 4. Return Code prüfen
17
if recorder.Code != http.StatusOK {
18
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.StatusOK)
19
}
20
21
// 1. HTTP Recorder erstellen
recorder := httptest.NewRecorder()
func TestHandleQuote(t *testing.T) {
1
2
3
4
// 2. Request erstellen (mit Body)
5
body := `
6
{
7
"age": 8,
8
"breed": "chow"
9
}
10
`
11
req, _ := http.NewRequest("GET", "/api/quote", strings.NewReader(body))
12
13
// 3. Handler Funktion aufrufen
14
HandleQuote(recorder, req)
15
16
// 4. Return Code prüfen
17
if recorder.Code != http.StatusOK {
18
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.StatusOK)
19
}
20
}
21
// 2. Request erstellen (mit Body)
body := `
{
"age": 8,
"breed": "chow"
}
`
req, _ := http.NewRequest("GET", "/api/quote", strings.NewReader(body))
func TestHandleQuote(t *testing.T) {
1
// 1. HTTP Recorder erstellen
2
recorder := httptest.NewRecorder()
3
4
5
6
7
8
9
10
11
12
13
// 3. Handler Funktion aufrufen
14
HandleQuote(recorder, req)
15
16
// 4. Return Code prüfen
17
if recorder.Code != http.StatusOK {
18
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.StatusOK)
19
}
20
}
21
// 3. Handler Funktion aufrufen
HandleQuote(recorder, req)
func TestHandleQuote(t *testing.T) {
1
// 1. HTTP Recorder erstellen
2
recorder := httptest.NewRecorder()
3
4
// 2. Request erstellen (mit Body)
5
body := `
6
{
7
"age": 8,
8
"breed": "chow"
9
}
10
`
11
req, _ := http.NewRequest("GET", "/api/quote", strings.NewReader(body))
12
13
14
15
16
// 4. Return Code prüfen
17
if recorder.Code != http.StatusOK {
18
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.StatusOK)
19
}
20
}
21
// 4. Return Code prüfen
if recorder.Code != http.StatusOK {
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.StatusOK)
}
func TestHandleQuote(t *testing.T) {
1
// 1. HTTP Recorder erstellen
2
recorder := httptest.NewRecorder()
3
4
// 2. Request erstellen (mit Body)
5
body := `
6
{
7
"age": 8,
8
"breed": "chow"
9
}
10
`
11
req, _ := http.NewRequest("GET", "/api/quote", strings.NewReader(body))
12
13
// 3. Handler Funktion aufrufen
14
HandleQuote(recorder, req)
15
16
17
18
19
20
}
21
func TestHandleQuote(t *testing.T) {
// 1. HTTP Recorder erstellen
recorder := httptest.NewRecorder()
// 2. Request erstellen (mit Body)
body := `
{
"age": 8,
"breed": "chow"
}
`
req, _ := http.NewRequest("GET", "/api/quote", strings.NewReader(body))
// 3. Handler Funktion aufrufen
HandleQuote(recorder, req)
// 4. Return Code prüfen
if recorder.Code != http.StatusOK {
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.StatusOK)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
29. Port perUmgebungsvariable
konfigurieren
// Einfach per Standardlib
port := os.Getenv("DOGOP_PORT")
1
2
function main() {
3
4
http.ListenAndServe(fmt.Sprintf(":%v", config.Port), r)
5
}
6
// Einfach per Standardlib
port := os.Getenv("DOGOP_PORT")
http.ListenAndServe(fmt.Sprintf(":%v", config.Port), r)
1
2
function main() {
3
4
5
}
6
30. Port perUmgebungsvariable
konfigurieren
// Oder mit envconfig von Kelsey Hightower
type Config struct {
Port string `default:"8080"`
}
1
2
3
4
5
6
function main() {
7
var config Config
8
err := envconfig.Process("dogop", &config)
9
if err != nil {
10
log.Fatal(err.Error())
11
}
12
13
http.ListenAndServe(fmt.Sprintf(":%v", config.Port), r)
14
}
15
// Oder mit envconfig von Kelsey Hightower
type Config struct {
Port string `default:"8080"`
}
1
2
3
4
5
6
function main() {
7
var config Config
8
err := envconfig.Process("dogop", &config)
9
if err != nil {
10
log.Fatal(err.Error())
11
}
12
13
http.ListenAndServe(fmt.Sprintf(":%v", config.Port), r)
14
}
15
// Oder mit envconfig von Kelsey Hightower
type Config struct {
Port string `default:"8080"`
}
var config Config
err := envconfig.Process("dogop", &config)
1
2
3
4
5
6
function main() {
7
8
9
if err != nil {
10
log.Fatal(err.Error())
11
}
12
13
http.ListenAndServe(fmt.Sprintf(":%v", config.Port), r)
14
}
15
// Oder mit envconfig von Kelsey Hightower
type Config struct {
Port string `default:"8080"`
}
var config Config
err := envconfig.Process("dogop", &config)
http.ListenAndServe(fmt.Sprintf(":%v", config.Port), r)
1
2
3
4
5
6
function main() {
7
8
9
if err != nil {
10
log.Fatal(err.Error())
11
}
12
13
14
}
15
31. Dockerfile fürDogOP
# 1. DogOp Builder
FROM golang as builder
WORKDIR /app
ADD . /app
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o build/dogop .
1
2
3
4
5
6
# 2. DogOp Container
7
FROM alpine
8
COPY --from=builder /app/build/dogop /usr/bin/
9
EXPOSE 8080
10
ENTRYPOINT ["/usr/bin/dogop"]
11
# 2. DogOp Container
FROM alpine
COPY --from=builder /app/build/dogop /usr/bin/
EXPOSE 8080
ENTRYPOINT ["/usr/bin/dogop"]
# 1. DogOp Builder
1
FROM golang as builder
2
WORKDIR /app
3
ADD . /app
4
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o build/dogop .
5
6
7
8
9
10
11
# 1. DogOp Builder
FROM golang as builder
WORKDIR /app
ADD . /app
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o build/dogop .
# 2. DogOp Container
FROM alpine
COPY --from=builder /app/build/dogop /usr/bin/
EXPOSE 8080
ENTRYPOINT ["/usr/bin/dogop"]
1
2
3
4
5
6
7
8
9
10
11
32. DockerDogOPmit
// 1. Docker Image bauen
>_ pack build dogop-cnb
--buildpack paketo-buildpacks/go
--builder paketobuildpacks/builder-jammy-base
// 2. Docker Image ausühren
>_ docker run dogop-cnb
34. CRUD API fürAngebote
Create Request
POST /api/offer
content-type: application/json
{
"name": "Rollo",
"age": 8,
"breed": "chow",
"customer": "jan"
}
Create Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "427ed4de",
"customer": "jan",
"age": 8,
"breed": "chow",
"name": "Rollo"
}
Read Request
GET /api/offer/427ed4de
Read Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "427ed4de",
"customer": "jan",
"age": 8,
"breed": "chow",
"name": "Rollo"
}
36. Create Request mit HandlerFunction
func HandleCreateOffer(offerRepository *OfferRepository) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
}
}
1
2
// 1. JSON Request lesen
3
var offer Offer
4
json.NewDecoder(req.Body).Decode(&offer)
5
6
// 2. Offer speichern
7
createdOffer, _ := offerRepository.Insert(req.Context(), &offer)
8
9
// 3. JSON Response schreiben
10
json.NewEncoder(w).Encode(createdOffer)
11
12
13
// 1. JSON Request lesen
var offer Offer
json.NewDecoder(req.Body).Decode(&offer)
// 2. Offer speichern
createdOffer, _ := offerRepository.Insert(req.Context(), &offer)
// 3. JSON Response schreiben
json.NewEncoder(w).Encode(createdOffer)
func HandleCreateOffer(offerRepository *OfferRepository) http.HandlerFunc {
1
return func(w http.ResponseWriter, req *http.Request) {
2
3
4
5
6
7
8
9
10
11
}
12
}
13
func HandleCreateOffer(offerRepository *OfferRepository) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
// 1. JSON Request lesen
var offer Offer
json.NewDecoder(req.Body).Decode(&offer)
// 2. Offer speichern
createdOffer, _ := offerRepository.Insert(req.Context(), &offer)
// 3. JSON Response schreiben
json.NewEncoder(w).Encode(createdOffer)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
37. Angebote speichern in Postgres
pgx als Postgres Driverund Toolkit
#
Interface database/sql in Go Standardlib
#
38. Angebote speichern in Postgres
type OfferRepository struct {
connPool *pgxpool.Pool
1
2
}
3
4
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer)
5
// ...
6
}
7
}
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer)
// ...
}
type OfferRepository struct {
1
connPool *pgxpool.Pool
2
3
4
5
6
7
type OfferRepository struct {
connPool *pgxpool.Pool
}
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer)
// ...
}
1
2
3
4
5
6
7
39. Angebote speichern in Postgres
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer) (*Offer, error) {
}
1
// 1. ID generieren
2
offer.ID = uuid.New().String()
3
4
// 2. Transaktion beginnen
5
tx, _ := r.connPool.Begin(ctx)
6
defer tx.Rollback(ctx)
7
8
// 3. Offer per Insert speichern
9
_, err := tx.Exec(
10
ctx,
11
`INSERT INTO offers
12
(id, customer, age, breed, name)
13
VALUES
14
($1, $2, $3, $4, $5)`,
15
offer.ID, offer.Customer, offer.Age, offer.Breed, offer.Name,
16
)
17
if err != nil {
18
return nil, err
19
}
20
21
// 4. Transaktion commiten
22
tx.Commit(ctx)
23
24
// 5. Gespeicherte Offer zurückgeben
25
return offer, nil
26
27
// 1. ID generieren
offer.ID = uuid.New().String()
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer) (*Offer, error) {
1
2
3
4
// 2. Transaktion beginnen
5
tx, _ := r.connPool.Begin(ctx)
6
defer tx.Rollback(ctx)
7
8
// 3. Offer per Insert speichern
9
_, err := tx.Exec(
10
ctx,
11
`INSERT INTO offers
12
(id, customer, age, breed, name)
13
VALUES
14
($1, $2, $3, $4, $5)`,
15
offer.ID, offer.Customer, offer.Age, offer.Breed, offer.Name,
16
)
17
if err != nil {
18
return nil, err
19
}
20
21
// 4. Transaktion commiten
22
tx.Commit(ctx)
23
24
// 5. Gespeicherte Offer zurückgeben
25
return offer, nil
26
}
27
// 2. Transaktion beginnen
tx, _ := r.connPool.Begin(ctx)
defer tx.Rollback(ctx)
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer) (*Offer, error) {
1
// 1. ID generieren
2
offer.ID = uuid.New().String()
3
4
5
6
7
8
// 3. Offer per Insert speichern
9
_, err := tx.Exec(
10
ctx,
11
`INSERT INTO offers
12
(id, customer, age, breed, name)
13
VALUES
14
($1, $2, $3, $4, $5)`,
15
offer.ID, offer.Customer, offer.Age, offer.Breed, offer.Name,
16
)
17
if err != nil {
18
return nil, err
19
}
20
21
// 4. Transaktion commiten
22
tx.Commit(ctx)
23
24
// 5. Gespeicherte Offer zurückgeben
25
return offer, nil
26
}
27
// 3. Offer per Insert speichern
_, err := tx.Exec(
ctx,
`INSERT INTO offers
(id, customer, age, breed, name)
VALUES
($1, $2, $3, $4, $5)`,
offer.ID, offer.Customer, offer.Age, offer.Breed, offer.Name,
)
if err != nil {
return nil, err
}
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer) (*Offer, error) {
1
// 1. ID generieren
2
offer.ID = uuid.New().String()
3
4
// 2. Transaktion beginnen
5
tx, _ := r.connPool.Begin(ctx)
6
defer tx.Rollback(ctx)
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 4. Transaktion commiten
22
tx.Commit(ctx)
23
24
// 5. Gespeicherte Offer zurückgeben
25
return offer, nil
26
}
27
// 4. Transaktion commiten
tx.Commit(ctx)
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer) (*Offer, error) {
1
// 1. ID generieren
2
offer.ID = uuid.New().String()
3
4
// 2. Transaktion beginnen
5
tx, _ := r.connPool.Begin(ctx)
6
defer tx.Rollback(ctx)
7
8
// 3. Offer per Insert speichern
9
_, err := tx.Exec(
10
ctx,
11
`INSERT INTO offers
12
(id, customer, age, breed, name)
13
VALUES
14
($1, $2, $3, $4, $5)`,
15
offer.ID, offer.Customer, offer.Age, offer.Breed, offer.Name,
16
)
17
if err != nil {
18
return nil, err
19
}
20
21
22
23
24
// 5. Gespeicherte Offer zurückgeben
25
return offer, nil
26
}
27
// 5. Gespeicherte Offer zurückgeben
return offer, nil
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer) (*Offer, error) {
1
// 1. ID generieren
2
offer.ID = uuid.New().String()
3
4
// 2. Transaktion beginnen
5
tx, _ := r.connPool.Begin(ctx)
6
defer tx.Rollback(ctx)
7
8
// 3. Offer per Insert speichern
9
_, err := tx.Exec(
10
ctx,
11
`INSERT INTO offers
12
(id, customer, age, breed, name)
13
VALUES
14
($1, $2, $3, $4, $5)`,
15
offer.ID, offer.Customer, offer.Age, offer.Breed, offer.Name,
16
)
17
if err != nil {
18
return nil, err
19
}
20
21
// 4. Transaktion commiten
22
tx.Commit(ctx)
23
24
25
26
}
27
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer) (*Offer, error) {
// 1. ID generieren
offer.ID = uuid.New().String()
// 2. Transaktion beginnen
tx, _ := r.connPool.Begin(ctx)
defer tx.Rollback(ctx)
// 3. Offer per Insert speichern
_, err := tx.Exec(
ctx,
`INSERT INTO offers
(id, customer, age, breed, name)
VALUES
($1, $2, $3, $4, $5)`,
offer.ID, offer.Customer, offer.Age, offer.Breed, offer.Name,
)
if err != nil {
return nil, err
}
// 4. Transaktion commiten
tx.Commit(ctx)
// 5. Gespeicherte Offer zurückgeben
return offer, nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
40. Aus dem Context gerissen
createdOffer, _ := offerRepository.Insert(req.Context(), &offer)
tx, _ := r.connPool.Begin(ctx)
defer tx.Rollback(ctx)
_, err := tx.Exec(
ctx,
)
tx.Commit(ctx)
return func(w http.ResponseWriter, req *http.Request) {
1
// ...
2
3
// 2. Offer speichern
4
5
6
// ...
7
}
8
9
func (r *OfferRepository) Insert(ctx context.Context, offer *Offer) (*Offer, error) {
10
// ..
11
12
// 2. Transaktion beginnen
13
14
15
16
// 3. Offer per Insert speichern
17
18
19
`INSERT INTO offers
20
(id, customer, age, breed, name)
21
VALUES
22
($1, $2, $3, $4, $5)`,
23
offer.ID, offer.Customer, offer.Age, offer.Breed, offer.Name,
24
25
// ...
26
27
// 4. Transaktion commiten
28
29
30
// ...
31
}
32
43. Health Check
// Register Health Check
h, _ := health.New(health.WithChecks(
health.Config{
Name: "db",
Timeout: time.Second * 2,
SkipOnErr: false,
Check: healthPgx.New(healthPgx.Config{
DSN: config.Db,
}),
},
))
1
2
3
4
5
6
7
8
9
10
11
12
// Register Handler Function
13
r.HandleFunc("GET /health", h.HandlerFunc)
14
// Register Handler Function
r.HandleFunc("GET /health", h.HandlerFunc)
// Register Health Check
1
h, _ := health.New(health.WithChecks(
2
health.Config{
3
Name: "db",
4
Timeout: time.Second * 2,
5
SkipOnErr: false,
6
Check: healthPgx.New(healthPgx.Config{
7
DSN: config.Db,
8
}),
9
},
10
))
11
12
13
14
// Register Health Check
h, _ := health.New(health.WithChecks(
health.Config{
Name: "db",
Timeout: time.Second * 2,
SkipOnErr: false,
Check: healthPgx.New(healthPgx.Config{
DSN: config.Db,
}),
},
))
// Register Handler Function
r.HandleFunc("GET /health", h.HandlerFunc)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
47. Fehlerbehandeln
Mit Problem Details
if err != nil {
problem.New(
problem.Title("invalid request"),
problem.Wrap(err),
problem.Status(http.StatusBadRequest),
).WriteTo(responseWriter)
return
}
Mit Standardbibliothek
if err != nil {
http.Error(responseWriter, "invalid request", http.StatusBadRequest)
return
}
48. Middleware
cross-cutting Features füralle Requests
(z.B. Logging, Authentifizierung)
#
erzeugen eine Kette von Handlern
router=> middleware handler=> application handler
#
implementieren das Interface http.Handler
#
49. Middleware loggt Requests
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%v requested URL %v", r.Host, r.URL)
next.ServeHTTP(w, r)
})
}
1
2
3
4
5
6
7
func main() {
8
r := chi.NewRouter()
9
10
// Nutze Logging Middleware
11
r.Use(loggingMiddleware)
12
13
http.ListenAndServe(":8080", r)
14
}
15
// Nutze Logging Middleware
r.Use(loggingMiddleware)
func loggingMiddleware(next http.Handler) http.Handler {
1
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2
log.Printf("%v requested URL %v", r.Host, r.URL)
3
next.ServeHTTP(w, r)
4
})
5
}
6
7
func main() {
8
r := chi.NewRouter()
9
10
11
12
13
http.ListenAndServe(":8080", r)
14
}
15
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%v requested URL %v", r.Host, r.URL)
next.ServeHTTP(w, r)
})
}
func main() {
r := chi.NewRouter()
// Nutze Logging Middleware
r.Use(loggingMiddleware)
http.ListenAndServe(":8080", r)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
50. Middlewares nutzen
// Basis Middleware
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
func main() {
1
r := chi.NewRouter()
2
3
4
5
6
7
8
9
// Timeout über Request Context setzen
10
r.Use(middleware.Timeout(60 * time.Second))
11
12
http.ListenAndServe(":8080", r)
13
}
14
// Timeout über Request Context setzen
r.Use(middleware.Timeout(60 * time.Second))
func main() {
1
r := chi.NewRouter()
2
3
// Basis Middleware
4
r.Use(middleware.RequestID)
5
r.Use(middleware.RealIP)
6
r.Use(middleware.Logger)
7
r.Use(middleware.Recoverer)
8
9
10
11
12
http.ListenAndServe(":8080", r)
13
}
14
// Basis Middleware
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
// Timeout über Request Context setzen
r.Use(middleware.Timeout(60 * time.Second))
func main() {
1
r := chi.NewRouter()
2
3
4
5
6
7
8
9
10
11
12
http.ListenAndServe(":8080", r)
13
}
14
51. Zusammenfassung DogOP
v0.1
1. Go Projekt aufsetzen
2. einfache Rest API Handler
3. Konfiguration per
Umgebungsvariable
4. Structs
5. Fehlerbehandeln
6. Unit Tests
7. DockerContainerbauen
v0.2
1. CRUD API fürAngebote
2. Angebote in Postgres speichern
3. Context
4. Health Check
5. Problem Details
6. Middleware