Learn to build and deploy simple Go Web Apps, Part Four

In part one of this tutorial, we installed the Google App Engine SDK and then built and ran a simple web app locally. In part two, we deployed this simple web app to the Google App Engine and shared it with users worldwide. In part three, we learned about package template (namely text/template and html/template).

Now, we shall learn how to handle forms, build and deploy our Go web app GoView.

Create folders

First, create folders as shown in the diagram below:

c:go_projects
+---go
    +---src
        +---github.com
            +---SatishTalim
                +---goview
                    |   goview.go
                    |   app.yaml
                    |   favicon.ico
                    |   
                    +---images
                    |       gsv.png
                    |       
                    +---stylesheets
                            goview.css

File goview.css

Here are the contents of the file goview.css:

body {
  background-color: #C2A7F2;
  font-family: sans-serif;
}
h1 {
  color: #2A1959;
  border-bottom: 2px solid #2A1959;
}
h2 {
  color: #474B94;
  font-size: 1.2em;
}
h2, p {
  margin-left: 120px;
}

File app.yaml

Here are the contents of the file app.yaml:

application: geocodeweb

version: 1-0

runtime: go

api_version: go1

handlers:
- url: /favicon.ico
  static_files: favicon.ico
  upload: favicon.ico

# All URLs beginning with /stylesheets are treated 
# as paths to static files in the stylesheets/ directory.
- url: /stylesheets
  static_dir: stylesheets

- url: /images
  static_dir: images

- url: /.*
  script: _go_app

In part one of this series, we have already talked about the configuration file app.yaml.

Program goview.go

In the goview folder, create a file named goview.go, open it in your favorite editor, and add the following lines:

package goview

import (
    "encoding/json"
    "fmt"
        "html/template"
    "io/ioutil"
    "net/http"
    "net/url"

    "appengine"
        "appengine/urlfetch"
)

func init() {
        http.HandleFunc("/", handler)
        http.HandleFunc("/showimage", showimage)
}

func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, rootForm)
}

const rootForm = `
  <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>Go View</title>
        <link rel="stylesheet" href="/stylesheets/goview.css">        
      </head>
      <body>
        <h1><img style="margin-left: 120px;" src="images/gsv.png" alt="Go View" />GoView</h1>
        <h2>Accept Address</h2>
        <p>Please enter your address:</p>
        <form style="margin-left: 120px;" action="/showimage" method="post" accept-charset="utf-8">
      <input type="text" name="str" value="Type address..." id="str" />
      <input type="submit" value=".. and see the image!" />
        </form>
      </body>
    </html>
`

var upperTemplate = template.Must(template.New("showimage").Parse(upperTemplateHTML))

func showimage(w http.ResponseWriter, r *http.Request) {
        addr := r.FormValue("str")

        safeAddr := url.QueryEscape(addr)
        fullUrl := fmt.Sprintf("http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=%s", safeAddr)

        c := appengine.NewContext(r)
        client := urlfetch.Client(c)

        resp, err := client.Get(fullUrl)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }

    defer resp.Body.Close()

        // Read the content into a byte array
    body, dataReadErr := ioutil.ReadAll(resp.Body)
    if dataReadErr != nil {
        panic(dataReadErr)
    }

        res := make(map[string][]map[string]map[string]map[string]interface{}, 0)

    json.Unmarshal(body, &res)

    lat, _ := res["results"][0]["geometry"]["location"]["lat"]
    lng, _ := res["results"][0]["geometry"]["location"]["lng"]

    // %.13f is used to convert float64 to a string
    queryUrl := fmt.Sprintf("http://maps.googleapis.com/maps/api/streetview?sensor=false&size=600x300&location=%.13f,%.13f", lat, lng)

        tempErr := upperTemplate.Execute(w, queryUrl)
        if tempErr != nil {
            http.Error(w, tempErr.Error(), http.StatusInternalServerError)
        }
}

const upperTemplateHTML = ` 
<!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8">
      <title>Display Image</title>
      <link rel="stylesheet" href="/stylesheets/goview.css">              
    </head>
    <body>
      <h1><img style="margin-left: 120px;" src="images/gsv.png" alt="Street View" />GoView</h1>
      <h2>Image at your Address</h2>
      <img style="margin-left: 120px;" src="{{html .}}" alt="Image" />
    </body>
  </html>
`

Let us now analyze the above program.

User Form

A user form give us the ability to communicate between clients and servers. We use the form tag <form> to define a form. Go has many convenient functions to deal with a user form making it easy to integrate it in our web applications.

In the program, we have used:

<form style="margin-left: 120px;" action="/showimage" method="post" accept-charset="utf-8">
  <input type="text" name="str" value="Type address..." id="str" />
  <input type="submit" value=".. and see the image!" />
</form>

This form will submit to /showimage on the server. After the user clicks the see the image! button, the data will be sent to the showimage handler on the server.

