歡迎您光臨本站 註冊首頁
  
使用社會網路可以更輕鬆地獲取並聚合數據,從而創建富有革新精神的新 Web 應用程序。但是,仍然必須處理創建可伸縮 Web 應用程序的所有常見問題。現在,使用 Google App Engine (GAE) 也可以簡化工作。使用 GAE,可以不必考慮管理應用伺服器池的所有事務,而是集中精力創建優秀的 mashup。本文是共分三部分的系列文章 “使用 Eclipse 在 Google App Engine 上創建 mashup” 的最後一部分,在本文中,將利用並進一步增強在前兩部分中構建的應用程序。我們將添加查看應用程序的其他用戶及訂閱其聚合提要的功能,然後通過將應用程序公開為可由其他 mashup 使用的 Web 服務完成 mashup 構建。

關於本系列

在本系列中,將了解如何開始使用 Google App Engine (GAE)。在 第 1 部分 中,了解了如何設置開發環境,以便可以開始創建運行在 GAE 上的應用程序。還了解了如何使用 Eclipse 簡化應用程序的開發和調試。在 第 2 部分 中,通過添加一些 Ajax 特性增強了該應用程序。還了解了在部署到 GAE 后如何監視該應用程序。本文是第 3 部分,將通過為應用程序創建 RESTful Web 服務返回到生態系統,這樣其他人就可以使用它創建自己的 mashup。

GAE 是創建 Web 應用程序的平台。使用它的最重要的先決條件是具備 Python 知識,因為要在 GAE 中使用 Python 作為編程語言(目前為 Python V2.5.2)。對於本系列,具備一些典型的 Web 開發技能將會有幫助(例如,HTML、JavaScript 和 CSS 知識)。要針對 App Engine 進行開發,需要下載 App Engine SDK(請參閱 參考資料)。在本系列中,還使用 Eclipse V3.3.2 以輔助 GAE 開發(請參閱 參考資料)。並且需要 PyDev 插件以將 Eclipse 轉換為 Python IDE。





訂閱

到目前為止,我們的應用程序 aggroGator 允許用戶聚合(mash up)多項常見 Web 服務並創建這些服務的聚合提要。現在,為了讓事情變得有趣一些,我們希望允許用戶訂閱其他用戶的提要(其中每個用戶的提要可能是提要本身的聚合)。例如,假定需要設置一個帳戶以在 Twitter、last.fm 和 del.icio.us 中訂閱自己的提要,這樣其他朋友隨後可以訂閱 aggroGator 提要以查看這些服務中的所有活動。要處理這種情況,需要再一次重新審視數據模型。

建模

要啟用訂閱,需要允許一個用戶(帳戶)訂閱其他帳戶列表。我們可以採取的一種方法是向每個帳戶中添加用戶列表。每個用戶將添加一個訂閱。此操作的代碼將類似清單 1。


清單 1. 帶有 user 列表的 Account 模型
class Account(db.Model):      user = db.UserProperty(required=True)      subscriptions = db.ListProperty(Account)  

這種方法有一些優點。檢索帳戶時,可以獲得該帳戶訂閱的所有其他帳戶。這是使用諸如 GAE 的 Bigtable 之類的非關係資料庫的常見策略:把所有相關數據保存在一起並且無需擔心標準化之類的事務。但是,這種方法有一個缺點。如果需要顯示特定用戶訂閱了哪些人的提要該怎麼辦?這樣做的惟一方法是檢索所有 Account 模型,查看所有訂閱,並查看給定用戶是否位於列表中。另外,可以在每個 Account 模型中保存兩張列表 — 一張用於 subscriptions,一張用於 subscribers。我們不會採取這種方法,而是使用更傳統的多對多模型,如清單 2 所示。


清單 2. Subscribe 模型
class Subscribe(db.Model):      subscriber = db.ReferenceProperty(Account, required=True,   collection_name='subscriptions')      subscribee = db.ReferenceProperty(Account, required=True,   collection_name='subscribers')  

正如您所見,這個模型類似於關係資料庫中的連接表(join table)。只是因為 GAE 使用非關係資料庫(Bigtable)並不意味著您不能利用與關係資料庫結合使用的技術。現在數據模型已經就緒,讓我們詳細查看如何從最終用戶的角度創建這些多對多關係。

