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:
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.
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'As you see you just use the built-in encode() method with the string rot13 and that's it.
>>> s.encode('rot13')
'uryyb'
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.Now that the type structure is ready we can focus on the Handler.
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)
}
This is what my Rot13Handler looks like in python:
class Rot13Handler(webapp2.RequestHandler):And this is how it looks in Go:
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
}
}
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>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.
<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
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 doingr.FormValue("text")and then encode this by doing the ROT13 substitution
r13.Encode()
.
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>The
<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
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{We retrieve the signup information as before done, by calling
Username string
Password string
Email string
Verify string
ErrorUser string
ErrorPassword string
ErrorPasswordMatch string
ErrorEmail string
}
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){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.
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) boolThey work with the regexp package and check the validity in different ways. There is not much difference between the two implementations:
func IsPasswordValid(password string) bool
func IsEmailValid(email string) bool
func IsStringValid(s string) bool
EMAIL_RE = re.compile(r"^[\S]+@[\S]+\.[\S]+$")Here is the Go version:
def valid_email(email):
return EMAIL_RE.match(email)
var EMAIL_RE = regexp.MustCompile(`^[\S]+@[\S]+\.[\S]+$`)In case of a wrong input, we update the signup data with the corresponding errors and execute again the signupTemplate.
func IsEmailValid(email string) bool{
return EMAIL_RE.MatchString(email)
}
/unit2/welcome
page this time with a parameter username as follows:http.Redirect(w,r, "/unit2/welcome?username="+s.Username, http.StatusFound)
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):In Go we do this by getting the information from the Form value method on the HTTP request:
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
}
}
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:
This covers the second CS253 Unit. Next time will be about Unit 3.
Santiaago