The statement addr := r.FormValue("str") returns the value for the named component namely str of the query, which in our case is the address entered by the user.

QueryEscape

The QueryEscape function escapes the addr string so that it can be safely placed inside a URL query.

App Engine specific

For our app to work on the Google App. Engine we have modified some of our code. App Engine applications can communicate with other applications or access other resources on the web by fetching URLs. An app can use the URL Fetch service to issue HTTP and HTTPS requests and receive responses. The URL Fetch service uses Google’s network infrastructure for efficiency and scaling purposes.

defer

Callers should close resp.Body when done reading from it. Defer the closing of the body by defer resp.Body.Close()

Unmarshal

We will be using the Unmarshal function to transform our JSON bytes into the appropriate structure. The Unmarshal function accepts a byte array and a reference to the object which shall be filled with the JSON data (this is simplifying, it actually accepts an interface) – json.Unmarshal(body, &amp;res).

Here, we successfully convert an addresses (like “1600 Amphitheatre Parkway, Mountain View, CA”) into its geographic coordinates (like latitude 37.423021 and longitude -122.083739).

Using Google Geocoding and Street View Image APIs

We shall use the following apis:

to build our Go web app GoView that accepts an address as input and displays the street-view image of that address.

Open your web browser window and type the following URL:


http://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false

Based on the parameters from our HTTP request, Google responds with a lot of geographical information about the address “1600 Amphitheatre Parkway, Mountain View, CA” in the JSON format. In Google’s JSON response, you can see how the ‘<lat>’ tag, wrapped in between the ‘<location>’ tags, contains the ‘latitude’ coordinates for our given address.

Note that the base Google url we used to test out the API was:


http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=

A Street View Image request is an HTTP URL of the following form:


http://maps.googleapis.com/maps/api/streetview?parameters

The image is specified using request parameters. As is standard in URLs, all parameters are separated using the ampersand (&) character.

Required parameters

  • size specifies the output size of the image in pixels. Size is specified as {width}x{height} – for example, size=600x400 returns an image 600 pixels wide, and 400 high. Street View images can be returned in any size up to 640 by 640 pixels.
  • location can be either a text string (such as Chagrin Falls, OH) or a lat/lng value (40.457375,-80.009353). The Street View Image API will snap to the panorama photographed closest to this location. Because Street View imagery is periodically refreshed, and photographs may be taken from slightly different positions each time, it’s possible that your location may snap to a different panorama when imagery is updated.
  • sensor indicates whether or not the request came from a device using a location sensor (e.g. a GPS) to determine the location sent in this request. This value must be either true or false.

An example request is shown below.


http://maps.googleapis.com/maps/api/streetview?size=600x300&location=37.4214111,-122.0840372&sensor=false

Here is the output:

Street View Image

Deploy our app GoView

In part two you have already learned how to deploy an app to the Google App Engine. Deploy our app goview.go to the App Engine.

You can check out this app. running on the Google App. Engine here.

Almost all addresses in the USA and Europe will render a photo but I am not too sure about addresses outside of USA/Europe.

Things to do

You can download all the files used in this article from here.

As an exercise, you can modify goview.go to handle errors and also modify/optimize the goview.css file.

You can learn more about writing and deploying web apps with Go from my new book.

I hope you’ve enjoyed this post, please leave any feedback in the comments section.

You can find the other parts of the series at the following links:

About these ads

About indianguru

http://satishtalim.com/
This entry was posted in Tutorials, Go and tagged , . Bookmark the permalink.

6 Responses to Learn to build and deploy simple Go Web Apps, Part Four

  1. Pingback: Learn to build and deploy simple Go Web Apps, Part Three | Josh Software – Where Programming is an Art!

  2. Pingback: Learn to build and deploy simple Go Web Apps, Part Two | Josh Software – Where Programming is an Art!

  3. Pingback: Learn to build and deploy simple Go Web Apps, Part One | Josh Software – Where Programming is an Art!

  4. Pingback: My articles on Go for JoshSoftware

  5. Pingback: A Fun, Weather Forecast Go Web App | Josh Software – Where Programming is an Art!

  6. jzarecta says:

    I am a bit confused about how to implement / request webservices specially across different language. Reading your code I am not sure if there is any webservice being presented, althought I do see a get method and some data being uploaded/served. I am more used to having a protocol (REST, XMLRPC) datatype (encode64, JSON, XML) being exchanged. From my readings on Go it seems this can be handled from the net/http library which is used on your app, but I am still can’t seem to grasp its used as a WS. From my reading (http://blog.smartbear.com/web-development/how-to-build-a-web-service-in-5-minutes-with-go/) it seems they use other frameworks like Martini, Gorilla, GoWeb etc. So I get confused trying to learn a new framework for just one action.
    You can see how this is becoming a incremental task. Thanks or your help.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s