On this post I continue to port Udacity course CS253 Web Development from python to Go. This time I am working on Unit 2. This is a small and simple unit, just to get warmed up for the next one.

You can check out my previous blog post if you haven't done it yet:

The code is at the following locations:

Note: Feel free to comment and share your thoughts, I will be more than happy to update and improve my code and learn to better develop in Go.

This unit has two parts. The first part is the Rot13 Handler which substitutes a string back and forth using the ROT13 cipher. The second part is a signup and welcome workflow.


Unit2: implementing Rot13


Some of you might already be familiar with Rot13, it is a substitution cipher. The Go Tour #60 presents it as well.

In python the ROT13 cipher is built-in the library. This is how it looks in a Python shell:

>>> s = 'hello'
>>> s.encode('rot13')
'uryyb'
As you see you just use the built-in encode() method with the string rot13 and that's it.
In Go I did not find anything like this and as I already did the Go Tour exercises I decided to reuse that.

I made some minor changes to the implementation so that the cipher method would return directly a string. This is what it looks like.

// Rot13 is the type used to hold the string to encode.
type Rot13 string

// rot13 returns the rot13 substitution of single byte.
func rot13(b byte) byte{
var first, second byte
switch{
case 'a' <= b && b <= 'z':
first, second = 'a', 'z'
case 'A' <= b && b <= 'Z':
first, second = 'A', 'Z'
default:
return b
}
return (b - first + 13)%(second - first + 1) + first
}

// Rot13 implement Encode function to perform ROT13 substitution.
func (r Rot13) Encode() string{
n := len(r)
t:= []byte(r)
for i := 0; i < n; i++{
t[i] = rot13(t[i])
}
return string(t)
}
Now that the type structure is ready we can focus on the Handler.


Unit 2: Rot13 Handler


This is what my Rot13Handler looks like in python:

class Rot13Handler(webapp2.RequestHandler):

def write_form(self,rot13=""):
self.response.out.write(htmlRot13%{"rot13":escape_html(rot13)})

def get(self):
self.write_form()

def post(self):
user_input = self.request.get('text')
input_changed = user_input.encode('rot13')
self.write_form(input_changed)
And this is how it looks in Go:

// Rot13Handler is the HTTP handler for encoding and decoding a string.
// (rot13(rot13(x)) = x )
func Rot13Handler(w http.ResponseWriter, r *http.Request){
c := appengine.NewContext(r)
if r.Method == "GET" {
writeFormRot13(w, "")
} else if r.Method == "POST"{
var r13 Rot13 = Rot13(r.FormValue("text"))
writeFormRot13(w, r13.Encode())
}else{
tools.Error404(w)
return
}
}

As in unit1 I define an internal const string and a template to work on this unit.

var rot13Template = template.Must(template.New("Rot13").Parse(rot13HTML))
And this is what the const rot13HTML looks like:

<!DOCTYPE html><html>
<head><title>Unit 2 Rot 13</title></head>
<body>
<h2>Enter some text to ROT13:</h2>
<form method="post">
<textarea name="text" style="height: 100px; width: 400px;">{{.Str}}</textarea><br>
<input type="submit">
</form>
</body>
</html>
Since this is a fairly simple example we can do this internally. For next units I am going to externalize the html into separate templates.
The Rot13Handler checks the POST and GET method. On the GET method it will simply display the blank form. On the POST method we will get the form information as usual by doing
r.FormValue("text")
and then encode this by doing the ROT13 substitution r13.Encode().


Unit 2: Signup handler


The signup url has a simple signup form. When the form is submitted we verify its content and redirect on success and display the errors otherwise.

<!DOCTYPE html><html>
<head>
<title>Sign Up</title>
<style type="text/css">.label {text-align: right}.error {color: red}</style>
</head>
<body>
<h2>Signup</h2>
<form method="post">
<table>
<tr>
<td class="label">Username</td>
<td><input type="text" name="username" value="{{.Username}}"></td>
<td class="error">{{.ErrorUser}}</td>
</tr>
<tr>
<td class="label">Password</td>
<td><input type="password" name="password" value="{{.Password}}"></td>
<td class="error">{{.ErrorPassword}}</td>
</tr>
<tr>
<td class="label">Verify Password</td>
<td><input type="password" name="verify" value="{{.Verify}}"></td>
<td class="error">{{.ErrorPasswordMatch}}</td>
</tr>
<tr>
<td class="label">Email (optional)</td>
<td><input type="text" name="email" value="{{.Email}}"></td>
<td class="error">{{.ErrorEmail}}</td>
</tr>
</table>
<input type="submit">
</form>
</body>
</html>
The GET method is a simple execution of the signup template with no information in it. For the POST method I decided to create a Signup type to hold the user's information and the possible errors it might have.