訂閱管理

我們的應用程序可以存儲訂閱,因此只需要一種方法能夠讓用戶創建訂閱。為此,需要為用戶創建一個用於添加訂閱的頁面(參見清單 3)。


清單 3. 帳戶頁面
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/      xhtml1-strict.dtd">  <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">    <head>      <meta http-equiv="content-type" content="text/html; charset=utf-8" />      <title>Aggrogator Accounts</title>      <link rel="stylesheet" href="/css/aggrogator.css" type="text/css" />      <script type="text/javascript" src="/js/prototype.js"></script>      <script type="text/javascript" src="/js/builder.js"></script>      <script type="text/javascript" src="/js/effects.js"></script>      <script type="text/javascript" src="/js/aggrogator.js"></script>    </head>    <body>        <img id="spinner" alt="spinner" src="/gfx/spinner.gif" style="display: none;   position: fixed;" />        <div id="logout">        {{ account.user.nickname }}        <a href="{{ logout_url }}">Logout</a>      </div>        <div class="clearboth"></div>        <ol>        {% for acc in all_accounts %}          <li>            <a href="" onclick="subscribe('{{ acc.user.email }}'); return false;">  {{ acc.user.email }}</a>          </li>        {% endfor %}      </ol>      </body>  </html>  

正如您所見,此頁面只顯示系統中的所有帳戶。然後用戶通過單擊選擇要訂閱的帳戶。您可以設想更複雜的界面。例如,如果存在大量用戶,這樣的界面將變得笨拙,因此基於搜索的系統將更好。或者使用一個系統,允許用戶導入地址本或使用 OpenSocial 等應用程序中的 API 查找已經包含在應用程序中的現有朋友。以上模板需要一張用戶列表,因此讓我們快速查看為頁面創建模型的控制器(參見清單 4)。


清單 4. 帳戶頁面控制器
#Accounts Module  class MainPage(webapp.RequestHandler):      def get(self):          # get the current user          user = users.get_current_user()            # is user an admin?          admin = users.is_current_user_admin();            # create user account if haven't already          account = aggrogator.DB.getAccount(user)          if account is None:              account = aggrogator.Account(user=user)              account.put()            # create logout url          logout_url = users.create_logout_url(self.request.uri)            all_accounts = aggrogator.Account.all()            template_values = {              'account': account,              'admin': admin,              'logout_url': logout_url,              'all_accounts': all_accounts,              }          path = os.path.join(os.path.dirname(__file__), 'accounts.html')          self.response.out.write(template.render(path, template_values))  

該控制器將獲取準備顯示在帳戶頁面中的所有數據。返回到清單 3 中的帳戶頁面,我們將看到在單擊帳戶時調用了 JavaScript。


清單 5. 訂閱 JavaScript
function subscribe(email) {    new Ajax.Request("/accounts/subscribe", {      method: "post",      parameters: {'email': email},      onSuccess: alert('subscribed to ' + email)    });  }  

此 JavaScript 將再次使用 Prototype 庫向伺服器發出 Ajax 請求。調用 URL /accounts/subscribe。要把該 URL 映射到哪裡?創建映射的代碼位於新帳戶模塊的 main 函數中,如下所示。


清單 6. 帳戶模塊的 URL 映射
def main():      app = webapp.WSGIApplication([          ('/accounts/', MainPage),          ('/accounts/subscribe', Subscribe),          ], debug=True)      util.run_wsgi_app(app)    if __name__ == '__main__':      main()  

正如您在 main 函數中看到的那樣,對 /accounts/subscribe 的調用是由 Subscribe 控制器類處理的。清單 7 中顯示了該類。


清單 7. Subscribe 控制器類
class Subscribe(webapp.RequestHandler):      def post(self):          # get the current user          user = users.get_current_user()          email = self.request.get('email')            aggrogator.DB.create_subscription(user, email)  

該控制器十分簡單。它將獲得當前用戶(訂閱者)和正在添加的訂閱的電子郵件地址。然後將在以前使用的 DB 實用程序類中調用新方法。該類將處理所有與 Bigtable 相關的調用。下面顯示了新 create_subscription 函數。


