Amazon

ラベル モバイル の投稿を表示しています。 すべての投稿を表示
ラベル モバイル の投稿を表示しています。 すべての投稿を表示

2011年8月14日日曜日

mixiモバイルアプリ開発(1)

mixiモバイルアプリ開発 - 一般論 -
いつものごとくGoogle App Engine (Python)で実装しているのだけど、
mixiのモバイル(携帯電話)アプリの場合、Firebugが使えないためデバッグ環境が乏しい。
FirefoxのextensionのFireMobileSimulatorを入れて、
携帯の機種(User-Agent)を切り替えて行う必要がある。

バグと戯れる(1)
さて、簡単にモバイルアプリつくってみたのだけど
モバイルアプリ内でのページの遷移方法はmixi developer centerでは以下のように書いてある

GETの場合

<a href="?url=http%3A%2F%2Fexample.com%2Ffoo%2Fbar%3Fa%3D1%26b%3D2">次のページ</a>

POSTの場合

<form action="?guid=ON&url=http%3A%2F%2Fexample.com%2Ffoo%2F" method="post">
  <input name="field1" type="text" value="value1" />
  <input type="submit" value="送信" />
</form>

要はリンク先はhrefにしろactionにしろ"?url=[エスケープ済みURL]"で記述しろとのこと。
そんなわけで早速エスケープ済みURLでGETリクエストを記述してみた
<a href="?url=http%3A%2F%2mytest.appspot.com%2Fhoge">次のページ</a>
ところが、
"只今アプリケーションサーバーでエラーが発生しています。しばらくしてから再度アクセスしてください。"
というエラーに遭遇。

デバッグしようにもmixi側ではこのメッセージのみで他は何も表示されないし、
Google App Engine側のログを見る当たりリクエストすら飛んで行ってない。
現象としてエラーが表示されている画面のURLに見慣れない"s=xxx"のクエリ文字列(93桁)がついている。
"http://ma.test.mixi.net/xxxx/?url=http%3A%2F%252mytest.appspot.com%2Fhoge&s=ZWJkZTEyMDNlNzY2MDZhN2Q5NmVmNWY2MDk....以下省略"

mixi側で勝手に変な文字列でもつけるのか?と思っていたのだけど、
どうも正しくないURLへアクセスした際に付いてくるデバックコードみたいなものっぽい。

上記のURLと自分で記述したaタグの中身をよーく見てみると一部書き変わっていることに気づく。

href="?url=http%3A%2F%2mytest.appspot.com%2Fhoge"
http://ma.test.mixi.net/xxxx/?url=http%3A%2F%252mytest.appspot.com%2Fhoge

ん?"52"ってなんだ?
あ。。。
hrefの記述の際に"/"のエスケープ文字列は"%2"ではなく"%2F"なのを書き間違えたためにエラーが発生。
おいおい、気づけよ自分(--;


バグと戯れる(2)
またまたエラー発生。
今度のエラーは
"只今アプリケーションサーバーでエラーが発生しています。しばらくしてから再度アクセスしてください。(body)"


(1)と似たエラーだが、今度は"(body)"がついてきた。

結論から言うと
Python(サーバーサイド)からHTMLのbodyタグの内容が変な場合に起こる。



*main.py*
class TestHandler(webapp.RequestHandler):  
def get(self):
        self.response.headers['Content-Type'] = 'application/xhtml+xml; charset=Shift_JIS'
        self.response.out.write('hoge')

application = webapp.WSGIApplication([
        ('/hello',TestHandler)], debug=True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()

3行目が原因なんだけど、乱暴な結果の返し方をすると怒られる。
通常はテンプレート(ここではtmpl/hello.html)を使って
*main.py*
class TestHandler(webapp.RequestHandler): 
def get(self):         self.response.headers['Content-Type'] = 'application/xhtml+xml; charset=Shift_JIS'         template_values = {             'user_info': 'hoge',             'page_num':page,         }         path = os.path.join(os.path.dirname(__file__), './tmpl/hello.html')         self.response.out.write(template.render(path, template_values))
application = webapp.WSGIApplication([         ('/hello',TestHandler)], debug=True) def main():     run_wsgi_app(application) if __name__ == '__main__':     main()

*tmpl/hello.html*
<?xml version="1.0" encoding="Shift_JIS"?>  <!DOCTYPE html PUBLIC "-//i-mode group (ja)//DTD XHTML i-XHTML(Locale/Ver.=ja/1.0) 1.0//EN" "i-xhtml_4ja_10.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" dir="ltr">     <head>         <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=Shift_JIS" />         <title>Test</title>     </head>     <body>         <div style="text-align:center">             {% if user_info %}             User Info: {{ user_info }}             {% else %}
            No user info.             {% endif %}             page_num:{{ page_num}} 
        </div>     </body> </html>

みたいに書く。


とりあえず困ったらまずはmixiのモバイル開発用コミュニティRemixLabsで確認
1)RemixLabsのコミュニティにアクセス
パートナーアカウント、またはパートナーテストアカウントでmixiにログインし、「RemixLabs」コミュニティにアクセスします。

2)RemixLabsコミュニティに参加
コミュニティTOPページにある「このコミュニティに参加」ボタンをクリックし、コミュニティに参加します。

