[Golang] JSONP Example (CORS) by GopherJS


Introduction

It is really cool to run Go code in the browser. GopherJS is a compiler from Go to JavaScript, which makes this possible. In this post, we will show how to make cross-domain requests (CORS) by JSONP (JSON with Padding) technique, which allows data to be retrieved from servers of other domains. This is an example of full-stack Go, which uses Golang to develop web applications in both front-end (runs on browsers) and backend (runs on servers).

Install GopherJS

Run the following command to install GopherJS and GopherJS bindings for the JavaScript DOM APIs:

$ go get -u github.com/gopherjs/gopherjs
$ go get -u honnef.co/go/js/dom

Source Code

First we write a simple HTML for our demo: (index.html)

<!doctype html>
<html>
<head>
  <title>JSONP example of Full-Stack Golang</title>
</head>
<body>
<script src="demo.js"></script>
</body>
</html>

A callback function whose name is mycallback are declared by js.Global.Set method. The mycallback function will receive JSON data from the server. Beside, a script element are inserted to the head element to make cross-domain request.

jsonp.go | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import "github.com/gopherjs/gopherjs/js"
import "honnef.co/go/js/dom"
import "net/url"

func main() {
	js.Global.Set("mycallback", func(jsonData map[string]string) {
		println(jsonData["input"])
	})

	targetUrl := "/jsonp?callback=mycallback" +
		"&input=" + url.QueryEscape("你好")

	d := dom.GetWindow().Document()
	ext := d.CreateElement("script")
	ext.SetAttribute("src", targetUrl)
	head := d.GetElementsByTagName("head")[0].(*dom.HTMLHeadElement)
	head.AppendChild(ext)
}

The server receive the name of the callback function and data from the client. Then encode the data in JSON format, and send the JSON data to the callback function.

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
package main

import "net/http"
import "fmt"
import "encoding/json"

func jsonpHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		return
	}
	if r.URL.Path != "/jsonp" {
		return
	}
	w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
	b, _ := json.Marshal(map[string]string{
		"input": r.URL.Query().Get("input"),
	})
	fmt.Fprintf(w, "%s(%s);", r.URL.Query().Get("callback"), b)
}

func main() {
	http.Handle("/", http.FileServer(http.Dir("./src")))
	http.HandleFunc("/jsonp", jsonpHandler)
	http.ListenAndServe(":8000", nil)
}

Compile the Go code to JavaScript by:

$ gopherjs build jsonp.go -o demo.js

Put demo.js together with the index.html and in the same directory. Modify the path in server.go to the path where you place demo.js and index.html. Run the server by:

$ go run server.go

Open your browser with URL localhost:8000. Open the console of the browser, and you will see the data printed out by the callback function.


Tested on: Ubuntu Linux 15.10, Go 1.5.3, Chromium Version 47.0.2526.106 Ubuntu 15.10 (64-bit).


References:

[1]GopherJS - A compiler from Go to JavaScript (GitHub, GopherJS Playground, godoc)
[2]Bindings · gopherjs/gopherjs Wiki · GitHub
[3]dom - GopherJS bindings for the JavaScript DOM APIs (GitHub)
[4]JSONP on Google App Engine Python
[5]golang encodeuricomponent
[6]escaping - Recommended way to encode/decode URLs - Stack Overflow
[7]golang get url from request
[8]In go's http package, how do I get the query string on a POST request? - Stack Overflow
[9][Webapp] Dart HTTP POST JSON Data to Go Server
[10]golang http write response
[11]Writing Web Applications - The Go Programming Language
[12]http - The Go Programming Language
[13]encoding/json - The Go Programming Language