清單 8. create_subscription 的 DB 函數
class DB:      @staticmethod      def create_subscription(user, email):          subscriber = DB.getAccount(user)          subscribee = DB.getAccountForEmail(email)          subscription = Subscribe.gql("WHERE subscriber = :1 AND subscribee = :2",   subscriber, subscribee).get()          if subscription is None:              Subscribe(subscriber=subscriber, subscribee=subscribee).put()        @classmethod      def getAccountForEmail(cls, email):          user = users.User(email)          return cls.getAccount(user)  

該函數首先查找 Account 模型以查找用戶和訂閱電子郵件。對於後者,它將使用新 getAccountForEmail 函數。這將使用 GAE 的用戶的 API 根據電子郵件查找 User 對象,然後查詢 Bigtable 查找帳戶。找到兩個帳戶后,將查看訂閱是否已經存在。如果不存在,則創建一個新訂閱。

當然,有了訂閱之後,需要在主應用程序中使用這些訂閱。我們並不是僅顯示當前用戶的服務,而是需要顯示聚合提要(還有用戶的服務及來自其訂閱的服務)。為此,需要對前幾篇文章中開發的主模塊的 GetUserServices 控制器進行微小更改,如下所示:


清單 9. 修改後的 GetUserServices 控制器
class GetUserServices(webapp.RequestHandler):      def get(self):          user = users.get_current_user()            # get the user's services from the cache          #userServices = aggrogator.Cache.getUserServices(user)          userServices = aggrogator.Aggrogator.get_services(user.email())            stats = memcache.get_stats()          self.response.headers['content-type']  = 'application/json'          self.response.out.write(simplejson.dumps({'stats': stats, 'userServices':   userServices}))  

在這段代碼中,我們只調用了一個新庫類:相應命名的 aggrogator 類,獲得聚合服務而不僅僅是用戶的服務。該庫的代碼如下所示:


清單 10. aggrogator 庫:檢索聚合服務
class aggrogator:      @staticmethod      def get_services(username):          accounts = []                    primary = DB.getAccountForEmail(username)          accounts.append(primary)            for subscription in primary.subscriptions:              accounts.append(subscription.subscribee)          services = []          for account in accounts:              services.extend(Cache.getUserServices(account.user))             return services     

在這裡,可以再次看到新 Subscribe 模型如何工作。在代碼中,獲得用戶名的帳戶(通過使用以前看到的 getAccountForEmail 函數),然後調用其訂閱屬性。在本例中,只使用此函數從緩存中獲得所有服務。稍後,我們將看到這些用於創建聚合提要的服務。

現在幾乎已經準備好測試新帳戶頁面。必須作出最後一項更改:需要將應用程序配置為向新帳戶模塊發送某些 URL 請求。為此,需要編輯 app.yaml 文件並添加一個新部分。


清單 11. 添加到 app.yaml 中的內容
- url: /accounts/.*    script: accounts.py    login: required  

這只是文件的新部分。它將把帶有 /accounts/ 的所有請求映射到帳戶模塊中。這部分代碼應當會顯示在先前使用的 catch-all 處理程序(url: /.*)之前,以便獲得優先權。現在可以像以前一樣使用 Eclipse 和 PyDev 並轉到 http://localhost:8080/acounts/ 測試該應用程序。一定要創建多個帳戶,這樣測試會十分有趣。





aggroGator Web 服務

使用社會 Web 服務可以非常輕鬆地創建諸如 aggroGator 之類的有趣應用程序。GAE 允許我們創建此類同樣具有很強可伸縮性的 mashup。因此,圍繞 mashup 創建 API/Web 服務,以便其他人可以使用它創建自己的有趣 mashup 將十分有意義。這實現起來也非常簡單。

對於我們的 Web 服務,首先把它製作成只讀服務。該服務只為用戶提供聚合提要(即在 aggroGator UI 中看到的相同內容)。使用簡單的 REST 樣式的 URL,例如 /[email protected]。這一次,將由下至上開始。要處理這樣的 URL,需要再次向 app.yaml 文件中添加一個部分。


清單 12. 添加到 app.yaml 中的內容
- url: /api    script: main.py  

