Go Hands-on - 6 april 2017

Harry de Boer 0fb79c4362 updates nav workshop vor 7 Jahren
ex1 9c01ab7cd6 opgaven uitgewerkt, klaar voor nu vor 9 Jahren
ex2 9c01ab7cd6 opgaven uitgewerkt, klaar voor nu vor 9 Jahren
ex3 9c01ab7cd6 opgaven uitgewerkt, klaar voor nu vor 9 Jahren
ex4 9aa59cba83 Voeg plaatjes en kleuren toe aan ex6 vor 9 Jahren
ex5 9c01ab7cd6 opgaven uitgewerkt, klaar voor nu vor 9 Jahren
ex6 0fb79c4362 updates nav workshop vor 7 Jahren
resources 33f7f78fea Voek ex7 toe vor 9 Jahren
.gitignore 9c01ab7cd6 opgaven uitgewerkt, klaar voor nu vor 9 Jahren
README.html b6c7da6f5d README.html toegevoegd vor 9 Jahren
README.md 0fb79c4362 updates nav workshop vor 7 Jahren
opgave5.zip 9c01ab7cd6 opgaven uitgewerkt, klaar voor nu vor 9 Jahren
opgave6.zip 9c01ab7cd6 opgaven uitgewerkt, klaar voor nu vor 9 Jahren
opgave6v2.zip 0fb79c4362 updates nav workshop vor 7 Jahren

README.html




Go Hands-on


6 april 2017


Harry de Boer, Quintor


Wat is go


Volgens https://golang.org: Go is an open source programming language that makes it easy to build simple, reliable,

and efficient software.


Eigenschappen:



  • type safe

  • compiled

  • garbage collected

  • staticly linked binaries


Tips



  • zoek online op ‘golang’ ipv ‘go’


Opgaven


In deze hands-on sessie beginnen we met een simpel programma.

Deze breiden we steeds verder uit naar volledig werkende backend voor een webapplicatie.

Opdrachten 1 t/m 5 zijn bedoeld als eerste kennismakinng.

Voor opdrachten 6 t/m 10 is een simpele website aangeleverd waarvoor gedurende de opdrachten de backend geschreven

wordt.


Opgave 1: een eerste programma


In deze opgave maken we ons eerste werkende go programma.

Een go programma begint altijd met een package declaratie.

Voor een executable - wat we hier gaan maken - is altijd dit altijd


package main

Na de package declaratie komen de import statements:


import (
"fmt"
)

Functies uit een geimporteerd package kan je aanroepen met packagenaam.Functie(...).

Alleen funties/methoden/typen/velden die beginnen met een hoofdletter zijn exported (public) en zichtbaar voor

andere packges. Binnen een package is alles zichtbaar.

Een package zit altijd in een directory van dezelfde naam, met uitzondering van main.


Het entrypoint van een executable is de main functie.


func main() {
statement
}

Het getoonde statement heeft geen ; aan het einde, deze zijn optioneel in go.


Schrijf nu een programma dat je favoriete paar woorden afdrukt.

Importeer hier voor package fmt en gebruik de Println functie.

De naam van het bestand maakt niet uit, maar deze moet eindigen op .go


Compileer het programma met


go build <bestandsnaam>.go

De resulterende executable zal dezelfde naam krijgen als de directory waar deze in staat.

Waar wacht je op? Voer hem uit!


Opgave 2: functies


Functies in go beginnen met het func keyword gevolgd door een lijst van parameters tussen haakjes


func bla(naam string) string {
return naam + "bla"
}

In tegenstelling tot veel andere talen komen de types van de parameters na de naam.


Voeg nu je eerste functie toe aan je programma.

Geef als parameter een string mee, verwerk deze in een andere string met fmt.Sprintf, return het resultaat en druk dit

af.


Opgave 3: template parsing


Sprintf is prima voor kleine strings, maar voor grotere teksten is een template parser handig.

Importeer het text/template package en maak een nieuw template:


template.New("helloTemplate").Parse("Hello {{.}}\n")

In de documentatie van template.New kan je zien dat deze twee return values heeft.

De eerste is het template, noem deze t.

De tweede is een error die je bijvoorbeeld kan printen, of je kan je programma laten crashen met


if err != nil {
panic(err)
}

Het template verwerken gaat met de Execute methode die op t aangeroepen kan worden


t.Execute(os.Stdout, "Quintor")

De eerste parameter is een interface, io.Writer, die afdwingt dat het meegegeven argument een Write([]byte)

methode heeft. In dit geval wordt naar de standaard uitvoer geschreven.

De tweede parameter is een object waarmee de placeholders in het template vervangen worden.

In dit voorbeeld is dit een simpele string


Verwerk nu het bovenstaande in je programma.


Opgave 4: template parsing #2


Templates kunnen ook uit een file geladen worden met


template.ParseFiles("mijntemplate.tpl")

Pas je programma nu zo aan dat het ParseFiles gebruikt ipv New/Parse.


Opgave 5: een webserver


Het io.Writer interface kan veelzijdig gebruikt worden.

Zoals in het voorbeeld hieronder te zien is voldoet http.ResponseWriter ook aan dit interface.


Analyseer het onderstaande programma en bouw een webserver in je eigen programma in.