2011年8月7日日曜日

Google App Engineでケータイサイトを作る際のポイント


参考:Google App Engineで、携帯サイトをつくる


○シフトJISのページを表示するには?

  1.  シフトJISで書いたテンプレートを使います(サンプルではtemplate_mobile.html)
  2.  self.response.headersを使って、ヘッダーにシフトJISである旨、情報を追加します
  3.  カスタムフィルター(サンプルではcustomfilters_m.py)を使って、テンプレートに差し込む文字列をユニコードからシフトJISに変換します

○シフトJISのページからPOSTされたデータを受け取るには?
  1.     POSTされたデータを受け取るためにself.request.get()を使っても、その結果はすでに文字化けしています
  2.     self.request.get()を使うまえに、self.request.charsetを定義します

○"~","-", "‐", "||"などの変換
上記の方法で body.encode('Shift-JIS')で変換しても以下のエラーが出る場合がある
UnicodeEncodeError: 'shift_jis' codec can't encode character u'\uff5e'
どうやらMSとUnicodeとShift-JISの間での変換マッピングにズレがあるようで、これを直す必要がある。

[参考]Pythonで「~」をエンコードすると例外が発生


サンプルコード
mobile.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import wsgiref.handlers, os
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template

webapp.template.register_template_library('customfilters_m')

class MainPage(webapp.RequestHandler):
    def get(self):
        message = u"ようこそ、テストのサイトへ"
        template_values = {
            'message': message,
        }
        path = os.path.join(os.path.dirname(__file__), "template_mobile.html")
        self.response.headers['Content-Type'] = "text/html; charset=Shift_JIS"
        self.response.out.write(template.render(path, template_values))
    def post(self):
        self.request.charset = "Shift_JIS"
        message = self.request.get("new_message")        
        template_values = {
            'message': message,
        }
        path = os.path.join(os.path.dirname(__file__), "template_mobile.html")
        self.response.headers['Content-Type'] = "text/html; charset=Shift_JIS"
        self.response.out.write(template.render(path, template_values))

def main():
    application = webapp.WSGIApplication(
            [('/m/', MainPage)],
            debug=True)
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
    main()
customfilters_m.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import datetime
from google.appengine.ext import webapp

register = webapp.template.create_template_register()

def sjis(body):
body = string.replace(body, u'\uff5e', u'\u301c') # fullwidth tilde "~"
    body = string.replace(body, u'\u2015', u'\u2014') # Horizontal bar "―"
    body = string.replace(body, u'\u2225', u'\u2016') # Parallel to "||"
    body = string.replace(body, u'\uff0d', u'\u2212') # Fullwidth hyphen-minus "-"
    return body.encode('Shift_JIS')

register.filter(sjis)
template_mobile.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=SHIFT_JIS" />
<title>テスト</title>
</head>
<body>
{{ message|sjis }}
<form method="post" action="#">
<input type="text" name="new_message" /><br>
<input type="submit" value="送信"  />
</form>
</body>
</html>


○モデルに日本時刻で記録するには?
Google App Engineで以下のようにモデルをつくっておくと、データが挿入されたときに、自動的にその時の時刻が記録されていくので便利なのだが、この時刻はUTCであって日本時間ではない。UTCで記録していくのはよいとして、表示するときは、どうにかして9時間プラスしておかないとまずい。
from google.appengine.ext import db
class hoge(db.Model):
    timestamp = db.DateTimeProperty(auto_now_add=True)
解決方法

