Google App Engine 簡介

  1. 這是 Google App Engine 及 Web Programming 簡介的簡化版
  2. 登入系上的ubuntu後,打開terminal,下載google app engine (若已經有google_appengine目錄,可以跳過這一個還有下一個步驟)
  3. wget https://storage.googleapis.com/appengine-sdks/featured/google_appengine_1.9.5.zip
  4. 解壓縮: unzip google_appengine_1.9.5.zip
  5. 更改目錄至google_appengine(in terminal): cd google_appengine
  6. Download Sample Code(in terminal): wget http://math.ncku.edu.tw/~mhchen/cs2014/mytest.zip
  7. unzip mytest.zip
  8. 我們先在你現在的機器上測試:
    1. 輸入: ./dev_appserver.py mytest
    2. 打開firefox,輸入網址 http://localhost:8080
    3. 若執行結果和http://ncku-mhchen-test.appspot.com/ 一樣,則繼續接下來的步驟
    4. 在你先前執行./dev_appserver.py mytest 的終端機中按 Ctrl+c,將先前執行的工作中斷
  9. 登入你的GAE帳戶 https://appengine.google.com/ ,建立一個application(或是使用先前的application)
  10. 進入mytest目錄:cd mytest (若你終端機目錄是google_appengine)
  11. 若你是重新打開一個新的終端機,則要先進入google_appengine (cd google_appengine)後再進入mytest
  12. 編輯app.yaml
    1. gedit app.yaml
    2. 將檔案第一行的 ncku-mhchen-hello 改為你建立的application的Application Identifier
  13. 回到上一層目錄(google_appengine) cd ..
  14. 上傳application
  15. ./appcfg.py update ./mytest
  16. 測試 (你的Application Identifier).appspot.com
  17. 接著可以開始將你的battleship程式移植到GAE上執行

底下簡單說明一下test.py的內容:

程式開頭先導入需要使用的模組,另外因為要處理中文,所以程式開頭要加上 #coding=utf-8
#coding=utf-8
#編碼要改成utf8 拿資料有中文才不會錯 

import cgi
import webapp2
import time
from google.appengine.api import users
from random import *
接下來的部分是我們希望GAE顯示的網頁形式,底下是連線至網址時顯示的網頁內容,注意MAIN_PAGE_HTML 和程式中 class MainPage 裡 get(self)函數呼叫指令的對應 self.response.write(MAIN_PAGE_HTML)。 另外注意 name="myname"以及 name="PName",這在之後我們會使用 self.request.get 得到對應的值 請參考HTML Forms and Google App Engine
MAIN_PAGE_HTML = """ <html> 
  <body> 
  <p> 這是一個簡單的GAE程式,首先輸入你的姓名,接著輸入兩個數字,程式會計算並顯示和。
  </p> 
      <form action="/" method="post">       
      <p> Your Name</p> 
      <div>     
      <input type="text" name="myname" />  
      <input type="submit" name="PName" value="Submit"> </div> 
    </form> 
    <a href = "http://math.ncku.edu.tw/~mhchen/computer/mytest.zip"> Source Code</a> 
  </body> 
</html> 
"""
第二個網頁除了顯示結果外,還有輸入的部分,較為複雜,所以把網頁分成三部分,header、程式輸出部分、以及 footer。header的部分主要是一些網頁的設定,還有輸入表單的設定:
LOG_PAGE_HEADER_TEMPLATE = """
<html> 
<head> 
<meta http-equiv="Content-Language" content="zh-tw" /> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title> Name Fight</title> 
<style type="text/css"> 
.style1 {
				text-align: center;
}
.style2 {
				font-size: x-large;
}
</style> 
</head> 
  <body> 
  <h3> Result</h3> 
    <hr> 
<h3> Options</h3> 
  <!form name="input" action="/play" method="post"!> 
  <form name="input" action="/" method="post"> 
  
    <a href="/"> <button type="button"> Replay</button> </a> 
    <h4> A + B =?</h4> 
    <div>     
    A: <input type="text" name="num1" />  
    B: <input type="text" name="num2" />  
    <input type="submit" name="step" value="Sum"> </div> 
    </form> 
    <hr> 
    <pre> 
"""
網頁底部的部分,主要是結尾,沒有特別的地方
LOG_PAGE_FOOTER_TEMPLATE = """\
</pre> 
<hr> 
  </body> 
</html> 
"""
FINAL_PAGE_HEADER_TEMPLATE 和 FINAL_PAGE_FOOTER_TEMPLATE 是LOG_PAGE的簡化版,所以略過。 接著說明主程式MainPage部分: 一開始的 self.response.write(MAIN_PAGE_HTML) 會把前面給的MAIN_PAGE_HTML (字串)顯示出來
class MainPage(webapp2.RequestHandler):

    def get(self):
        self.response.write(MAIN_PAGE_HTML)
