I am starting to port Udacity course CS253 Web Development from python to Go. This seems to be a good way to get into Go while covering App Engine SDK.
To start with Go and App Engine there are a lot of resources out there. Here are the ones I used the most:
Go App Engine SDK does not have a GoogleAppEngineLauncher (like for python). So you will have to launch and deploy your application via the command line.
To run my application I do as follows: 
~/google_appengine/dev_appserver.py udacity.cs253.go/I put the google_appengine sdk in
~. I had some troubles when I put the Go GAE scripts in my PATH as I also have the python GAE SDK, I think it conflicts. This is the reason why I am explicit when running dev_appserver.py.~/google_appengine/appcfg.py update udacity.cs253.goNote: udacity.cs253.go is the name of my application.
So I started with Unit 1. Here is what the python code of my main.py looks like in python, 3 handlers a simple hello world handler and 2 handlers date and thanks that comunicate through a Form:
import webapp2And here is what my main.go looks like after some work.
import re
from unit1 import *
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.out.write('Hello Udacity!')
app = webapp2.WSGIApplication([('/', MainHandler),
('/unit1/date',DateHandler),
('/unit1/thanks',ThanksHandler)],debug=True)
package mainI am using the context interface to log in the console.
import (
"fmt"
"net/http"
"appengine"
"unit1"
)
func init(){
http.HandleFunc("/", mainHandler)
http.HandleFunc("/unit1/date", unit1.DateHandler)
http.HandleFunc("/unit1/thanks", unit1.ThanksHandler)
}
func mainHandler(w http.ResponseWriter, r *http.Request){
c := appengine.NewContext(r)
c.Infof("Requested URL: %v", r.URL)
fmt.Fprint(w,"hello Udacity with Go!")
}
c.Infof("Requested URL: %v", r.URL)This will display on your console as follows:2013/06/09 19:38:14 INFO: Requested URL: /Also, I decided to do a package for every unit so that I can simply import each package like this:
import (
"fmt"
"net/http"
"appengine"
"unit1"
"unit2"
"unit3"
)
The /unit1/date url has a simple form with month, day and year inputs and a submit button.
In Jinja this is the template that was used for the course:
<form method="post">Go handles templates in the html package, so there is no need to install jinja or any template API, that is a great plus. Here is my resulting template for this part of the course.
What is your birthday?
<br>
<label>Month
<input name="month" value="%(month)s">
</label>
<label>Day
<input name="day" value="%(day)s">
</label>
<label>Year
<input name="year" value="%(year)s">
</label>
<div style="color:red">%(error)s</div>
<br>
<br>
<input type="submit">
</form>
<html>Some notes about this:
<body>
<form method="post">
What is your birthday?
<br>
<label>Month
<input name="month" value="{{.Month}}">
</label>
<label>Day
<input name="day" value="{{.Day}}">
</label>
<label>Year
<input name="year" value="{{.Year}}">
</label>
<div style="color:red">{{.Error}}</div>
<br>
<br>
<input type="submit">
</form>
</body>
</html>
type Date struct{
	Month string
	Day string
	Year string
	Error string
}To render the form I now do the following:func DateHandler(w http.ResponseWriter, r *http.Request){
	if r.Method == "GET" {
		date := Date{
			Month: "",
			Day: "",
			Year: "",
		}
		if err := dateTemplate.Execute(w,date); err != nil{
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	} else if r.Method == "POST"{
                // ...
	}
}
 where dateHTML is the html template defined above and  dateTemplate is defined as follows and uses the template package:var dateTemplate = template.Must(template.New("MyDate").Parse(dateHTML))
To handle Post and Get methods in python you would have a class for each handler and a Post and Get method as follows:
class DateHandler(webapp2.RequestHandler):
def get(self):
# to something
def post(self):
# do something else
func DateHandler(w http.ResponseWriter, r *http.Request){
	c := appengine.NewContext(r)
	c.Infof("cs253: Requested URL: %v", r.URL)
	c.Infof("cs253: Http METHOD: %v",r.Method)
	if r.Method == "GET" {
                // do something
	} else if r.Method == "POST"{
                // do something else
	}else{
                // this is an error
	}
}
On the post method we will extract the form information, this is a very common case.
In python you would get the information as follows:
def post(self):
user_month = self.request.get('month')
user_day = self.request.get('day')
user_year = self.request.get('year')
month = valid_month(user_month)
day = valid_day(user_day)
year = valid_year(user_year)
if not(month and day and year):
self.write_form("That's an error!",user_month,user_day,user_year)
else:
self.redirect('/unit1/thanks')
func DateHandler(w http.ResponseWriter, r *http.Request){
	c := appengine.NewContext(r)
	c.Infof("cs253: Requested URL: %v", r.URL)
	c.Infof("cs253: Http METHOD: %v",r.Method)
	if r.Method == "GET" {
                // the GET method
	} else if r.Method == "POST"{
		d := Date{
			Month: validMonth(r.FormValue("month")),
			Day: validDay(r.FormValue("day")),
			Year: validYear(r.FormValue("year")),
					}
		if d.Day == "" || d.Month == "" || d.Year == ""{
			d.Error = "That's an error!"
			if err := dateTemplate.Execute(w,d); err != nil{
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
		http.Redirect(w,r, "/unit1/thanks", http.StatusFound)
	}
}