type Signup struct{
Username string
Password string
Email string
Verify string
ErrorUser string
ErrorPassword string
ErrorPasswordMatch string
ErrorEmail string
}
We retrieve the signup information as before done, by calling r.FormValue then we proceed to check the validity of the Form. Here is what the SignupHandler looks like:

func SignupHandler(w http.ResponseWriter, r *http.Request){
c := appengine.NewContext(r)
if r.Method == "GET" {
s := Signup{}
writeFormSignup(w, s)
} else if r.Method == "POST"{
s := Signup{
Username: r.FormValue("username"),
Password: r.FormValue("password"),
Email: r.FormValue("email"),
Verify: r.FormValue("verify"),
ErrorUser: "",
ErrorPassword: "",
ErrorPasswordMatch: "",
ErrorEmail: "",
}
// verify signup info.
if !(tools.IsUsernameValid(s.Username) &&
tools.IsPasswordValid(s.Password) &&
s.Password == s.Verify) ||
(len(s.Email) > 0 && !tools.IsEmailValid(s.Email)){

if ! tools.IsUsernameValid(s.Username){
s.ErrorUser = "That's not a valid user name."
}
// more code for each input.
// ...
s.Password = ""
s.Verify = ""
writeFormSignup(w s)
}
}else{
http.Redirect(w,r, "/unit2/welcome?username="+s.Username, http.StatusFound)
}
}
}
I put all the validation of inputs in a small valid.go file with some helper functions. Right now I am adding this to a Tools packages next to other helper functions. Though I might move it somewhere else later on.
The functions in valid.go are the following:

func IsUsernameValid(username string) bool
func IsPasswordValid(password string) bool
func IsEmailValid(email string) bool
func IsStringValid(s string) bool
They work with the regexp package and check the validity in different ways. There is not much difference between the two implementations:
In Python:

EMAIL_RE = re.compile(r"^[\S]+@[\S]+\.[\S]+$")
def valid_email(email):
return EMAIL_RE.match(email)
Here is the Go version:

var EMAIL_RE = regexp.MustCompile(`^[\S]+@[\S]+\.[\S]+$`)
func IsEmailValid(email string) bool{
return EMAIL_RE.MatchString(email)
}
In case of a wrong input, we update the signup data with the corresponding errors and execute again the signupTemplate.
In case of a correct input we will redirect to the /unit2/welcome page this time with a parameter username as follows:

http.Redirect(w,r, "/unit2/welcome?username="+s.Username, http.StatusFound)


Unit 2: Welcome Handler


Once we redirect to the welcome handler we need to retrieve the information from the URL and display in. Here is the HTML of the welcome handler:

<!DOCTYPE html><html>
<head>
<title>Unit 2 Signup</title>
</head>
<body>
<h2>Welcome, {{.}}!</h2>
</body>
</html>

In python we would get the user name value with a request.get method:

class WelcomeHandler(webapp2.RequestHandler):
def get(self):
username= self.request.get('username')
self.response.out.write(htmlWelcome%{'username':username})
In Go we do this by getting the information from the Form value method on the HTTP request:

func WelcomeHandler(w http.ResponseWriter, r *http.Request){
c := appengine.NewContext(r)
if r.Method == "GET" {
username := r.FormValue("username")
if err := welcomeTemplate.Execute(w,username); err != nil{
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}else{
tools.Error404(w)
return
}
}


Organizing packages:


In the python version, I had a unitx.py for each unit I had. In Go I have changed this by putting a Package for each unit. So right now I have 2 directories. unit1 and unit2. What is interesting in Go is that I can have multiple .go files and still have all of them correspond to the same package.
For example, in the unit2 package I have 3 files:

  • rot13.go
  • signup.go
  • welcome.go
This separates the logic of each handler without the pain of importing each file separately. I think this is really nice.
This is also very nice when you are doing small helper functions. I did a tools package and I have some files in it, like valid.go which is used in this unit. And server.go which handles 404 errors. It feels really nice to decouple each helper function into separate files and still have them all belong to the same package.

This covers the second CS253 Unit. Next time will be about Unit 3.

Santiaago


comments powered by Disqus