接著的 post 函數是主要執行的部分,注意先前的輸入表單部分都有 method="post"。 由於有些變數在大部分的函數中出現,所以我們使用global 宣告,另外你自己定義的函數也建議先定義在開頭的地方
#class Playbook(webapp2.RequestHandler):
    def post(self):
        global player1
        global hp1
        global i
        global showlist
        global board,boardsize,xinx
        global ship_row, ship_col
###########################
# Define your functions here
# 在此定義你會使用到的函數
        
############################
        html_txt= u''
#        hp_msg = "{0}擲出點數{1},目前點數{2}\n"
#        hp_msg_u = u"{0}擲出點數{1},目前點數{2}\n"
#        hp_old_msg_u = u"{0}目前點數{1}\n"
        sum_msg = "A = {0}, B= {1}; A+B = {2}\n"
        hello_msg = "Hello {0}\n"
上面的sum_msg 以及 hello_msg是字串,但是我們使用了格式化的功能,讓我們可以自行填入{0}、{1}、{2}的值 接著我們使用 self.request.get 取得 PName 還有 myname 的值,另外 ".encode('utf-8')"主要是由於網頁設定為utf-8 另外注意 hello_msg.format(myname) 的使用
        
        s_input = self.request.get('PName')
        if s_input != '':
            myname = self.request.get('myname').encode('utf-8')
            html_txt=  hello_msg.format(myname)
            i = i+1
            showlist.append(html_txt)
接著我們使用 self.request.get 取得 step、num1、num2的值,由於這些值是字串,所以如果要做為數值使用需使用int(整數) 或是float(浮點數)轉換
        step = self.request.get('step')
        flag_end = "N"
        if step != '':
            i = i + 1
#            dice = randint(1,6)
            num1 = self.request.get('num1').encode('utf-8')
            num2 = self.request.get('num2').encode('utf-8')
            num_A = int(num1)
            num_B = int(num2)
            html_txt= html_txt + sum_msg.format(num_A,num_B,num_A+num_B)
在取得輸入資料後,我們可以開始處理我們的問題,以battleship遊戲為例,我們可以先判斷是否有擊中敵艦
#  if-elif-else (Determine if you hit the ship
#  

# end of if                                    
            showlist.append(html_txt)
底下的 my_def() 函數的功能是把先前的記錄顯示出來
        def my_def(): #print previous messages
            self.response.write('<hr> <h3> Log</h3> ')
            for j in range(0,i):
                self.response.write('<h4> Round %s </h3> ' %
                            cgi.escape(str(i-j-1)))
                self.response.write('<blockquote> %s</blockquote> ' %
                                cgi.escape(showlist[i-j-1]))
在判斷是否擊中後,可以改變 flag_end 的值來變換顯示網頁,由於顯示網格有點複雜,所以我先把 呼叫 print_board(board) 的部分給註解,先不執行
        if flag_end == "N":
            self.response.write(LOG_PAGE_HEADER_TEMPLATE)        
#            print_board(board)
            self.response.write('<h3> Round %s </h3> ' %
                                cgi.escape(str(i)))
            self.response.write(html_txt)
            my_def()
            self.response.write(LOG_PAGE_FOOTER_TEMPLATE)
        else:
            self.response.write(FINAL_PAGE_HEADER_TEMPLATE)        
#            print_board(board)
            self.response.write('<h3> Round %s </h3> ' %
                                cgi.escape(str(i)))
            self.response.write(html_txt)
            my_def()
            self.response.write(FINAL_PAGE_FOOTER_TEMPLATE)

i = 0
showlist = []
board = []
xinx = []        
最底下底下的部分告訴Server網頁由哪個Handler處理:"/"由class MainPage處理。 另外 app 對應到 app.yaml 裡的script: test.app 的副檔名 app
app = webapp2.WSGIApplication([
    ('/', MainPage),
#    ('/play', Playbook),
], debug=True)

Last Modified 6/6/2014