注意,仍然把 /api 請求發送給主模塊。app.yaml 中為什麼需要一個新映射?我們不需要對 aggroGator API 進行驗證。這是在 app.yaml 中需要新規則的惟一原因。由於利用主模塊,因此需要對它進行修改。


清單 13. 主模塊的新規則
def main():      app = webapp.WSGIApplication([          ('/', MainPage),          ('/addService', AddService),          ('/getEntries', GetEntries),          ('/api', AggroWebService),          ('/getUserServices', GetUserServices),          ], debug=True)      util.run_wsgi_app(app)    if __name__ == '__main__':      main()  

對該函數所做的全部操作是向映射列表中添加一個條目。將把 /api 映射到 AggroWebService 控制器類中。該類如下所示:


清單 14. AggroWebService 控制器類
class AggroWebService(webapp.RequestHandler):      def get(self):          self.response.headers['content-type'] = 'text/xml'          username = self.request.get('username')          entries = aggrogator.Aggrogator.get_feed(username)          str = u"""<?xml version="1.0" encoding="utf-8"?><entries>"""          for entry in entries:              str += entry.to_xml()          str += "</entries>"          self.response.out.write(str)  

該服務首先檢索 username 請求參數,然後使用以前看過的 aggroGator 庫,但是使用不同的方法 get_feed 以獲得聚合條目。該庫函數的代碼如下所示:


清單 15. aggroGator get_feed
class Aggrogator:      @staticmethod      def get_feed(username):          services = Aggrogator.get_services(username)          entries = []          for svc_tuple in set((svc['service'], svc['username']) for svc in services):              entries.extend(Cache.getEntries(*svc_tuple))          entries.sort(key=operator.attrgetter('timestamp'), reverse=True)          return entries  		

該 library 函數將使用在清單 10 中看到的 get_services 函數檢索聚合服務,然後遍歷服務。代碼將使用一個集合以確保服務是惟一的(即如果用戶已經訂閱了都使用同一項服務的另外兩個用戶)。由於使用了集合,因此必須使用元組(tuple),因為只能使用不可修改的對象。最後,按時間戳降序排列所有條目(首先列出最新的條目)。

返回到清單 14,在擁有條目列表之後,使用某個簡單的字元串連接來創建 XML 文檔。對每個 Entry 實例使用 to_xml() 方法。這是一個新方法,如下所示:


清單 16. Entry 類
class Entry:      def __init__(self, service=None, username=None, title=None,   link=None, content=None, timestamp=None):          self.service = service          self.username = username          self.title = title          self.link = link          self.content = content          self.timestamp = timestamp      def to_dict(self):          return self.__dict__      def to_xml(self):          str = """<entry>                      <service>%s</service>                      <username>%s</username>                      <title>%s</title>                      <link>%s</link>                      <content><![CDATA[%s]]></content>                      <timestamp>%s</timestamp>                  </entry>"""          return str % (self.service, self.username, self.title, self.link, self.content,   self.timestamp)   

正如您所見,to_xml() 方法僅使用字元串模板和字元串替換來創建 XML 節點。返回到清單 14,在將 XML 文檔創建為字元串后,設置內容類型的響應頭部並將 XML 字元串發送回給請求者。這是我們需要做的全部操作,我們創建了一個可以供其他 mashup 使用的 Web 服務。





結束語

關於 Google App Engine 的 “使用 Eclipse 在 Google App Engine 上創建 mashup” 系列的第 3 部分到此結束。在本文中,添加了訂閱和用於創建訂閱的 UI。修改了使用訂閱的現有應用程序,並且創建了 REST 樣式的 Web 服務以允許其他 mashup 從 aggroGator 進行構建。在此之後我們還可以完成更多事務。可以把註釋添加到 Entry 類中,添加允許用戶向條目添加註釋的 UI。可以提供訂閱視圖和個人視圖。可以擴展 Web 服務,以便允許用戶直接添加到提要中。由於使用了 Google App Engine,並且結合使用了 Eclipse 和 PyDev 等工具,完成所有這些工作變得更加容易。(責任編輯:A6)



[火星人 ] 使用 Eclipse 在 Google App Engine 上創建 mashup,第 3 部分: 使用 RESTful Web 服務已經有477次圍觀

http://coctec.com/docs/linux/show-post-69113.html