Gebruik evt. de templates/resources in opgave5.zip


Een paar hints:



  • go kent geen classes

  • datatypen worden gedefineerd met type, hieronder wordt het type server

    gedefineerd als een struct met een veld template

  • main() is een funcie

  • handleRoot() is een methode op het type server

  • http.ListenAndServe start een http server op

  • http.FileServer serveert static files in een directory

  • wees niet bang om de documentatie van een package er bij te pakken


package main

import (
"log"
"net/http"
"text/template"
)

type server struct {
template *template.Template
}

func main() {
t := template.Must(template.ParseFiles("templates/index.html"))
s := server{template: t}

http.HandleFunc("/", s.handleRoot)
http.Handle("/resources/", http.FileServer(http.Dir(".")))

err := http.ListenAndServe(":8080", nil)
log.Fatal(err)
}

func (s *server) handleRoot(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf8")

err := s.template.Execute(w, "Vrolijk pasen!")

if err != nil {
log.Println(err)
}
}

Opgave 6: paaseieren zoeken


opgave6.zip bevat een html/javascript webapplicatie.

Serveer de bestanden in /resources met http.FileServer en zoek de eieren door in het scherm te klikken.


Je zult merken dat je geen mooie paaseieren krijgt, hiervoor moet de backend geimplementeerd.


Maak een server type aan met een veld naam.


type server struct {
name string
}

Initialiseer een variabele van dit type:


s := server{name: "Golang Hands-on"}

Maak een endpoint onder /api/greet


http.HandleFunc("/api/greet/", s.titleHandler)

En de bijbehorende handler


func (s server) titleHandler(w http.ResponseWriter, r *http.Request) {
var err error

w.Header().Set("Content-Type", "text/plain; charset=utf8")
_, err = fmt.Fprint(w, s.name)

if err != nil {
log.Println(err)
}
}

Als het goed is kan je nu een paasei vinden!


Opgave 7: marshalling json


Om het tweede ei te vinden moet het endpoint om kunnen gaan met de Accept: application/json

http header. Voor het eerste ei werd om text/plain gevraagd, voor het tweede ei

wordt json gevraagd op hetzelfde endpoint. Er wordt een json object in het volgende formaat verwacht:


{title: "title", subtitle: "subtitle"}  

Je kan de header ophalen met


r.Header.Get("Accept")

Je kan het strings package gebruiken om strings te manipuleren.


Om json te encoden heb je een geschikt type nodig om te encoden, bijvoorbeeld


type greet struct {
Title string `json:"title"`
Subtitle string `json:"subtitle"`
}

Deze kan je encoden met het json package.

json.NewEncoder verwacht wederom een io.Writer.


   e := json.NewEncoder(w)
err = e.Encode(greet{Title: s.name, Subtitle: "Hallo Quintor!"})

Stuur ook het juiste Content-Type mee:


w.Header().Set("Content-Type", "application/json; charset=utf8")

Als je dit goed doet heb je een ei erbij!


Opgave 8: unmarshalling json


Maak een nieuwe Handler voor het endpoint /api/sum/.

In het request wordt een json string meegestuurd.

Om deze te kunnen decoden hebben we een struct nodig met de juiste structuur:


type sumRequest struct {
Start int `json:"start"`
End int `json:"end"`
Numbers []int `json:"numbers"`
}

En decode met:


var sreq sumRequest
d := json.NewDecoder(r.Body)
err := d.Decode(&sreq)

Als response verwacht de client een json object van de vorm


{answer: 13, contributers: 3}

Het veld answer moet de som van de elementen tussen start (inclusief) en end (exclusief)

in de numbers slice bevatten. contributers moet gelijk zijn aan end - start.

Maak hier zelf een type voor aan.


Opgave 9: data opslaan in een database


Maak een nieuw endpoint /api/store/ waar een json request in de vorm


{name: "name", message: "message"} 

verwacht en een response van de vorm terug stuurt.


{id: 0}

Om data op te slaan gaan we gebruik maken maken van gorm.

Voeg de volgende imports toe:


"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"

Pas de server struct aan en voeg een type Message toe, dit wordt onze database entiteit.


type server struct {
name string
db *gorm.DB
}

type Message struct {
ID uint
Name string
Content string
}

Voeg de volgende regels toe bovenaan je main()


db, err := gorm.Open("sqlite3", "sqlite.db")
if err != nil {
log.Fatal("Could not open database", err)
}
defer db.Close()

db.DropTableIfExists(&Message{})
db.AutoMigrate(&Message{})

s := server{name: "Golang Hands-on", db: db}

In je handler kan je nu een message als volgt opslaan.

Zorg ervoor dat je de message vult met de waarden uit het json request.


m := Message{Name: "naam", Content: "inhoud"}
db.Create(&m)
log.Println("message id", m.ID)

Na de create kan je het ID opvragen waarmee het message is opgeslagen.

Deze stuur je terug naar de client.


Opgave 10: data ophalen uit database


Voor het laatste paasei gaan we data weer ophalen uit de database aan de hand van een id.

De client zal een request doen naar /api/store/<id> en verwacht json object in hen volgende

formaat als response:


{name: "name", message: "message"}

Gebruik voor het ophalen First, zie http://jinzhu.me/gorm/crud.html#query


Bonusopgaven



Resources