テンプレートに、与えられた時刻に9時間を足すカスタムフィルターを登録する。

(1) 以下のコードをcustomfilters.pyというファイル名で保存する。
import datetime
from google.appengine.ext import webapp
register = webapp.template.create_template_register()
def jshift(body):
    return body + datetime.timedelta(hours=9)
register.filter(jshift)
(2) メインとなるコードのほうに以下の1文を追加する。モジュールのインポートが終わったあたりがよい。
webapp.template.register_template_library('customfilters')
(3) テンプレートでは、以下のように記述する。
<p>{{ item.timestamp|jshift|date:"Y-m-d H:i" }}</p>

2011年7月31日日曜日

フィーチャーフォンのアプリを作る際の注意点





Perlで作るモバイルサイトのコツ  より

・文字コード

携帯用サイトの構築を行う場合にはWebアプリケーション内部ではUTF-8を使用し、出力の際にはShift_JISを使用できるようにUTF-8とShift_JISを相互変換する処理を実装する必要があります。

・キャリアに応じたXHTMLの出力

キャリアの仕様


・セッション管理

セッション管理は、ユーザーからのリクエストごとに一意のID(セッションID)を発行し、常にそのIDをリクエストに含ませることで行います。 PCであればクッキーを使用してセッションIDをブラウザに保持しておくことができますが、前述したとおり携帯ではクッキーを使用できる端末が限られるた め、画面遷移時にセッションIDを直接リクエストのパラメータとして渡す必要があります。

 パラメータを引き継ぐ必要のある遷移のパターンは以下の3つです。

  1. リンクによる遷移
  2. フォームによる遷移
  3. リダイレクトによる遷移

 

○PythonでUser Agentの値を取得する

http://d.hatena.ne.jp/perezvon/20080919/1221844404

logging.info(dir(self.request))でuser_agentというプロパティ

# it works!
self.request.user_agentを使う
 
○User Agentの値を表示するPythonサンプル 
 
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

class Receiver(webapp.RequestHandler):
  def get(self):
    self.response.out.write(
    """
    <html>
      <body>
        <b>%s</b>
      </body>
    </html>
    """ % self.request.user_agent)
    
    
application = webapp.WSGIApplication([
  ("/", Receiver)
], debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main() 
 
 
Google App Engine for Pythonでのセッション管理の実装
セッション管理部分のみ抜粋

def getSession(request):
# クッキーのセッションIDとmemcache/Datastoreのセッション情報を照合する関数
# リクエストハンドラーのself.requestを引数として受け取る

    # クッキーからセッション番号を取得
    sid = request.cookies.get('SID', '')
    if sid:
        # まずmemcacheにそのセッション番号があるか探す
        session = memcache.get(sid)
        if session:
            return session

        # memcacheになければDatastoreから探す
        else:
            session = Session.get(sid)
            if session:
                # Datastoreにあればmemcacheにも入れておく
                memcache_key = str(session.key())
                memcache_value = {
                    'user' : {
                        'key' : str(session.user.key()),
                        'name' : session.user.name,
                    },
                }
                memcache.add(key=memcache_key, value=memcache_value, time=3600)

                return session
 
Google App EngineでDjangoのテンプレートエンジンを使う
import wsgiref.handlers
from google.appengine.ext import webapp

#テンプレートを使用するため、下記二つのimportを追加
import os
from google.appengine.ext.webapp import template

class MainHandler(webapp.RequestHandler):

    def get(self):
        #テンプレートに渡す果物リスト
        fruits = ('みかん','りんご','バナナ','パイナップル','スイカ')
        #テンプレートに渡す文字列
        j_str = '日本語文字列';
        
        #連想配列にテンプレートに渡す値を設定
        #テンプレートからは、連想配列のキーでアクセスする。
        template_values = {'list' : fruits,
                            'str_value' : j_str}
        
        #使用するテンプレートを指定
        path = os.path.join(os.path.dirname(__file__), 'template/index.html')
        #レンダリングを実行し、結果をレスポンスとしてブラウザに返却
        self.response.out.write(template.render(path, template_values))

def main():
    application = webapp.WSGIApplication([('/', MainHandler)],
                                       debug=True)
    wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
    main()
 
アプリケーションの構成はapp.yamlと同階層フォルダにtemplateを作って、その下にindex.htmlテンプレートファイルを作る
template/index.html
app.yaml
index.yaml
main.py 

Amazon3