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 python repository.

The Go repository.

The code is at the following locations: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 stringand 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)

}

Unit 2: Rot13 Handler

Now that the type structure is ready we can focus on the 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)



// 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

}

}

And this is how it looks in Go:

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))

<!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>



POST

GET

GET

POST

r.FormValue("text")

r13.Encode()

Unit 2: Signup handler

And this is what the constlooks like: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.Thechecks theandmethod. On themethod it will simply display the blank form. On themethod we will get the form information as usual by doingand then encode this by doing the ROT13 substitution

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>



GET

POST

type Signup struct{

Username string

Password string

Email string

Verify string

ErrorUser string

ErrorPassword string

ErrorPasswordMatch string

ErrorEmail string

}

r.FormValue

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)

}

}

}



func IsUsernameValid(username string) bool

func IsPasswordValid(password string) bool

func IsEmailValid(email string) bool

func IsStringValid(s string) bool



EMAIL_RE = re.compile(r"^[\S]+@[\S]+\.[\S]+$")

def valid_email(email):

return EMAIL_RE.match(email)

var EMAIL_RE = regexp.MustCompile(`^[\S]+@[\S]+\.[\S]+$`)

func IsEmailValid(email string) bool{

return EMAIL_RE.MatchString(email)

}

/unit2/welcome

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

Unit 2: Welcome Handler

Themethod is a simple execution of the signup template with no information in it. For themethod I decided to create a Signup type to hold the user's information and the possible errors it might have.We retrieve the signup information as before done, by callingthen we proceed to check the validity of the Form. Here is what the SignupHandler looks like:I put all the validation of inputs in a smallfile with some helper functions. Right now I am adding this to apackages next to other helper functions. Though I might move it somewhere else later on.The functions inare the following:They work with the regexp package and check the validity in different ways. There is not much difference between the two implementations:In Python:Here is the Go version: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 thepage this time with a parameter username as follows:

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>

class WelcomeHandler(webapp2.RequestHandler):

def get(self):

username= self.request.get('username')

self.response.out.write(htmlWelcome%{'username':username})



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 python we would get the user name value with a request.get method:In Go we do this by getting the information from the Form value method on the HTTP request:

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 apackage and I have some files in it, likewhich is used in this unit. Andwhich 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