[Webapp] Dart HTTP POST JSON Data to Go Server


This post shows how to write a web application which passes JSON-format data between browser (client side) and web server (server side) via HTTP POST. The code which runs on the browser is implemented with Dart, and the code which runs on web server is implemented with Go. I put explanation and references directly in the code for the convenience of fast lookup and tracing code.

To run the code, download the following files in the same directory. Modify the path in Makefile and type make to execute. (If you do not have Dartium, type make js before you type make). Then open your browser at http://localhost:8000/.

Development Environment: Ubuntu Linux 14.10, Dart 1.8, Go 1.4.

Server side (Go web server):

server.go | repository | view raw
 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Google search keyword: golang http template
// https://golang.org/doc/articles/wiki/
// http://golang.org/pkg/html/template/

// Google search keyword: go serve static file
// http://stackoverflow.com/questions/17690230/go-web-page-static-file-serving
// http://stackoverflow.com/questions/25945538/go-golang-to-serve-a-specific-html-file

// Google search keyword: golang http minetype set
// https://golang.org/src/net/http/pprof/pprof.go#L73
// http://stackoverflow.com/questions/12830095/setting-http-headers-in-golang

// Handling JSON Post Request in Go
// http://stackoverflow.com/questions/15672556/handling-json-post-request-in-go
package main

import (
	"html/template"
	"net/http"
	"encoding/json"
	"fmt"
)

type dataFromClient struct {
	Title string // cannot use title (lower case will case json decode failure)
	Url string // cannot use url (lower case will case json decode failure)
}

func handler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/app.dart" {
		w.Header().Set("Content-Type", "application/dart; charset=utf-8")
		http.ServeFile(w, r, "./app.dart")
		return
	}
	if r.URL.Path == "/app.js" {
		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
		http.ServeFile(w, r, "./app.js")
		return
	}

	t, _ := template.ParseFiles("index.html")
	t.Execute(w, r.URL.Path)
}

func postHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" { return }
	if r.URL.Path != "/post" { return }

	decoder := json.NewDecoder(r.Body)
	var d dataFromClient

	// should handle error here
	decoder.Decode(&d)
	fmt.Fprintf(w, "<a href=\"%s\">%s</a>", d.Url, d.Title)
}

func main() {
	http.HandleFunc("/", handler)
	http.HandleFunc("/post", postHandler)
	http.ListenAndServe(":8000", nil)
}

Server side (HTML template for Go):

index.html | repository | view raw
 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
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Dart HTTP POST JSON to Go Server</title>
</head>
<body>
  <div>URL Path: {{.}}</div>
  <button type="button" data-title="my blog" data-url="https://siongui.github.io/">My blog</button>
  <button type="button" data-title="Google" data-url="https://www.google.com/">Google</button>
  <button type="button" data-title="Facebook" data-url="https://www.facebook.com/">Facebook</button>

<script type='text/javascript'>
  var script = document.createElement('script');
  if (navigator.userAgent.indexOf('(Dart)') === -1) {
    // Browser doesn't support Dart
    script.setAttribute("type","text/javascript");
    script.setAttribute("src", "app.js");
  } else {
    script.setAttribute("type","application/dart");
    script.setAttribute("src", "app.dart");
  }
  document.getElementsByTagName("head")[0].appendChild(script);
</script>
</body>
</html>

Client side (Dart):

app.dart | repository | view raw
 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
28
29
30
31
import 'dart:html';
// https://www.dartlang.org/articles/json-web-service/
import 'dart:convert';

void httpPostJSON(Event e) {
  ButtonElement elm = e.target as ButtonElement;

  HttpRequest request = new HttpRequest(); // create a new XHR

  // add an event handler that is called when the request finishes
  request.onReadyStateChange.listen((_) {
    if (request.readyState == HttpRequest.DONE &&
        (request.status == 200 || request.status == 0)) {
      // data saved OK.
      // http://stackoverflow.com/questions/12177970/how-do-i-add-arbitrary-html-to-an-element-in-dart
      querySelector("body").appendHtml(request.responseText);
    }
  });

  // POST the data to the server
  var url = "/post";
  request.open("POST", url, async: false);

  request.send(JSON.encode(elm.dataset)); // perform the async POST
}

void main() {
  querySelectorAll("button").forEach((Element elm) {
    elm.onClick.listen(httpPostJSON);
  });
}

Makefile for automating the development:

Makefile | repository | view raw
 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
28
29
30
31
32
33
34
35
36
# cannot use relative path in GOROOT, otherwise 6g not found. For example,
#   export GOROOT=../go  (=> 6g not found)
# it is also not allowed to use relative path in GOPATH
export GOROOT=$(realpath ../../../../../go)
export GOPATH=$(realpath .)
export PATH := $(GOROOT)/bin:$(PATH)

# path of Dart and utilities
DART_DIR=../../../../../dart
DART_SDK=$(DART_DIR)/dart-sdk
DART_SDK_BIN=$(DART_SDK)/bin
DARTVM=$(DART_SDK_BIN)/dart
DART2JS=$(DART_SDK_BIN)/dart2js
DARTPUB=$(DART_SDK_BIN)/pub
DARTIUM=$(DART_DIR)/chromium/chrome


devserver:
	go run server.go

# Fix Dartium startup error:
# http://askubuntu.com/questions/369310/how-to-fix-missing-libudev-so-0-for-chrome-to-start-again
dartium:
	DART_FLAGS='--checked' $(DARTIUM) --user-data-dir=/tmp/data http://localhost:8000/ &

js: app.dart
	$(DART2JS) --minify --out=app.js app.dart

clean:
	[ -e app.js.deps ] && rm app.js.deps
	[ -e app.js.map  ] && rm app.js.map
	[ -e app.js      ] && rm app.js

help:
	$(DARTVM) --print-flags
	go help

References:

[1]Writing Web Applications - The Go Programming Language
[2]Handling JSON Post Request in Go
[3]Using Dart with JSON Web Services