|
|
7 năm trước cách đây | |
|---|---|---|
| ex1 | 9 năm trước cách đây | |
| ex2 | 9 năm trước cách đây | |
| ex3 | 9 năm trước cách đây | |
| ex4 | 9 năm trước cách đây | |
| ex5 | 9 năm trước cách đây | |
| ex6 | 7 năm trước cách đây | |
| resources | 9 năm trước cách đây | |
| .gitignore | 9 năm trước cách đây | |
| README.html | 9 năm trước cách đây | |
| README.md | 7 năm trước cách đây | |
| opgave5.zip | 9 năm trước cách đây | |
| opgave6.zip | 9 năm trước cách đây | |
| opgave6v2.zip | 7 năm trước cách đây |
6 april 2017
Harry de Boer, Quintor
Volgens https://golang.org: Go is an open source programming language that makes it easy to build simple, reliable,
and efficient software.
Eigenschappen:
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.
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!
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.
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.
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.
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:
type, hieronder wordt het type serverstruct met een veld templatemain() is een funciehandleRoot() is een methode op het type serverhttp.ListenAndServe start een http server ophttp.FileServer serveert static files in een directorypackage 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)
}
}
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!
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!
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.
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.
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