Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Developing Web Applications with Go

Developing Web Applications with Go

Developing Web Applications with Go.

Sau Sheong Chang

August 19, 2014
Tweet

More Decks by Sau Sheong Chang

Other Decks in Technology

Transcript

  1. Browser/Client Server HTTP  Request • Request  line GET /some/index.html HTTP/1.1

    • Request  headers • Empty  line • Message  body  (optional)
  2. Browser/Client Server HTTP  Request HTTP  Response • Request  line GET

    /some/index.html HTTP/1.1 • Request  headers • Empty  line • Message  body  (optional)
  3. Browser/Client Server HTTP  Request HTTP  Response • Request  line GET

    /some/index.html HTTP/1.1 • Request  headers • Empty  line • Message  body  (optional) • Status  line HTTP/1.1 200 OK
  4. Browser/Client Server HTTP  Request HTTP  Response • Request  line GET

    /some/index.html HTTP/1.1 • Request  headers • Empty  line • Message  body  (optional) • Status  line HTTP/1.1 200 OK • Response  headers
  5. Browser/Client Server HTTP  Request HTTP  Response • Request  line GET

    /some/index.html HTTP/1.1 • Request  headers • Empty  line • Message  body  (optional) • Status  line HTTP/1.1 200 OK • Response  headers • Empty  line
  6. Browser/Client Server HTTP  Request HTTP  Response • Request  line GET

    /some/index.html HTTP/1.1 • Request  headers • Empty  line • Message  body  (optional) • Status  line HTTP/1.1 200 OK • Response  headers • Empty  line • Message  body  (optional)
  7. Parts of a web app • Routes
 Processing  the  request

      • Templates
 Generating  response   • Store
 Persisting  data
  8. net/http • Server  -­‐  ServeMux(type),  Handler(type),   Handle(func),  HandleFunc(func),  

    ListenAndServe(func),  Serve(func) • Client  -­‐  Get,  Post,  Head  etc  (func) 10
  9. net/http • Server  -­‐  ServeMux(type),  Handler(type),   Handle(func),  HandleFunc(func),  

    ListenAndServe(func),  Serve(func) • Client  -­‐  Get,  Post,  Head  etc  (func) • Request,  Response,  ResponseWriter  (type) 10
  10. net/http • Server  -­‐  ServeMux(type),  Handler(type),   Handle(func),  HandleFunc(func),  

    ListenAndServe(func),  Serve(func) • Client  -­‐  Get,  Post,  Head  etc  (func) • Request,  Response,  ResponseWriter  (type) • Cookie(type) 10
  11. net/http • Server  -­‐  ServeMux(type),  Handler(type),   Handle(func),  HandleFunc(func),  

    ListenAndServe(func),  Serve(func) • Client  -­‐  Get,  Post,  Head  etc  (func) • Request,  Response,  ResponseWriter  (type) • Cookie(type) • Redirect(func) 10
  12. Routes package main! ! import (! "fmt"! "net/http"! )! !

    func handler(w http.ResponseWriter, r *http.Request) {! fmt.Fprintf(w, "Hello World, %s!", r.URL.Path[1:])! }! ! func main() {! http.HandleFunc("/", handler)! http.ListenAndServe(":8080", nil)! }!
  13. Routes package main! ! import (! "fmt"! "net/http"! )! !

    func main() {! mux := http.NewServeMux()! mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {! fmt.Fprintf(w, "Hello World, %s!", r.URL.Path[1:])! })! http.ListenAndServe(":8080", mux)! }!
  14. Routes package main! ! import (! "fmt"! "net/http"! )! !

    type MyHandler struct {}! ! func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {! fmt.Fprintf(w, "Hello World, %s!", r.URL.Path[1:])! }! ! func main() {! http.ListenAndServe(":8080", &MyHandler{})! }!
  15. Routes type MyHandler struct {}! ! func (h *MyHandler) ServeHTTP(w

    http.ResponseWriter, r *http.Request) {! fmt.Fprintf(w, "Hello World, %s!", r.URL.Path[1:])! }! ! func main() {! server := &http.Server{! ! Addr: "0.0.0.0:8080",! ! Handler: &MyHandler{},! ! ReadTimeout: 10 * time.Second,! ! WriteTimeout: 600 * time.Second,! ! MaxHeaderBytes: 1 << 20,! }! server.ListenAndServe() ! }
  16. Routes package main! ! import (! "fmt"! "net/http"! "github.com/julienschmidt/httprouter"! )!

    ! func main() {! router := httprouter.New() ! router.GET("/:name", hello) ! http.ListenAndServe(":8080", router)! }! ! func hello(w http.ResponseWriter, r *http.Request, p httprouter.Params) {! fmt.Fprintf(w, "Hello World, %s!", p.ByName("name"))! }!
  17. Routes package main! ! import (! "fmt"! "net/http"! "github.com/julienschmidt/httprouter"! )!

    ! func main() {! router := httprouter.New() ! router.GET("/:name", hello) ! http.ListenAndServe(":8080", router)! }! ! func hello(w http.ResponseWriter, r *http.Request, p httprouter.Params) {! fmt.Fprintf(w, "Hello World, %s!", p.ByName("name"))! }! Not  part  of  standard   Go  libraries
  18. Serve Files func main() {! router := httprouter.New() ! router.ServeFiles("/images/*filepath",

    ! http.Dir("public/images"))! router.ServeFiles("/css/*filepath", ! http.Dir("public/css"))! router.ServeFiles(“/fonts/*filepath",! http.Dir("public/fonts"))! router.ServeFiles("/js/*filepath", ! http.Dir("public/js"))! http.ListenAndServe(":8080", router)! }! !
  19. Templates 21 Hello  World!  This  is   {  name  }

    name  =  “Sau  Sheong” Hello  World!  This  is   Sau  Sheong +
  20. Templates • text/template  -­‐  data  driven  templates • html/template  -­‐

     data  driven  templates  for  HTML • Actions  (data  evaluations  or  control  structure)  -­‐   delimited  by  {{  and  }}
  21. Templates • text/template  -­‐  data  driven  templates • html/template  -­‐

     data  driven  templates  for  HTML • Actions  (data  evaluations  or  control  structure)  -­‐   delimited  by  {{  and  }} • Arguments  -­‐  values/variables  in  the  template
  22. Templates • text/template  -­‐  data  driven  templates • html/template  -­‐

     data  driven  templates  for  HTML • Actions  (data  evaluations  or  control  structure)  -­‐   delimited  by  {{  and  }} • Arguments  -­‐  values/variables  in  the  template • Pass  variables  to  template,  accessible  through   the  ‘dot’  {{ . }}
  23. Templates • text/template  -­‐  data  driven  templates • html/template  -­‐

     data  driven  templates  for  HTML • Actions  (data  evaluations  or  control  structure)  -­‐   delimited  by  {{  and  }} • Arguments  -­‐  values/variables  in  the  template • Pass  variables  to  template,  accessible  through   the  ‘dot’  {{ . }} • Can  define  variables  (start  with  $)
  24. Templates • Pipeline  -­‐  a  chained  sequence  of  commands •

    Function  -­‐  global  functions  (predefined)  or   custom  functions
  25. Templates • Pipeline  -­‐  a  chained  sequence  of  commands •

    Function  -­‐  global  functions  (predefined)  or   custom  functions • Include  other  templates  with  keyword   ‘template’
  26. Generating response 1. Set  {{ define <template name> }}  in

     the   template 2. Allocate  the  template  (in  the  route)  using  New()
  27. Generating response 1. Set  {{ define <template name> }}  in

     the   template 2. Allocate  the  template  (in  the  route)  using  New() 3. Create  the  template  by  parsing  one  or  more   files  using  Parse(),  ParseFiles()  or   ParseGlob()  
  28. Generating response 1. Set  {{ define <template name> }}  in

     the   template 2. Allocate  the  template  (in  the  route)  using  New() 3. Create  the  template  by  parsing  one  or  more   files  using  Parse(),  ParseFiles()  or   ParseGlob()   4. Execute  the  template,  passing  the  output   writer  and  the  argument  (or  nil  if  none)
  29. t1.html 25 {{ define "t1" }}! ! <!DOCTYPE html>! <html>!

    {{ template "header" }}! <body>! Hello World, {{ . }}!! </body>! </html>! ! {{ end }}!
  30. header.html 26 {{ define "header" }}! <head>! <meta http-equiv="Content-Type" content="text/html;

    charset=utf-8">! <title>Template Example</title>! </head>! {{ end }}!
  31. templates.go 27 package main! ! import (! "os"! "html/template"! )!

    ! func main() {! t := template.New("t1")! t = template.Must(t.ParseGlob("*.html")) ! t.Execute(writer, "Sau Sheong")! }!
  32. templates.go 27 package main! ! import (! "os"! "html/template"! )!

    ! func main() {! t := template.New("t1")! t = template.Must(t.ParseGlob("*.html")) ! t.Execute(writer, "Sau Sheong")! }! Allocate
  33. templates.go 27 package main! ! import (! "os"! "html/template"! )!

    ! func main() {! t := template.New("t1")! t = template.Must(t.ParseGlob("*.html")) ! t.Execute(writer, "Sau Sheong")! }! Create
  34. templates.go 27 package main! ! import (! "os"! "html/template"! )!

    ! func main() {! t := template.New("t1")! t = template.Must(t.ParseGlob("*.html")) ! t.Execute(writer, "Sau Sheong")! }! Execute
  35. templates.go 27 package main! ! import (! "os"! "html/template"! )!

    ! func main() {! t := template.New("t1")! t = template.Must(t.ParseGlob("*.html")) ! t.Execute(writer, "Sau Sheong")! }!
  36. Response 28 <!DOCTYPE html>! <html>! ! <head>! <meta http-equiv="Content-Type" content="text/html;

    charset=utf-8">! <title>Template Example</title>! </head>! ! <body>! Hello World, Sau Sheong!! </body>! </html>
  37. Response 28 <!DOCTYPE html>! <html>! ! <head>! <meta http-equiv="Content-Type" content="text/html;

    charset=utf-8">! <title>Template Example</title>! </head>! ! <body>! Hello World, Sau Sheong!! </body>! </html> from  header.html
  38. Response 28 <!DOCTYPE html>! <html>! ! <head>! <meta http-equiv="Content-Type" content="text/html;

    charset=utf-8">! <title>Template Example</title>! </head>! ! <body>! Hello World, Sau Sheong!! </body>! </html> from  header.html replaces  {{  .  }}
  39. templates.go 29 type Presentation struct {! Title string! Author string!

    }! ! func main() {! t := template.New("t2")! t = template.Must(t.ParseGlob("*.html")) ! presso := Presentation{! Title: "Write Web Applications with Go",! Author: "Chang Sau Sheong",! }! t.Execute(writer, presso)! }!
  40. t2.html 30 {{ define "t2" }}! ! <!DOCTYPE html>! <html>!

    {{ template "header" }}! <body>! <h1>{{ .Title }}</h1>! <h3>{{ .Author}}</h3>! </body>! </html>! ! {{ end }}!
  41. Response 31 <!DOCTYPE html>! <html>! ! <head>! <meta http-equiv="Content-Type" content="text/html;

    charset=utf-8">! <title>Template Example</title>! </head>! ! <body>! <h1>Write Web Applications with Go</h1>! <h3>Chang Sau Sheong</h3>! </body>! </html>!
  42. SQL

  43. 35 package main! ! import(! "log"! "fmt" ! "database/sql"! _

    "github.com/lib/pq"! )! ! func main() {! db, _ := sql.Open("postgres", "dbname=blog sslmode=disable”)! defer db.Close() ! ! rows, err := db.Query("SELECT name, email FROM users WHERE email = $1", “[email protected]”); if err != nil {! log.Fatal(err)! }! for rows.Next() {! var name, email string! if err := rows.Scan(&name, &email); err != nil {! log.Fatal(err)! }! fmt.Printf("%s - %s\n", name, email)! }! }!
  44. Define models 38 type User struct {! Id int64! Uuid

    string `sql:"size:255;not null;unique"`! Email string `sql:"size:255"`! Password string `sql:"size:255"`! Name string `sql:"size:255"`! CreatedAt time.Time! }! ! type Session struct {! Id int64! Uuid string `sql:"size:255;not null;unique"`! UserId int64 ! CreatedAt time.Time! } // foreign key for User table
  45. Initialize database 39 var DB gorm.DB! ! // initialize gorm!

    func init() {! var err error! DB, err = gorm.Open("postgres", "user=test password=test dbname=test sslmode=disable")! if err != nil {! panic(fmt.Sprintf("Got error when connect database, the error is '%v'", err))! }! migrate()! }
  46. Migrate tables 40 // Create tables! func migrate() {! DB.Exec("DROP

    TABLE users;DROP TABLE sessions;")! DB.AutoMigrate(User{})! DB.AutoMigrate(Session{})! }
  47. Create 41 user := User{Name: "Sau Sheong", ! Email: "[email protected]",

    ! Password: “passw0rd”,! }! DB.Save(&user) ! !
  48. Query 42 var user = User{}! // Get the first

    record! err := DB.Where("email = ?", ! "[email protected]").First(&user).Error! if err != nil {! fmt.Println("Cannot retrieve this user:", err)! } ! ! // Get the multiple records! var users []User! err := DB.Where("email LIKE ?", ! "%@gmail.com").Find(&users).Error! if err != nil {! fmt.Println("Cannot retrieve users:", err)! }
  49. Update & Delete 43 var user = User{}! // Get

    the first record! err := DB.Where("email = ?", ! "[email protected]").First(&user).Error! if err != nil {! fmt.Println("Cannot retrieve this user:", err)! } ! ! // Update the name! user.Name = "Batman"! DB.Save(&user)! ! // Delete the record! DB.Delete(&user)!
  50. Callbacks 44 // Before creating a user, add in the

    uuid! func (u *User) BeforeCreate() (err error) {! ! ! u.Password = encrypt(u.Password)! ! u.Uuid = createUUID()! ! return! }
  51. Associations 45 user := User{Name: "Sau Sheong", ! Email: "[email protected]",

    ! Password: "test",! }! DB.Save(&user) ! ! sess := Session{UserId: user.Id}! DB.Save(&sess)! ! var usr User! err := DB.Model(&sess).Related(&usr).Error! if err != nil {! fmt.Println("Cannot retrieve this user:", err)! } ! ! var session Session! err := DB.Model(&user).Related(&session).Error! if err != nil {! fmt.Println("Cannot retrieve this session:", err)! }
  52. Associations 45 user := User{Name: "Sau Sheong", ! Email: "[email protected]",

    ! Password: "test",! }! DB.Save(&user) ! ! sess := Session{UserId: user.Id}! DB.Save(&sess)! ! var usr User! err := DB.Model(&sess).Related(&usr).Error! if err != nil {! fmt.Println("Cannot retrieve this user:", err)! } ! ! var session Session! err := DB.Model(&user).Related(&session).Error! if err != nil {! fmt.Println("Cannot retrieve this session:", err)! } Retrieve  user  given   the  session
  53. Associations 45 user := User{Name: "Sau Sheong", ! Email: "[email protected]",

    ! Password: "test",! }! DB.Save(&user) ! ! sess := Session{UserId: user.Id}! DB.Save(&sess)! ! var usr User! err := DB.Model(&sess).Related(&usr).Error! if err != nil {! fmt.Println("Cannot retrieve this user:", err)! } ! ! var session Session! err := DB.Model(&user).Related(&session).Error! if err != nil {! fmt.Println("Cannot retrieve this session:", err)! } Retrieve  session   given  the  user
  54. Associations 45 user := User{Name: "Sau Sheong", ! Email: "[email protected]",

    ! Password: "test",! }! DB.Save(&user) ! ! sess := Session{UserId: user.Id}! DB.Save(&sess)! ! var usr User! err := DB.Model(&sess).Related(&usr).Error! if err != nil {! fmt.Println("Cannot retrieve this user:", err)! } ! ! var session Session! err := DB.Model(&user).Related(&session).Error! if err != nil {! fmt.Println("Cannot retrieve this session:", err)! }
  55. Other features • Query  chains • Batch  updates  and  deletes

    • Soft  delete   • Associations • Has  One
  56. Other features • Query  chains • Batch  updates  and  deletes

    • Soft  delete   • Associations • Has  One • Belongs  To
  57. Other features • Query  chains • Batch  updates  and  deletes

    • Soft  delete   • Associations • Has  One • Belongs  To • Has  Many
  58. Other features • Query  chains • Batch  updates  and  deletes

    • Soft  delete   • Associations • Has  One • Belongs  To • Has  Many • Many  to  Many
  59. Other features • FirstOrInit,  FirstOrCreate • Order,  Limit,  Offset,  Count

    • Group,  Having,  Joins • Transactions • Logger
  60. Frameworks • Gorilla  web  toolkit   • Martini   •

    Gin  Gonic   • Beego   • Revel  
  61. Unit Testing 55 package main! ! import( ! "testing"! )!

    ! // Test package function that adds a new user! func TestAddUser(t *testing.T) {! addUser("Sau Sheong", "[email protected]", "password")! if val, ok := users["[email protected]"]; !ok {! t.Errorf("Cannot add user")! } else {! if val.Name != "Sau Sheong" {! t.Errorf("User name is wrong")! }! }! }
  62. HTTP Testing 56 package main! ! ...! ! func TestIndex(t

    *testing.T) { ! router := httprouter.New()! router.GET("/", index)! writer := httptest.NewRecorder()! request, _ := http.NewRequest("GET", "/", nil) ! router.ServeHTTP(writer, request)! ! if writer.Code != 302 {! t.Errorf("Response code is %v", writer.Code)! } ! }
  63. HTTP Testing 57 func TestSignUp(t *testing.T) {! router := httprouter.New()!

    router.POST("/signup", createUser)! writer := httptest.NewRecorder()! body := strings.NewReader("name=Sau Sheong&[email protected]&password=password")! request, _ := http.NewRequest("POST", "/signup", body)! request.Header.Add("Content-Type", "application/x-www- form-urlencoded")! router.ServeHTTP(writer, request)! ! if writer.Code != 302 {! t.Errorf("Response code is %v", writer.Code)! }! if writer.Header().Get("Location") != "/login" {! t.Errorf("Location is %v", writer.Header().Get("Location"))! }! }
  64. HTTP Testing 58 func TestAuthenticate(t *testing.T) {! addUser("Sau Sheong", "[email protected]",

    "password")! router := httprouter.New()! router.POST("/login", authenticate) ! writer := httptest.NewRecorder()! body := strings.NewReader("[email protected]&password=pa ssword")! request, _ := http.NewRequest("POST", "/login", body)! request.Header.Add("Content-Type", "application/x-www- form-urlencoded")! router.ServeHTTP(writer, request)! ...! if !strings.HasPrefix(writer.Header().Get("Set- Cookie"), "pixelate_cookie") {! t.Errorf("Cookie not set")! }! }
  65. Go App Engine • Download  the  Go  App  Engine  SDK

    • Install  Mercurial • Add  a  file  app.yaml  to  the  same  directory 60
  66. Go App Engine • Download  the  Go  App  Engine  SDK

    • Install  Mercurial • Add  a  file  app.yaml  to  the  same  directory 60 application: pixelate! version: 1! runtime: go! api_version: go1! ! handlers:! - url: /.*! script: _go_app
  67. Go App Engine • Code  changes  required: • Change  all

     package  names  from  main  to  the   application  name  (in  this  case  it  is   sausheong-pixelate) 61
  68. Go App Engine • Code  changes  required: • Change  all

     package  names  from  main  to  the   application  name  (in  this  case  it  is   sausheong-pixelate) • Change  the  main()  function  to  init() 61
  69. Go App Engine • Code  changes  required: • Change  all

     package  names  from  main  to  the   application  name  (in  this  case  it  is   sausheong-pixelate) • Change  the  main()  function  to  init() • Change  from  http.ListenAndServe()  to   http.Handle(mux) 61
  70. Run locally • Run  at  command  line: • Open  browser

     to: 62 go_appengine/dev_appserver.py sausheong-pixelate
  71. Run locally • Run  at  command  line: • Open  browser

     to: 62 go_appengine/dev_appserver.py sausheong-pixelate http://localhost:8080
  72. Deploy to Google App Engine • Create  app  on  Google

     App  Engine • Run  at  command  line: 63 go_appengine/goapp deploy sausheong-pixelate
  73. Deploy to Google App Engine • Create  app  on  Google

     App  Engine • Run  at  command  line: 63 go_appengine/goapp deploy sausheong-pixelate
  74. Deploy to Google App Engine • Create  app  on  Google

     App  Engine • Run  at  command  line: • Open  browser  to: 63 go_appengine/goapp deploy sausheong-pixelate
  75. Deploy to Google App Engine • Create  app  on  Google

     App  Engine • Run  at  command  line: • Open  browser  to: 63 go_appengine/goapp deploy sausheong-pixelate http://sausheong-pixelate.appspot.com
  76. My projects 64 • Pixelate
 Convert  a  JPEG  file  to

     a  pixelated  version   • TodayReader
 A  faster,  simpler  Today  (newspaper)  reader   • GoAuthServ
 Simple  authentication  service   • Polyglot
 Multi-­‐language  web  framework